Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security Token based Authentication

I have a rest api where I am authenticating using spring security Basic Authorization where client sends username and password for each request. Now, I wanted to implement token based authentication where I will send a token in response header when user is authenticated at first. For further requests, client can include that token in the header which will be used to authenticate the user to the resources. I have two authentication providers tokenAuthenticationProvider and 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);         } } 

And in daoAuthenticationProvider I am setting custom userDetailsService and authenticating against user login details by fetching it from the database (which is working fine as long as user name and password are passed using Authorization:Basic bGllQXBpVXNlcjogN21wXidMQjRdTURtR04pag== as header)

But when I include token in the header using X-AUTH-TOKEN (which is Constants.AUTH_HEADER_NAME), tokenAuthenticationProvider is not being called. I am getting error as

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

And here is how I am adding authentication providers.

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

Please suggest how can I implement Token based authentication without hurting the current behavior of spring security.

like image 816
Raghavendra Avatar asked Feb 20 '17 21:02

Raghavendra


People also ask

What is token in Spring Security?

JSON Web Token or JWT, as it is more commonly called, is an open Internet standard (RFC 7519) for securely transmitting trusted information between parties in a compact way. The tokens contain claims that are encoded as a JSON object and are digitally signed using a private secret or a public key/private key pair.

Is Spring Security a JWT?

As the authorization server makes available new keys, Spring Security will automatically rotate the keys used to validate JWTs. The resulting Authentication#getPrincipal , by default, is a Spring Security Jwt object, and Authentication#getName maps to the JWT's sub property, if one is present.


1 Answers

Here is how I was able to implement token based authentication and basic authentication

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");       }  } 

As our CustomBasicAuthenticationFilter has been configured and added as a filter to the spring security,

Whenever basic authentication is successful the request will be redirected to onSuccessfulAuthentication where we set the token and send it in the response with some header "header-name".

If "header-name" is sent for further request, then the request will go through TokenAuthenticationFilter first before attempting to try Basic Authentication.

like image 195
Raghavendra Avatar answered Sep 19 '22 06:09

Raghavendra