Spring Security自定义授权规则实现:用户资源修改权限问题
实现方案说明
针对你的场景,不需要强制自定义AuthorizationManager,Spring Security提供了两种贴合其设计的实现方式,可根据场景复杂度选择:
前提准备:解析JWT到Authentication对象
首先需要确保JWT中的sub(用户ID)和roles(角色)被正确提取并转换为Spring Security的Authentication对象。通过配置JwtAuthenticationConverter实现:
@Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter(); authoritiesConverter.setAuthorityPrefix("ROLE_"); // 为角色添加ROLE_前缀,适配Spring Security的角色校验规则 authoritiesConverter.setAuthoritiesClaimName("roles"); // 指定JWT中存储角色的字段名 JwtAuthenticationConverter converter = new JwtAuthenticationConverter(); converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter); return converter; }
在SecurityFilterChain中配置OAuth2资源服务器时关联该转换器,确保JWT被正确解析为JwtAuthenticationToken。
方式一:表达式访问控制(简单场景首选)
利用Spring Security的SpEL表达式,结合自定义校验Bean实现授权规则,无需自定义AuthorizationManager。
步骤1:编写授权校验Bean
创建一个组件类,封装所有者/管理员的校验逻辑:
@Component public class UserAuthorizationChecker { public boolean isOwnerOrAdmin(Authentication authentication, HttpServletRequest request) { // 优先校验是否为管理员角色 boolean isAdmin = authentication.getAuthorities().stream() .anyMatch(auth -> auth.getAuthority().equals("ROLE_ADMIN")); if (isAdmin) { return true; } // 校验是否为资源所有者 if (!(authentication instanceof JwtAuthenticationToken jwtToken)) { return false; } String currentUserId = jwtToken.getToken().getClaim("sub"); // 从请求中获取路径参数userId Map<String, String> pathVariables = (Map<String, String>) request.getAttribute( HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE ); String targetUserId = pathVariables.get("userId"); return currentUserId.equals(targetUserId); } }
步骤2:配置HttpSecurity
在SecurityFilterChain中通过access()方法引用自定义校验逻辑:
@Configuration @EnableWebSecurity public class SecurityConfig { private final UserAuthorizationChecker authorizationChecker; public SecurityConfig(UserAuthorizationChecker authorizationChecker) { this.authorizationChecker = authorizationChecker; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 针对PUT请求的用户资源路径配置授权规则 .requestMatchers(HttpMethod.PUT, "/some_path/user/{userId}") .access("@userAuthorizationChecker.isOwnerOrAdmin(authentication, request)") // 其他请求只需认证即可 .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(jwtAuthenticationConverter()) ) ); return http.build(); } // 引入前面定义的jwtAuthenticationConverter Bean }
方式二:自定义/组合AuthorizationManager(复杂场景首选)
Spring Security 5.6+推荐使用AuthorizationManager作为授权核心组件,适合需要复用授权规则、扩展性要求高的场景。可以通过组合现有组件+自定义逻辑实现:
步骤1:实现所有者校验的AuthorizationManager
public class OwnerAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> { @Override public AuthorizationDecision check(Supplier<Authentication> authenticationSupplier, RequestAuthorizationContext context) { Authentication authentication = authenticationSupplier.get(); if (!(authentication instanceof JwtAuthenticationToken jwtToken)) { return new AuthorizationDecision(false); } String currentUserId = jwtToken.getToken().getClaim("sub"); String targetUserId = context.getVariables().get("userId"); boolean isOwner = Objects.equals(currentUserId, targetUserId); return new AuthorizationDecision(isOwner); } }
步骤2:组合管理员角色校验与所有者校验
通过OrAuthorizationManager将两个规则组合(满足任一即可):
@Bean public AuthorizationManager<RequestAuthorizationContext> userModifyAuthorizationManager() { // 管理员角色校验规则 AuthorizationManager<RequestAuthorizationContext> adminRule = AuthorityAuthorizationManager.hasRole("ADMIN"); // 所有者校验规则 AuthorizationManager<RequestAuthorizationContext> ownerRule = new OwnerAuthorizationManager(); // 组合为"或"规则 return new OrAuthorizationManager<>(adminRule, ownerRule); }
步骤3:配置HttpSecurity
在SecurityFilterChain中直接引用组合后的AuthorizationManager:
@Configuration @EnableWebSecurity public class SecurityConfig { private final AuthorizationManager<RequestAuthorizationContext> userModifyAuthorizationManager; public SecurityConfig(AuthorizationManager<RequestAuthorizationContext> userModifyAuthorizationManager) { this.userModifyAuthorizationManager = userModifyAuthorizationManager; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.PUT, "/some_path/user/{userId}") .access(userModifyAuthorizationManager) .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(jwtAuthenticationConverter()) ) ); return http.build(); } // 引入前面定义的jwtAuthenticationConverter Bean }
总结
- 是否必须自定义AuthorizationManager? 否。简单场景用表达式访问控制更简洁;复杂场景或需要遵循Spring Security最新授权模型时,自定义/组合AuthorizationManager是更贴合的选择。
- 两种方式均无需访问用户数据库,完全基于JWT中的信息完成授权校验,适配微服务架构的无状态要求。
内容的提问来源于stack exchange,提问作者momopoi
相关产品推荐
相关产品推荐

