首页 新闻 会员 周边

spring security自定义授权,校验总为false,谢谢

0
悬赏园豆:10 [待解决问题]

自定义的授权:

@Component("rbacService")
public class RBACService {
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
if (principal instanceof LoginUser) {//instanceof编译器会检查 obj 是否能转换成右边的class类型
LoginUser LoginUser = (LoginUser) principal;
//req.getRequestURI() 当前请求的路径
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(request.getRequestURI());
//userDetails.getAuthorities() // 用户所拥有的所有资源
Collection<? extends GrantedAuthority> authorities = LoginUser.getAuthorities();

System.out.println(simpleGrantedAuthority);
// 输出:/api/test1
System.out.println(authorities);
// 输出:[{"authority":"/api/test1"}, {"authority":"/api/test2"}, {"authority":"/api/test3"}, {"authority":"/api/test4"}]
boolean contains = authorities.contains(simpleGrantedAuthority);
System.out.println(contains);
// 输出:false(问题1:为什么总是为false?权限中是存在有“/api/test1”)
return contains;
}
return false;
}
}
问题2:一次请求,而这个RBACService被调用两次,这是为何?

配置类如下:
 @Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
return http
// 跨域配置
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// 配置路径是否需要认证
// .authorizeHttpRequests()
.authorizeRequests()
// 允许匿名访问
.antMatchers("/api/auth/**").permitAll()
// 注意:.anyRequest()只能出现一次,否则报错:Caused by: java.lang.IllegalStateException: Can't configure anyRequest after itself
.anyRequest().access("@rbacService.hasPermission(request,authentication)")
.and()
//关闭csrf (注意:允许匿名访问,但不关闭csrf访问也是报403)
.cors().configurationSource(this.corsConfigurationSource())
.and()
// 禁用缓存,使用无状态session,即不使用session缓存数据
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// 添加JWT过滤器
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}

/**
*跨域资源配置
*/
@Bean
public CorsConfigurationSource corsConfigurationSource(){
CorsConfiguration cors = new CorsConfiguration();
cors.addAllowedOriginPattern("*");
// TODO 正式项目为保安全须设置cors
// cors.setAllowedOriginPatterns(List.of("http://192.168.1.6:8887"));
cors.setAllowCredentials(true);
cors.addAllowedHeader("*");
cors.addAllowedMethod("*");
cors.addExposedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", cors);
return source;
}

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

/**
* TODO 4.4 基于用户名和密码或使用用户名和密码进行身份验证
* @param config
* @return
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
 
qjwen8的主页 qjwen8 | 初学一级 | 园豆:184
提问于:2023-06-15 23:33
< >
分享
所有回答(2)
0

问题一:
在您的配置代码中,看起来主要问题在于您的自定义授权逻辑@rbacService.hasPermission(request,authentication)被设置为anyRequest().access(),但这种方式可能会导致许多问题,包括Spring Security无法正确解析您的自定义逻辑并为请求提供正确的授权。

为了解决这个问题,建议使用更直接的方式配置自定义授权。您可以继承WebSecurityConfigurerAdapter并在configure(HttpSecurity http)方法中进行配置,以使用AccessDecisionManager来实现您的自定义授权逻辑。

首先,创建一个自定义的AccessDecisionManager实现,例如:

@Component
public class RBACAccessDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication authentication, Object object, Collection<? extends GrantedAuthority> authorities) {
        // 在这里实现您的自定义授权逻辑,例如检查用户的权限是否包含请求的URL等
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

然后,在您的SecurityConfiguration中使用这个RBACAccessDecisionManager,并配置http.authorizeRequests().accessDecisionManager(),例如:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private RBACAccessDecisionManager rbacAccessDecisionManager;
    
    // ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
                .accessDecisionManager(rbacAccessDecisionManager)
                .and()
            // 其他配置...
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .cors().configurationSource(this.corsConfigurationSource())
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

    }

    // ...
}

RBACAccessDecisionManagerdecide()方法实现中,您可以使用authentication对象获取当前用户的信息,然后根据您的逻辑与用户权限进行比较,从而判断是否授权。

lanedm | 园豆:2381 (老鸟四级) | 2023-06-16 08:33

问题2:
在您的配置中,RBACService被调用两次的原因可能是由于您的配置中使用了两个不同的Spring Security过滤器。

首先,在您的SecurityConfiguration中,您使用了jwtAuthenticationFilter作为UsernamePasswordAuthenticationFilter之前的过滤器。这个过滤器负责验证JWT并将用户信息添加到Spring Security的上下文中。

其次,在您的RBACService中,您使用了@PreAuthorize注解来进行权限验证。这个注解会在方法执行之前进行权限验证。

因此,当您发送一次请求时,首先会经过jwtAuthenticationFilter进行JWT验证和用户信息添加,然后请求将继续到达您的控制器方法,同时RBACService也会被调用来进行权限验证。

如果您希望减少RBACService被调用的次数,可以考虑将权限验证逻辑移动到jwtAuthenticationFilter中,或者在jwtAuthenticationFilter中缓存权限验证的结果,以避免多次调用RBACService

另外,还可以检查您的代码中是否存在其他地方调用了RBACService,或者在其他地方使用了@PreAuthorize注解,这也可能导致RBACService被调用多次。

支持(0) 反对(0) lanedm | 园豆:2381 (老鸟四级) | 2023-06-16 08:34
0

在你提供的代码中,存在两个问题:

问题1:校验总是返回false的原因
在你的代码中,将请求的URI作为权限进行校验,但是在用户的权限列表中,权限是以SimpleGrantedAuthority对象的形式存在的,而不是以字符串的形式。因此,直接使用authorities.contains(simpleGrantedAuthority)会返回false,因为集合中存储的是对象而不是字符串。

解决方法是,将请求的URI作为字符串进行比较,而不是SimpleGrantedAuthority对象。可以使用SimpleGrantedAuthority的getAuthority()方法获取权限的字符串表示形式,并与请求的URI进行比较,如下所示:

java
Copy code
String requiredAuthority = request.getRequestURI();
boolean contains = authorities.stream()
.anyMatch(authority -> authority.getAuthority().equals(requiredAuthority));
问题2:RBACService被调用两次的原因
在Spring Security中,为了进行请求匹配和授权决策,可能会对URL进行多次匹配。这可能导致自定义的RBACService被多次调用。

要解决这个问题,可以使用WebExpressionVoter的setAllowEmptyAuthorization方法将空的认证对象(没有认证信息)视为已授权。这样可以确保RBACService只在有认证信息时被调用一次。

在你的SecurityConfiguration类中,可以添加以下代码来解决这个问题:

java
Copy code
@Bean
public AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<?>> decisionVoters = Arrays.asList(
new WebExpressionVoter().setExpressionHandler(new DefaultWebSecurityExpressionHandler()),
new RoleVoter(),
new AuthenticatedVoter()
);

AffirmativeBased accessDecisionManager = new AffirmativeBased(decisionVoters);
accessDecisionManager.setAllowIfAllAbstainDecisions(false);

return accessDecisionManager;

}
这样配置后,RBACService应该只被调用一次。

希望以上解答能帮到你,如果还有其他问题,请随时提问。

Technologyforgood | 园豆:5718 (大侠五级) | 2023-06-21 22:38
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册