¿Cómo asegurar la API REST con Spring Boot y Spring Security?

80

Sé que asegurar la API REST es un tema muy comentado, pero no puedo crear un pequeño prototipo que cumpla con mis criterios (y necesito confirmar que estos criterios son realistas). Hay tantas opciones sobre cómo asegurar los recursos y cómo trabajar con Spring Security, necesito aclarar si mis necesidades son realistas.

Mis requisitos

  • Autenticador basado en token: los usuarios proporcionarán sus credenciales y obtendrán un token de acceso único y por tiempo limitado. Me gustaría administrar la creación de tokens, verificar la validez y el vencimiento en mi propia implementación.
  • Algunos recursos REST serán públicos, no es necesario autenticarse en absoluto,
  • Algunos recursos serán accesibles solo para usuarios con derechos de administrador,
  • Se podrá acceder a otro recurso después de la autorización para todos los usuarios.
  • No quiero usar la autenticación básica
  • Configuración de código Java (no XML)

Estado actual

Mi API REST funciona muy bien, pero ahora necesito protegerla. Cuando buscaba una solución, creé un javax.servlet.Filterfiltro:

  @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);

    }

Pero esta solución javax.servlet.filtersno funciona como necesito porque hay un problema con el manejo de excepciones a través @ControllerAdvicede Spring servlet dispatcher.

Lo que necesito

Me gustaría saber si estos criterios son realistas y obtener ayuda, cómo comenzar a proteger la API REST con Spring Security. Leí muchos tutoriales (por ejemplo, Spring Data REST + Spring Security ) pero todos funcionan en una configuración muy básica: los usuarios con sus credenciales se almacenan en la memoria en la configuración y necesito trabajar con DBMS y crear un autenticador propio.

Por favor, dame algunas ideas sobre cómo empezar.

Artegon
fuente

Respuestas:

67

Autenticación basada en token: los usuarios proporcionarán sus credenciales y obtendrán un token de acceso único y por tiempo limitado. Me gustaría administrar la creación de tokens, verificar la validez y el vencimiento en mi propia implementación.

En realidad, use Filter for token Auth, la mejor manera en este caso

Eventualmente, puede crear CRUD a través de Spring Data para administrar las propiedades de Token como expirar, etc.

Aquí está mi filtro de token: http://pastebin.com/13WWpLq2

E implementación del servicio Token

http://pastebin.com/dUYM555E

Algunos recursos REST serán públicos, no es necesario autenticarse en absoluto

No es un problema, puede administrar sus recursos a través de la configuración de seguridad de Spring de esta manera: .antMatchers("/rest/blabla/**").permitAll()

Algunos recursos serán accesibles solo para usuarios con derechos de administrador,

Eche un vistazo a la @Securedanotación a la clase. Ejemplo:

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

El otro recurso será accesible después de la autorización para todos los usuarios.

De vuelta a la configuración de Spring Security, puede configurar su url de esta manera:

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

No quiero usar la autenticación básica

Sí, a través del filtro de token, sus usuarios serán autenticados.

Configuración de código Java (no XML)

Volviendo a las palabras anteriores, mira @EnableWebSecurity. Tu clase será:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {}

Tienes que anular el método de configuración . Codifique a continuación, solo por ejemplo, cómo configurar los emparejadores. Es de otro proyecto.

    @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
fuente
me pueden ayudar en mi pregunta por favor? stackoverflow.com/questions/46065063/…
Felipe A.
2
¿Algún proyecto de ejemplo que incluya todos los requisitos?
AlikElzin-kilaka
@Oleksandr: Esa es una posibilidad remota, pero ¿puede decirme por qué ha iniciado un hilo en el método updateLastLogin (...) de la clase RESTAuthenticationTokenProcessingFilter?
Z3d4s
1
@ z3d4s, en realidad es un ejemplo antiguo (4 años), por ahora sugeriré usar OffsetDateTime, otros enfoques, etc. :) nuevo hilo que propuse usar para disminuir el tiempo de procesamiento de las solicitudes de los usuarios, porque puede llevar tiempo adicional durante el guardado en la base de datos.
Oleksandr Loushkin
Ahhh ya veo, ¡esa es una solución genial! ¡Gracias!
Z3d4s
4

Spring Security también es muy útil para proporcionar autenticación y autorización a las URL REST. No necesitamos especificar implementaciones personalizadas.

Primero, debe especificar el punto de entrada ref para restAuthenticationEntryPoint en su configuración de seguridad como se muestra a continuación.

 <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>

La implementación para restAuthenticationEntryPoint podría ser la siguiente.

 @Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

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

Después de esto, debe especificar RequestHeaderAuthenticationFilter. Contiene la clave RequestHeader. Esto se utiliza básicamente para identificar la autenticación del usuario. Generalmente RequestHeader lleva esta información al realizar las llamadas REST. Por ejemplo, considere el siguiente código

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

Aquí,

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

"Autorización" es la clave que presenta la solicitud entrante. Contiene la información de autenticación del usuario requerida. También debe configurar PreAuthenticatedAuthenticationProvider para cumplir con nuestro requisito.

   <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>

Este código funcionará para proteger las URL REST mediante autenticación y autorización sin implementaciones personalizadas.

Para obtener el código completo, busque el siguiente enlace:

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

Nalla Srinivas
fuente
-1

Para validar la API REST hay 2 formas

1 - Autenticación básica con nombre de usuario y contraseña predeterminados configurados en el archivo application.properties

Autenticación básica

2 - Autenticar usando la base de datos (userDetailsService) con el nombre de usuario y la contraseña reales

Autenticación avanzada

jeet singh parmar
fuente
El video es útil Cómo hacer la misma autenticación avanzada para la API ReST. Aquí solo se describe sobre Web. ¿Existe algún video tutorial para la autenticación avanzada en REST API?
Jacob
Si vio el segundo video (Autenticación avanzada), entonces estoy haciendo la misma autenticación usando el cliente REST (para REST API) también.
jeet singh parmar