我正在尝试使用REST实现一个网站。我对用户进行身份验证的策略包括向用户发送JWT令牌以回复通过POST发送的用户名/密码组合。我的安全配置中最相关的部分如下所示。
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/images/**", "/scripts/**", "/styles/**", "favicon.ico");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling()
.authenticationEntryPoint(new RESTAuthenticationEntryPoint()).and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.loginProcessingUrl("/login") //Not necesary because is the default
.permitAll().and()
.authorizeRequests()
.antMatchers("/api/getStatistics").permitAll()
.anyRequest().denyAll().and()
.addFilterBefore(new JwtTokenAuthenticationFilter(jWTTokenService()), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public SavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler() {
return new RESTAuthenticationSuccessHandler(jWTTokenService());
}
@Bean
public JWTTokenService jWTTokenService() {
return new JWTTokenServiceImpl();
}
为了允许CORS访问,我在WebMvcConfigurerAdapter扩展的类中编写了以下行
@Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
.allowedMethods("GET", "POST", "OPTIONS")
.allowCredentials(true).maxAge(3600);
registry.addMapping("/login")
.allowedOrigins("*")
.allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
.allowedMethods("POST", "OPTIONS")
.allowCredentials(true).maxAge(3600);
}
因此,当我调用/ login发送用户名和密码时,假设Spring将捕获请求,将处理它,然后将重定向到成功或失败处理程序。
嗯,而不是我在CORS预检期间得到了403 Forbidden响应。我决定调试该程序,因为我认为当我编写formLogin()时,UsernamePasswordAuthenticationFilter创建一个带有值的新AntPathRequestMatcher(" / login"," POST")。
我在调试控制台中找到的内容如下 请求'选项/登录'没有匹配' POST / login
当然不是!几个小时后试图解决问题我发现一切都有效,如果我声明一个空方法/登录,因为在预检期间Spring找到方法,然后向客户端发送200OK,然后允许客户端发送捕获的POST通过UsernamePasswordAuthenticationFilter。
@Controller
public class LoginController {
@RequestMapping(value = { "/login" }, method = RequestMethod.POST)
public void dummyLogin() {
}
}
所以,我的问题是:我是否真的要声明一个空方法来"作弊"在CORS预检期间,还是我错过了一些东西?因为当你真的想要将作业委托给UsernamePasswordAuthenticationFilter时,声明一个虚方法并不是那么优雅......
答案 0 :(得分:3)
问题是org.springframework.security.web.authentication.logout.LogoutFilter
和org.springframework.security.web.authenticationUsernamePasswordAuthenticationFilter
如果处理登录/注销,则不会继续使用过滤器链。由于通过WebSecurityConfigurerAdapter
的配置稍后会在链中处理,因此永远不会应用CorsProcessor
。
我决定保留旧解决方案并使用org.springframework.web.filter.CorsFilter
。
答案 1 :(得分:0)
没有必要使用空方法来使其工作。您唯一要做的就是允许OPTIONS
调用/login
网址。
.antMatchers(HttpMethod.OPTIONS," / login")。permitAll()
前:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling()
.authenticationEntryPoint(new RESTAuthenticationEntryPoint()).and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.loginProcessingUrl("/login") //Not necesary because is the default
.permitAll().and()
.authorizeRequests()
.antMatchers("/api/getStatistics").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/login").permitAll()
.anyRequest().denyAll().and()
.addFilterBefore(new JwtTokenAuthenticationFilter(jWTTokenService()), UsernamePasswordAuthenticationFilter.class);
}