I'm trying to implement a custom authentication logic with latest version of Spring Boot, Web and Security, but I'm struggling with some issues. I was trying out many solutions in similar questions/tutorials without success or understanding what actually happens.
I'm creating a REST application with stateless authentication, i.e. there is a REST endpoint (/web/auth/login) that expects username and password and returns a string token, which is then used in all the other REST endpoints (/api/**) to identify the user. I need to implement a custom solution as authentication will become more complex in the future and I would like to understand the basics of Spring Security.
To achieve the token authentication, I'm creating a customized filter and provider:
The filter:
public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public TokenAuthenticationFilter() {
super(new AntPathRequestMatcher("/api/**", "GET"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String token = request.getParameter("token");
if (token == null || token.length() == 0) {
throw new BadCredentialsException("Missing token");
}
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(token, null);
return getAuthenticationManager().authenticate(authenticationToken);
}
}
The provider:
@Component
public class TokenAuthenticationProvider implements AuthenticationProvider {
@Autowired
private AuthenticationTokenManager tokenManager;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = (String)authentication.getPrincipal();
return tokenManager.getAuthenticationByToken(token);
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.equals(authentication);
}
}
The config:
@EnableWebSecurity
@Order(1)
public class TokenAuthenticationSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private TokenAuthenticationProvider authProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(authenticationFilter(), BasicAuthenticationFilter.class);
}
@Bean
public TokenAuthenticationFilter authenticationFilter() throws Exception {
TokenAuthenticationFilter tokenProcessingFilter = new TokenAuthenticationFilter();
tokenProcessingFilter.setAuthenticationManager(authenticationManager());
return tokenProcessingFilter;
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
}
The AuthenticationTokenManager used in the provider (and also in the login process):
@Component
public class AuthenticationTokenManager {
private Map<String, AuthenticationToken> tokens;
public AuthenticationTokenManager() {
tokens = new HashMap<>();
}
private String generateToken(AuthenticationToken authentication) {
return UUID.randomUUID().toString();
}
public String addAuthentication(AuthenticationToken authentication) {
String token = generateToken(authentication);
tokens.put(token, authentication);
return token;
}
public AuthenticationToken getAuthenticationByToken(String token) {
return tokens.get(token);
}
}
What happens: I'm appending a valid token in the request to "/api/bla" (which is a REST controller returning some Json). The filter and provider both get invoked. The problem is, the browser is redirected to "/" instead of invoking the REST controller's requested method. This seems to happen in SavedRequestAwareAuthenticationSuccessHandler, but why is this handler being used?
I tried
I would like to understand why my controller is not being called after I authenticated the token. Besides that, is there a "Spring" way to store the token instead of storing it in a Map, like a custom implementation of SecurityContextRepository?
I really appreciate any hint!
Might be a little late but I was having the same problem and adding:
@Override
protected void successfulAuthentication(
final HttpServletRequest request, final HttpServletResponse response,
final FilterChain chain, final Authentication authResult)
throws IOException, ServletException {
chain.doFilter(request, response);
}
to my AbstractAuthenticationProcessingFilter implementation did the trick.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With