Jak zabezpieczyć REST API za pomocą Spring Boot i Spring Security?

80

Wiem, że zabezpieczanie REST API jest szeroko komentowanym tematem, ale nie jestem w stanie stworzyć małego prototypu spełniającego moje kryteria (i muszę potwierdzić, że te kryteria są realistyczne). Jest tak wiele opcji, jak zabezpieczyć zasoby i jak pracować z zabezpieczeniami Spring, muszę wyjaśnić, czy moje potrzeby są realistyczne.

Moje wymagania

  • Token uwierzytelniający - użytkownicy dostarczą swoje poświadczenia i otrzymają unikalny i ograniczony czasowo token dostępu. Chciałbym zarządzać tworzeniem tokena, sprawdzaniem jego ważności, wygaśnięciem we własnej implementacji.
  • Niektóre zasoby REST będą publiczne - nie trzeba w ogóle uwierzytelniać,
  • Niektóre zasoby będą dostępne tylko dla użytkowników z uprawnieniami administratora,
  • Inne zasoby będą dostępne po autoryzacji dla wszystkich użytkowników.
  • Nie chcę używać uwierzytelniania podstawowego
  • Konfiguracja kodu Java (nie XML)

Aktualny stan

Mój REST API działa bardzo dobrze, ale teraz muszę go zabezpieczyć. Szukając rozwiązania stworzyłem javax.servlet.Filterfiltr:

  @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        String accessToken = request.getHeader(AUTHORIZATION_TOKEN);
        Account account = accountDao.find(accessToken);

        if (account == null) {    
            throw new UnauthorizedException();    
        }

        chain.doFilter(req, res);

    }

Ale to rozwiązanie javax.servlet.filtersnie działa tak, jak potrzebuję, ponieważ występuje problem z obsługą wyjątków przez @ControllerAdviceSpring servlet dispatcher.

Czego potrzebuję

Chciałbym wiedzieć, czy te kryteria są realistyczne i uzyskać pomoc, jak zacząć zabezpieczać REST API za pomocą Spring Security. Czytałem wiele tutoriali (np. Spring Data REST + Spring Security ), ale wszystkie działają w bardzo podstawowej konfiguracji - użytkownicy wraz z ich poświadczeniami są przechowywani w pamięci w konfiguracji i muszę popracować z DBMS i stworzyć własny token uwierzytelniający.

Proszę, daj mi kilka pomysłów, jak zacząć.

Artegon
źródło

Odpowiedzi:

67

Uwierzytelnianie oparte na tokenach - użytkownicy dostarczą swoje poświadczenia i otrzymają unikalny i ograniczony czasowo token dostępu. Chciałbym zarządzać tworzeniem tokena, sprawdzaniem jego ważności, wygaśnięciem we własnej implementacji.

Właściwie użyj filtru do uwierzytelniania tokenu - najlepszy sposób w tym przypadku

Ostatecznie możesz utworzyć CRUD za pośrednictwem Spring Data do zarządzania właściwościami tokena, takimi jak wygasanie itp.

Oto mój filtr tokenów: http://pastebin.com/13WWpLq2

I implementacja usługi tokenów

http://pastebin.com/dUYM555E

Niektóre zasoby REST będą publiczne - nie trzeba w ogóle uwierzytelniać

To żaden problem, możesz zarządzać swoimi zasobami za pomocą konfiguracji zabezpieczeń Spring w następujący sposób: .antMatchers("/rest/blabla/**").permitAll()

Niektóre zasoby będą dostępne tylko dla użytkowników z uprawnieniami administratora,

Spójrz na @Securedadnotacje do zajęć. Przykład:

@Controller
@RequestMapping(value = "/adminservice")
@Secured("ROLE_ADMIN")
public class AdminServiceController {

Drugi zasób będzie dostępny po autoryzacji dla wszystkich użytkowników.

Wróć do konfiguracji Spring Security, możesz skonfigurować swój adres URL w następujący sposób:

    http
            .authorizeRequests()
            .antMatchers("/openforall/**").permitAll()
            .antMatchers("/alsoopen/**").permitAll()
            .anyRequest().authenticated()

Nie chcę używać uwierzytelniania podstawowego

Tak, poprzez filtr tokenów, Twoi użytkownicy zostaną uwierzytelnieni.

Konfiguracja kodu Java (nie XML)

Wracając do słów powyżej, spójrz na @EnableWebSecurity. Twoja klasa będzie:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {}

Musisz nadpisać metodę konfiguracji . Kod poniżej, na przykład, jak skonfigurować dopasowania. Pochodzi z innego projektu.

    @Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/assets/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
                .usernameParameter("j_username")
                .passwordParameter("j_password")
                .loginPage("/login")
                .defaultSuccessUrl("/", true)
                .successHandler(customAuthenticationSuccessHandler)
                .permitAll()
            .and()
                .logout()
                .logoutUrl("/logout")
                .invalidateHttpSession(true)
                .logoutSuccessUrl("/")
                .deleteCookies("JSESSIONID")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .and()
                .csrf();
}
Oleksandr Loushkin
źródło
czy możesz mi pomóc w sprawie mojego pytania? stackoverflow.com/questions/46065063/…
Felipe A.
2
Jakiś przykładowy projekt, który zawiera wszystkie wymagania?
AlikElzin-kilaka
@Oleksandr: To długa szansa, ale czy możesz mi powiedzieć, dlaczego zacząłeś wątek w metodzie updateLastLogin (...) klasy RESTAuthenticationTokenProcessingFilter?
Z3d4s
1
@ z3d4s, właściwie to stary przykład (4 lata), na razie zasugeruję użycie OffsetDateTime, innego podejścia itp. :) nowy wątek, który zaproponowałem, aby skrócić czas przetwarzania żądań użytkowników, ponieważ może to zająć więcej czasu podczas zapisywania do bazy danych.
Oleksandr Loushkin
Ach, rozumiem, to genialne rozwiązanie! Dzięki!
Z3d4s
4

Zabezpieczenia Spring są również bardzo przydatne do zapewniania uwierzytelniania i autoryzacji do adresów URL REST. Nie musimy określać żadnych niestandardowych implementacji.

Najpierw musisz określić punkt wejścia do restAuthenticationEntryPoint w konfiguracji zabezpieczeń, jak poniżej.

 <security:http pattern="/api/**" entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" auto-config="true" create-session="stateless" >

    <security:intercept-url pattern="/api/userList" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/api/managerList" access="hasRole('ROLE_ADMIN')"/>
    <security:custom-filter ref="preAuthFilter" position="PRE_AUTH_FILTER"/>
</security:http>

Implementacja dla restAuthenticationEntryPoint może wyglądać jak poniżej.

 @Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

   public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException ) throws IOException {
      response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
   }
}

Następnie musisz określić RequestHeaderAuthenticationFilter. Zawiera klucz RequestHeader. Jest to zasadniczo używane do identyfikacji uwierzytelnienia użytkownika. Zazwyczaj RequestHeader przenosi te informacje podczas wykonywania wywołań REST. Na przykład rozważ poniższy kod

   <bean id="preAuthFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
    <property name="principalRequestHeader" value="Authorization"/>
    <property name="authenticationManager" ref="authenticationManager" />
  </bean>

Tutaj,

<property name="principalRequestHeader" value="Authorization"/>

„Autoryzacja” to klucz przedstawiany w przychodzącym żądaniu. Zawiera wymagane informacje uwierzytelniające użytkownika. Musisz również skonfigurować PreAuthenticatedAuthenticationProvider, aby spełniał nasze wymagania.

   <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
  <bean id="userDetailsServiceWrapper"
      class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
    <property name="userDetailsService" ref="authenticationService"/>
  </bean>
</property>
</bean>

Ten kod będzie działał w celu zabezpieczenia adresów URL REST za pomocą uwierzytelniania i autoryzacji bez żadnych niestandardowych implementacji.

Aby uzyskać pełny kod, znajdź poniższy link:

https://github.com/srinivas1918/spring-rest-security

Nalla Srinivas
źródło
-1

Aby zweryfikować REST API, istnieją 2 sposoby

1 - Podstawowe uwierzytelnianie przy użyciu domyślnej nazwy użytkownika i hasła ustawionego w pliku application.properties

Uwierzytelnianie podstawowe

2 - Uwierzytelnij się przy użyciu bazy danych (userDetailsService) z rzeczywistą nazwą użytkownika i hasłem

Zaawansowane uwierzytelnianie

jeet singh parmar
źródło
Wideo jest przydatne, jak wykonać to samo zaawansowane uwierzytelnianie dla ReST API. Tutaj opisuję tylko o sieci. Czy istnieje samouczek wideo dotyczący zaawansowanego uwierzytelniania w REST API.
Jacob
Jeśli widziałeś drugie wideo (Advanced Authentication), to w tym przypadku wykonuję to samo uwierzytelnianie za pomocą klienta REST (dla REST API).
jeet singh parmar