【问题标题】:Spring Boot permitAll not working in WebSecurityConfigurerAdapterSpring Boot permitAll 在 WebSecurityConfigurerAdapter 中不起作用
【发布时间】:2021-03-06 22:00:08
【问题描述】:

当我 POST 到 /api/v1/auth/register 时,我收到了配置的 accessDeniedHandler 生成的 403 响应。但是,我希望这个请求会被允许,因为它被 permitAll() 覆盖并且在 anyRequest().authenticated() 之前。

GET /api/v1/reference/countries 之类的请求可以正常工作。此外,命中这些/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 调用这些端点,这是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

【问题讨论】:

  • 您是否尝试过为 Spring Security 启用调试日志记录?这能说明问题吗?
  • 为 Spring Security 启用调试日志记录后,请求挂起然后超时。
  • 看起来熵不足与启用日志记录同时发生。

标签: java spring-boot spring-security


【解决方案1】:

configure(HttpSecurity http) 中使用.csrf().disable() 禁用CSRF 更多细节在here 中解释

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
           .csrf().disable()
           ... // other configurations
}

【讨论】:

  • 谢谢。我选择了选项 2。选项 1 不是一个好主意,因为 ti 完全禁用了网络安全。很高兴接受这个答案,去掉了选项 1。
  • 是的,configure(WebSecurity web) 理想情况下应该用于静态内容。
猜你喜欢
  • 2020-10-02
  • 2023-01-11
  • 2017-08-20
  • 2016-02-16
  • 2018-01-31
  • 1970-01-01
  • 1970-01-01
  • 2014-11-24
  • 2022-11-27
相关资源
最近更新 更多