@PreAuthorize 升级到 Spring Boot 3 (Spring Security 6) 后不起作用

问题描述 投票:0回答:2

我已将 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”角色。 这 2 个测试失败:

@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
2个回答
13
投票

根据文档:

启用方法安全性

在 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


0
投票

即使添加@Configuration后我也面临同样的问题。

© www.soinside.com 2019 - 2024. All rights reserved.