Spring Security @PreAuthroize具有与requestMatchers()相对的角色,hasRole()

ohfgkhjo  于 2022-12-13  发布在  Spring
关注(0)|答案(1)|浏览(163)

我已经创建了jwt实现,它具有Spring资源服务器依赖性。

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
@EnableMethodSecurity
public class WebSecurityConfig {

    @Value("${app.chat.jwt.public.key}")
    private RSAPublicKey publicKey;

    @Value("${app.chat.jwt.private.key}")
    private RSAPrivateKey privateKey;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.exceptionHandling(
                exceptions ->
                        exceptions
                                .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                                .accessDeniedHandler(new BearerTokenAccessDeniedHandler()));

        http.authorizeHttpRequests()
                .requestMatchers("/auth/sign-in").permitAll()
                .requestMatchers("/auth/sign-up").permitAll()
//                .requestMatchers("/hello").hasRole("ROLE_USER")
                .anyRequest().authenticated()
                .and()
                .httpBasic(Customizer.withDefaults())
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);

        return http.build();
    }

    @Bean
    public AuthenticationManager authManager(UserDetailsServiceImpl userDetailsService) {
        var authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(authProvider);
    }

    @SneakyThrows
    @Bean
    public JwtEncoder jwtEncoder() {
        var jwk = new RSAKey.Builder(publicKey).privateKey(privateKey).build();
        var jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwks);
    }

    @SneakyThrows
    @Bean
    public JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withPublicKey(publicKey).build();
    }

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        var jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("scope");
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");

        var jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source =
                new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

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

我有简单的控制器:

@RestController
public class HelloController {

    @PreAuthorize("hasRole('ROLE_USER')")
    @GetMapping("/hello")
    public String hello() {
        return "Hello!";
    }
}

如果我使用@PreAuthorize("hasRole('ROLE_USER')"),我总是返回一个403状态码。但是如果我在配置文件中做同样的事情:requestMatchers("/hello").hasRole("ROLE_USER"),那么它就可以正常工作了。我有@EnableMethodSecurity注解,所以@PreAuthorize应该可以工作,但是由于某些原因,它不能。还有,为什么我需要在hasRole()方法中添加ROLE_?我认为Spring应该为我处理它。
主要问题是为什么hasRole()requestMatchers()有效,但对@PreAuthorize无效。
如果需要的话,这里是gitHub repo的链接(不要注意提交消息):https://github.com/EternalSadnes/chat-app

更新日期:

以下是使用@PreAuthorize注解向/hello端点发出的请求失败后的日志:

2022-12-05T12:27:45.300+02:00 DEBUG 6260 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Securing GET /hello
2022-12-05T12:27:45.312+02:00 DEBUG 6260 --- [nio-8080-exec-2] o.s.s.o.s.r.a.JwtAuthenticationProvider  : Authenticated token
2022-12-05T12:27:45.313+02:00 DEBUG 6260 --- [nio-8080-exec-2] .s.r.w.a.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@621ae24b, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ROLE_ROLE_USER]]
2022-12-05T12:27:45.315+02:00 DEBUG 6260 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Secured GET /hello
2022-12-05T12:27:45.317+02:00 DEBUG 6260 --- [nio-8080-exec-2] horizationManagerBeforeMethodInterceptor : Authorizing method invocation ReflectiveMethodInvocation: public java.lang.String com.eternal.chatapp.controller.HelloController.hello(); target is of class [com.eternal.chatapp.controller.HelloController]
2022-12-05T12:27:45.347+02:00 DEBUG 6260 --- [nio-8080-exec-2] horizationManagerBeforeMethodInterceptor : Failed to authorize ReflectiveMethodInvocation: public java.lang.String com.eternal.chatapp.controller.HelloController.hello(); target is of class [com.eternal.chatapp.controller.HelloController] with authorization manager org.springframework.security.config.annotation.method.configuration.DeferringObservationAuthorizationManager@e30a265 and decision ExpressionAuthorizationDecision [granted=false, expressionAttribute=hasRole('ROLE_USER')]

有什么想法,我可以调试它更深或我应该注意什么?

fkaflof6

fkaflof61#

确实,安全调试日志很有帮助。从它们中,我看到spring在某个时候将ROLE_添加到授权中,结果是ROLE_ROLE_USER。我可以通过将角色添加到没有ROLE_前缀的令牌中来修复这个问题。

String[] scope = user.getAuthorities().stream()
        .map(grantedAuthority -> StringUtils.substringAfter(grantedAuthority.getAuthority(),"ROLE_"))
        .toArray(String[]::new);

我在某个地方看到我们可以阻止Spring添加角色前缀,但是我已经丢失了这篇文章。

相关问题