当我POST到/api/v1/auth/寄存器
时,我会收到由配置的access DeniedHandler
生成的403响应。但是,我希望这个请求是允许的,因为它由permitAll()
覆盖,并且在anyRequest().身份验证()
之前。
诸如GET /api/v1/reference/nations
之类的请求工作正常。此外,我命中这些 /api/v1/auth/**
终结点的集成测试也可以正常工作,这表明与 CORS 有关,尽管预检请求为 200。
你知道这个安全配置有什么问题吗?
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true
)
@Order(1)
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsServiceImpl userDetailsService;
private final Config config;
private final ObjectMapper objectMapper;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.authorizeRequests()
.antMatchers(
"/api/v1/auth/register",
"/api/v1/auth/register/check",
"/api/v1/auth/register/activate",
"/api/v1/auth/password/update",
"/api/v1/auth/recover",
"/api/v1/auth/recover/check",
"/api/v1/auth/recover/reset",
"/api/v1/csrf-token",
"/api/v1/reference/**"
)
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.successHandler((request, response, authentication) -> {
response.getWriter().append("OK");
response.setStatus(HttpServletResponse.SC_OK);
})
.failureHandler((request, response, exception) -> {
response.getWriter().append("Invalid credentials or inactive account");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
})
.loginProcessingUrl("/api/v1/auth/login")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/api/v1/auth/logout", "POST"))
.permitAll()
.and()
.exceptionHandling()
.accessDeniedHandler((request, response, accessDeniedException) -> {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
objectMapper.writeValue(
response.getWriter(),
ErrorResponseBody
.builder()
.code(ErrorType.ACCESS_DENIED)
.status(HttpServletResponse.SC_FORBIDDEN)
.message("Access denied")
.build()
);
})
.authenticationEntryPoint((request, response, authException) -> {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
objectMapper.writeValue(
response.getWriter(),
ErrorResponseBody
.builder()
.code(ErrorType.LOGIN_REQUIRED)
.status(HttpServletResponse.SC_UNAUTHORIZED)
.message("You are not authorized to access this resource")
.build()
);
})
.and()
.userDetailsService(userDetailsService);
if (config.isCsrfDisabled()) {
http
.csrf()
.disable();
}
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
final var configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOrigins(config.getAllowedOrigins());
configuration.setAllowedMethods(asList("GET", "POST", "PUT", "PATCH", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList(HttpHeaders.AUTHORIZATION, HttpHeaders.CACHE_CONTROL, HttpHeaders.CONTENT_TYPE, HttpHeaders.ACCEPT));
final var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", configuration);
return source;
}
}
这是我的CORS配置:
@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class WebConfig {
private final Config config;
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
final var allowedOrigins = Optional
.ofNullable(config.getAllowedOrigins())
.map(origins -> origins.toArray(new String[]{}))
.orElse(new String[]{});
System.out.println("Enabling CORS for the following origins:" + Arrays.asList(allowedOrigins).toString());
registry
.addMapping("/api/**")
.allowedOrigins(allowedOrigins)
.allowCredentials(true)
.allowedMethods("*")
.allowedHeaders("*");
}
};
}
}
我从http://localhost:3000
调用这些endpoint,这是config.getAllowedOrigins()
返回的项目之一。
以下是来自请求的Spring Security调试日志:
2020-11-24 06:51:16.110 INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-11-24 06:51:16.112 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-11-24 06:51:16.158 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 44 ms
2020-11-24 06:51:16.193 DEBUG 1 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-11-24 06:51:16.200 DEBUG 1 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-11-24 06:51:16.205 DEBUG 1 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-11-24 06:51:16.206 DEBUG 1 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-11-24 06:51:16.214 DEBUG 1 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-11-24 06:51:16.217 DEBUG 1 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 4 of 13 in additional filter chain; firing Filter: 'CorsFilter'
2020-11-24 06:51:16.286 DEBUG 1 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@461bbc94
2020-11-24 06:51:16.290 DEBUG 1 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-11-24 06:51:16.299 DEBUG 1 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-11-24 06:51:16.483 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-11-24 06:51:16.485 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-11-24 06:51:16.486 DEBUG 1 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-11-24 06:51:16.487 DEBUG 1 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-11-24 06:51:16.488 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-11-24 06:51:16.489 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 4 of 13 in additional filter chain; firing Filter: 'CorsFilter'
2020-11-24 06:51:16.494 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 5 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2020-11-24 06:51:16.525 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://dev.api.example.com/api/v1/auth/register
2020-11-24 06:51:16.609 DEBUG 1 --- [nio-8080-exec-2] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@461bbc94
2020-11-24 06:51:16.610 DEBUG 1 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-11-24 06:51:16.615 DEBUG 1 --- [nio-8080-exec-2] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
使用< code >禁用CSRF。csrf()。在< code >配置(HttpSecurity http)中的disable()更多细节在此解释
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
... // other configurations
}