#java #authentication #spring-security #jwt-auth
#java #аутентификация #spring-безопасность #jwt-auth
Вопрос:
У меня есть приложение spring boot с thymeleaf. Я использую метод spring security formLogin для обеспечения безопасности, и теперь мне нужно добавить JWT только для некоторых API.
@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();
}
}
}
при этом JWT работает нормально, как мне и нужно, но formlogin остановился и вызов «/ signInProcess» теперь выдает 404:
ПРИМЕЧАНИЕ: если я изменю порядок и сделаю formLogin @order(1), он снова заработает, но, конечно, не будет работать.
Также я попытался объединить их так, теперь оба работают нормально, но проблема с обработкой исключений, если ошибка аутентификации JWT вернет страницу с ошибкой formlogin thymeleaf :
@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);
}
любые предложения по выполнению этой работы. Спасибо.
Ответ №1:
Вы WebSecurityConfigurerAdapters
будете обрабатывать входящие запросы по порядку.
Поскольку JWTSecurityConfig
он помечен, @Order(1)
он будет обрабатывать запросы в первую очередь.
Вы не указали a antMatcher
для этого адаптера, поэтому он будет соответствовать всем запросам.
Это означает, что запрос никогда не будет достигнут FormLoginConfigurationAdapter
, поскольку JWTSecurityConfig
соответствует им всем.
Если вы хотите JWTSecurityConfig
применять только к определенным запросам, вы можете указать an antMatcher
в своей конфигурации безопасности.
Ниже приведен пример:
@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();
}
}
}
Для получения более подробной информации о нескольких WebSecurityConfigurerAdapter
, вы можете увидеть раздел несколько HttpSecurity
в справочных документах Spring Security.
Для получения более подробной информации о разнице между authorizeRequests()
и requestMatchers()
, вы можете увидеть этот вопрос о переполнении стека.