I have spring boot app with thymeleaf. I am using spring security formLogin method for security and now I need to add JWT for only some APIs.
@EnableWebSecurity public class SecurityConfigurations { @Autowired UserDetailsServiceImpl userDetails; @Bean DaoAuthenticationProvider provider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setPasswordEncoder(encoder()); provider.setUserDetailsService(userDetails); return provider; } @Bean PasswordEncoder encoder() { return new BCryptPasswordEncoder(); } @Configuration @Order(1) public class JWTSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired private JwtRequestFilter jwtRequestFilter; @Autowired DaoAuthenticationProvider provider; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(provider); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf().disable() .authorizeRequests().antMatchers("/api/user/authenticate").permitAll() .antMatchers("/api/user/**").hasRole("USER") .and(). exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // Add a filter to validate the tokens with every request httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } } @Configuration public static class FormLoginConfigurationAdapter extends WebSecurityConfigurerAdapter { @Autowired DaoAuthenticationProvider provider; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(provider); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/admin/admins**").hasAnyRole("SADMIN").antMatchers("/admin/**") .hasAnyRole("ADMIN", "SADMIN", "WADMIN").antMatchers("/rest/**") .hasAnyRole("ADMIN", "SADMIN", "WADMIN", "USER").antMatchers("/user/**").hasAnyRole("USER") .anyRequest().permitAll().and().formLogin().loginPage("/sign-in-up") .loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout() .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/") .invalidateHttpSession(false).and().csrf().disable().cors(); } } }
by doing this JWT is working fine as just I need but the formlogin has stopped and calling “/signInProcess” now give 404:
NOTE: if I change the order and make formLogin @order(1) it works again but of course will not work.
Also I tried to combine them both like this now it is both works fine but the problem with exception handling if the JWT authentication error will return formlogin thymeleaf error page :
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/admin/admins**").hasAnyRole("SADMIN").antMatchers("/admin/**") .hasAnyRole("ADMIN", "SADMIN", "WADMIN").antMatchers("/rest/**") .hasAnyRole("ADMIN", "SADMIN", "WADMIN", "USER").antMatchers("/user/**").hasAnyRole("USER") .antMatchers("/api/user/authenticate").permitAll() .antMatchers("/api/user/**").hasRole("USER") .anyRequest().permitAll().and().formLogin().loginPage("/sign-in-up") .loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout() .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/") .invalidateHttpSession(false).and().csrf().disable().cors(); http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); }
any suggestions to make this work. thank you.
Advertisement
Answer
Your WebSecurityConfigurerAdapters
will process the incoming requests in order.
Since JWTSecurityConfig
is annotated with @Order(1)
it will process the requests first.
You have not specified a antMatcher
for this Adapter, so it will match all requests.
This means that a request will never reach FormLoginConfigurationAdapter
, since JWTSecurityConfig
matches them all.
If you want JWTSecurityConfig
to only apply to certain requests, you can specify an antMatcher
in your security configuration.
Below is an example:
@EnableWebSecurity public class SecurityConfigurations { @Configuration @Order(1) public class JWTSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .requestMatchers(matchers -> matchers .antMatchers("/api/**") // apply JWTSecurityConfig to requests matching "/api/**" ) .authorizeRequests(authz -> authz .anyRequest().authenticated() ) .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } } @Configuration public class FormLoginConfigurationAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(authz -> authz .anyRequest().authenticated() ) .formLogin(); } } }
For more details on multiple WebSecurityConfigurerAdapter
, you can see the multiple HttpSecurity
section in the Spring Security reference docs.
For more details on the difference between authorizeRequests()
and requestMatchers()
, you can see this Stack Overflow question.