web-dev-qa-db-fra.com

Authentification basée sur un jeton de sécurité Spring

J'ai une api de repos où je m'authentifie à l'aide de l'autorisation de base Spring Security où le client envoie le nom d'utilisateur et le mot de passe pour chaque demande. Maintenant, je voulais implémenter une authentification basée sur un jeton où je vais envoyer un jeton dans l'en-tête de la réponse lorsque l'utilisateur est authentifié au début. Pour les autres demandes, le client peut inclure ce jeton dans l'en-tête qui sera utilisé pour authentifier l'utilisateur auprès des ressources. J'ai deux fournisseurs d'authentification tokenAuthenticationProvider et daoAuthenticationProvider

@Component
public class TokenAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private TokenAuthentcationService service;

    @Override
    public Authentication authenticate(final Authentication authentication) throws AuthenticationException {

        final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        final HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        final String token = request.getHeader(Constants.AUTH_HEADER_NAME);
        final Token tokenObj = this.service.getToken(token);
        final AuthenticationToken authToken = new AuthenticationToken(tokenObj);
        return authToken;
    }

     @Override
        public boolean supports(final Class<?> authentication) {
            return AuthenticationToken.class.isAssignableFrom(authentication);
        }
}

Et dans daoAuthenticationProvider, je suis en train de définir userDetailsService personnalisé et de l'authentifier en fonction des informations de connexion de l'utilisateur en le récupérant dans la base de données (qui fonctionne correctement tant que le nom d'utilisateur et le mot de passe sont transmis à l'aide de Authorization: Basic bGllQXBpVXNlcjogN21wXidMQjRdTUR = 0 en tant qu'en-tête)

Mais lorsque j'inclus un jeton dans l'en-tête à l'aide de X-AUTH-TOKEN (Constants.AUTH_HEADER_NAME), tokenAuthenticationProvider n'est pas appelé. Je reçois l'erreur comme

{"timestamp":1487626368308,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/find"}

Et voici comment j'ajoute des fournisseurs d'authentification.

    @Override
    public void configure(final AuthenticationManagerBuilder auth) throws Exception {

        final UsernamePasswordAuthenticationProvider daoProvider = new 

UsernamePasswordAuthenticationProvider(this.service, this.passwordEncoder());
    auth.authenticationProvider(this.tokenAuthenticationProvider);
    auth.authenticationProvider(daoProvider);
} 

Suggérez-nous comment mettre en œuvre l'authentification par jeton sans nuire au comportement actuel de la sécurité de Spring.

24
Raghavendra

Voici comment j'ai pu implémenter l'authentification par jeton et l'authentification de base

SpringSecurityConfig.Java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{

    @Override
    public void configure(final AuthenticationManagerBuilder auth) throws Exception
    {
        auth.userDetailsService(this.participantService).passwordEncoder(this.passwordEncoder());
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception
    {

        //Implementing Token based authentication in this filter
        final TokenAuthenticationFilter tokenFilter = new TokenAuthenticationFilter();
        http.addFilterBefore(tokenFilter, BasicAuthenticationFilter.class);

        //Creating token when basic authentication is successful and the same token can be used to authenticate for further requests
        final CustomBasicAuthenticationFilter customBasicAuthFilter = new CustomBasicAuthenticationFilter(this.authenticationManager() );
        http.addFilter(customBasicAuthFilter);

    }
}

TokenAuthenticationFilter.Java

    public class TokenAuthenticationFilter extends GenericFilterBean
    {


        @Override
        public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
                throws IOException, ServletException
        {
            final HttpServletRequest httpRequest = (HttpServletRequest)request;

             //extract token from header
            final String accessToken = httpRequest.getHeader("header-name");
            if (null != accessToken) {
           //get and check whether token is valid ( from DB or file wherever you are storing the token)

          //Populate SecurityContextHolder by fetching relevant information using token
               final User user = new User(
                            "username",
                            "password",
                            true,
                            true,
                            true,
                            true,
                            authorities);
                    final UsernamePasswordAuthenticationToken authentication =
                            new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authentication);

            }

            chain.doFilter(request, response);
        }

      }

CustomBasicAuthenticationFilter.Java

@Component
public class CustomBasicAuthenticationFilter extends BasicAuthenticationFilter {


    @Autowired
    public CustomBasicAuthenticationFilter(final AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void onSuccessfulAuthentication(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response, final Authentication authResult) {
        //Generate Token
        //Save the token for the logged in user
        //send token in the response
        response.setHeader("header-name" , "token");


    }

}

Comme notre CustomBasicAuthenticationFilter a été configuré et ajouté en tant que filtre à la sécurité du ressort,

Chaque fois que l'authentification de base réussit, la demande est redirigée vers onSuccessfulAuthentication où nous définissons le jeton et l'envoyons dans la réponse avec un en-tête "nom-en-tête".

Si "nom-en-tête" est envoyé pour une requête ultérieure, la requête passe d'abord par TokenAuthenticationFilter avant de tenter une authentification de base.

22
Raghavendra

Vous pouvez essayer de définir votre jeton personnalisé AuthenticationToken dans votre filtre d'authentification, par exemple:

public class AuthenticationFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        final String authTokenHeader = ((HttpServletRequest)request).getHeader(Constants.AUTH_HEADER_NAME);

        if (authTokenHeader != null) {
            SecurityContextHolder.getContext().setAuthentication(createAuthenticationToken(authTokenHeader));
        }

        chain.doFilter( request, response );
    }
}
2
armandino