I've been following a tutorial to implementing JWT authentication in Spring Boot but am trying to adapt it to a case where I have two WebSecurityConfigurerAdapter
classes, one for my API (/api/**
endpoints) and one for my web front-end (all other endpoints). In the tutorial, a JWTAuthenticationFilter
is created as a subclass of UsernamePasswordAuthenticationFilter
and added to the chain. According to the author, this filter will automatically register itself with the "/login
" endpoint, but I want it to point somewhere different, such as "/api/login
" because I'm using this authentication method for my API only.
Here's the security configuration code for both the API and front-end (with some abbrevation):
@EnableWebSecurity
public class MultipleSecurityConfigurations {
@Configuration
@Order(1)
public static class APISecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
}
@Configuration
public static class FrontEndSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/?logout")
.and()
.authorizeRequests()
.mvcMatchers("/").permitAll()
.mvcMatchers("/home").authenticated()
.anyRequest().denyAll()
;
}
}
}
The question is: how can I define an endpoint such as "/api/login
" as the endpoint for my custom JWTAuthenticationFilter
?
Or, do I need to change the filter to not be a subclass of UsernamePasswordAuthenticationFilter
and if so, how would I configure that?
EDIT: Something I've tried:
I guessed that the /api/login
endpoint needed to be .permitAll()
and I tried using formLogin().loginProcessingUrl()
, even though it's not really a form login - it's a JSON login. This doesn't work. When i POST to /api/login
I end up getting redirected to the HTML login form as if I were not logged in. Moreover, my Spring boot app throws a weird exception:
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
The configuration I'm trying now:
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.formLogin().loginProcessingUrl("/api/login").and()
.authorizeRequests()
.antMatchers("/api/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
Since JWTAuthenticationFilter
is a UsernamePasswordAuthenticationFilter
, you could change the login endpoint directly on the filter instance:
JWTAuthenticationFilter customFilter = new JWTAuthenticationFilter(authenticationManager());
customFilter.setFilterProcessesUrl("/api/login");
http.addFilter(customFilter);
This configures JWTAuthenticationFilter
to attempt to authenticate POST requests to /api/login
.