【问题标题】:@PreAuthorize not working after upgrading to Spring Boot 3 (Spring Security 6)升级到 Spring Boot 3 (Spring Security 6) 后 @PreAuthorize 不工作
【发布时间】:2023-02-04 04:00:28
【问题描述】:

我已将我的 Spring Boot 项目升级到 Spring Boot 3。

我还更新了 WebSecurityConfig,它现在看起来像这样:

// imports...

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class CustomWebSecurityConfig {
    final UserDetailsServiceImpl userDetailsService;

    private final AuthEntryPointJwt unauthorizedHandler;
    private final PasswordEncoder passwordEncoder;

    @Bean
    public AuthTokenFilter authenticationJwtTokenFilter() {
        return new AuthTokenFilter();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();

        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder);

        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    /**
     * Sets up a chain of antmatchers specifying what permissions and roles have access to which resources.
     *
     * @param http          Injected HttpSecurity object
     * @return              Chain of Security filters
     * @throws Exception    Currently throws general exception
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                // https://stackoverflow.com/questions/74447778/spring-security-in-spring-boot-3
                .authorizeHttpRequests(requests -> requests.requestMatchers("/api/auth/**").permitAll()
                        .requestMatchers("/api/test/**").permitAll()
                        .requestMatchers("/").permitAll()
                        .requestMatchers("/index.html").permitAll()
                        .requestMatchers("/favicon.ico").permitAll()
                        .requestMatchers("/main.js").permitAll()
                        .requestMatchers("/polyfills.js").permitAll()
                        .requestMatchers("/runtime.js").permitAll()
                        .requestMatchers("/styles.css").permitAll()
                        .requestMatchers("/vendor.css").permitAll()
                        .requestMatchers("/assets/**").permitAll()
                        .requestMatchers("/error").permitAll()
                        .requestMatchers("/**").permitAll()
                        .anyRequest().authenticated());

        http.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.authenticationProvider(authenticationProvider());

        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

这是带有@PreAuthorize 的示例端点:


// imports...

@RestController
@RequestMapping("/api/test")
public class TestController {
    @GetMapping("/all")
    public String allAccess() {
        return "Public Content.";
    }

    @GetMapping("/user")
    @PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
    public String userAccess() {
        return "User Content.";
    }

    @GetMapping("/mod")
    @PreAuthorize("hasRole('MODERATOR')")
    public String moderatorAccess() {
        return "Moderator Board.";
    }

    @GetMapping("/admin")
    @PreAuthorize("hasRole('ADMIN')")
    public String adminAccess() {
        return "Admin Board.";
    }
}

我为这个用例编写的测试部分失败,因为登录用户可以访问所有端点,但默认情况下只有“USER”-Role。 这两个测试失败:

@Test
@DisplayName("Give user no token and forbid access")
@WithMockUser(roles = "USER")
void givenUserToken_whenGetSecureRequest_thenForbidden() throws Exception {
    mockMvc.perform(get("/api/test/mod"))
            .andExpect(status().isForbidden());
}

@Test
@DisplayName("Give user no token and forbid access v.2")
@WithMockUser(roles = "USER")
void givenUserToken_whenGetSecureRequest_thenForbidden2() throws Exception {
    mockMvc.perform(get("/api/test/admin"))
            .andExpect(status().isForbidden());
}

我阅读了一些关于@EnableMethodSecurity 的内容,但我还没有找到使用它和修复@PreAuthorize 不起作用的方法

【问题讨论】:

    标签: spring-boot spring-security


    【解决方案1】:

    根据文档:

    启用方法安全

    在 Spring Security 5.6 中,我们可以使用 任何@Configuration 实例上的@EnableMethodSecurity 注释。

    只需将此注释添加到您的 @Configuration 类之上即可,它应该可以工作。

    Example:
    
    @Configuration
    @EnableMethodSecurity
    public class SecurityConfig {
    
        @Bean
        SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeHttpRequests((authorize) -> authorize
                            .anyRequest().authenticated()
                    ).httpBasic(withDefaults());
            return http.build();
        }
    }
    

    这在许多方面改进了@EnableGlobalMethodSecurity。 @EnableMethodSecurity:

    Uses the simplified AuthorizationManager API instead of metadata sources, config attributes, decision managers, and voters. This simplifies reuse and customization.
    
    Favors direct bean-based configuration, instead of requiring extending GlobalMethodSecurityConfiguration to customize beans
    
    Is built using native Spring AOP, removing abstractions and allowing you to use Spring AOP building blocks to customize
    
    Checks for conflicting annotations to ensure an unambiguous security configuration
    
    Complies with JSR-250
    
    Enables @PreAuthorize, @PostAuthorize, @PreFilter, and @PostFilter by default
    

    https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html

    【讨论】:

      【解决方案2】:

      @J能力 我有类似的问题。根据描述,当我更正我的代码时,出现错误“在 SecurityContext 中找不到身份验证对象”。

      @Configuration
      @EnableMethodSecurity
      @Slf4j
      public class WebSecurityConfig {
          @Bean
          SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
              http.csrf().disable()
                      .authorizeHttpRequests((authorize) -> authorize
                              .anyRequest().authenticated()
                      ).httpBasic(withDefaults());
              // Add a filter to log the request-response of every request
              http.addFilterBefore(loggingFilter, UsernamePasswordAuthenticationFilter.class);
              // Add a filter to validate the tokens with every request
              http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
              return http.build();
          }
      

      【讨论】:

        猜你喜欢
        • 2022-12-04
        • 2013-11-16
        • 2018-11-20
        • 1970-01-01
        • 2021-12-22
        • 2015-10-12
        • 2019-03-24
        • 2013-09-25
        • 2023-02-24
        相关资源
        最近更新 更多