在 Spring Security 整合 OAuth2 的认证授权体系中,默认的权限校验通常基于静态配置或者固定的角色规则,当业务需要根据实时数据、用户属性动态调整权限判断逻辑时,就需要通过自定义 Filter 来扩展权限校验能力。自定义 Filter 可以在 OAuth2 完成令牌校验之后,进入接口方法之前介入请求处理流程,实现灵活的权限判断。

自定义 Filter 的实现思路
要实现在 OAuth2 流程中注入动态权限校验,首先需要明确 Filter 的执行时机。OAuth2 的令牌校验由 Spring Security 内置的 OAuth2 相关 Filter 完成,我们的自定义 Filter 需要放在这些 Filter 之后执行,这样才能获取到已经解析完成的用户认证信息,再基于这些信息做动态权限判断。
核心实现步骤
- 创建实现
jakarta.servlet.Filter接口的自定义 Filter 类 - 在 Filter 中获取当前请求的 OAuth2 认证信息,提取用户标识、角色等基础数据
- 调用动态权限规则获取接口,查询当前请求路径对应的权限要求
- 对比用户权限与规则要求,不满足则直接返回无权限响应
- 将自定义 Filter 注册到 Spring Security 的 Filter 链中,指定正确的执行顺序
自定义 Filter 代码实现
以下是一个基础的自定义动态权限校验 Filter 实现示例,代码基于 Spring Security 6 和 Jakarta Servlet 规范:
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class DynamicPermissionFilter implements Filter {
// 动态权限规则获取接口,实际业务中可替换为数据库、配置中心等实现
private final DynamicPermissionService permissionService;
public DynamicPermissionFilter(DynamicPermissionService permissionService) {
this.permissionService = permissionService;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 获取当前请求的认证信息,OAuth2 流程中会解析为 JwtAuthenticationToken
Authentication authentication = (Authentication) request.getUserPrincipal();
// 如果未认证,直接放行给后续 Filter 处理,避免重复校验
if (authentication == null || !authentication.isAuthenticated()) {
filterChain.doFilter(request, response);
return;
}
// 提取当前请求的路径和方法
String requestUri = request.getRequestURI();
String requestMethod = request.getMethod();
// 获取当前用户的所有权限标识
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
List<String> userPermissions = authorities.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
// 如果是 JWT 认证,还可以从 JWT 中提取自定义字段,比如用户ID、部门等信息
String userId = null;
if (authentication instanceof JwtAuthenticationToken jwtAuth) {
userId = jwtAuth.getToken().getClaimAsString("user_id");
}
// 调用动态权限服务,判断当前用户是否有访问当前接口的权限
boolean hasPermission = permissionService.checkPermission(userId, userPermissions, requestUri, requestMethod);
if (!hasPermission) {
// 无权限时返回 403 状态码和提示信息
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{"code":403,"msg":"无访问权限"}");
return;
}
// 权限校验通过,继续执行后续流程
filterChain.doFilter(request, response);
}
}
动态权限服务接口定义
上面的 Filter 依赖动态权限服务来获取权限规则,以下是该服务的基础接口定义,开发者可以根据实际业务实现具体的逻辑:
import java.util.List;
public interface DynamicPermissionService {
/**
* 校验用户是否有访问指定接口的权限
* @param userId 用户ID,可为空
* @param userPermissions 用户已有的权限标识列表
* @param requestUri 请求路径
* @param requestMethod 请求方法
* @return 是否有权限
*/
boolean checkPermission(String userId, List<String> userPermissions, String requestUri, String requestMethod);
}
将自定义 Filter 注册到 Spring Security
实现 Filter 之后,需要将其注册到 Spring Security 的 Filter 链中,并且要保证它在 OAuth2 令牌校验 Filter 之后执行。可以通过 SecurityFilterChain 来配置:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
public class SecurityConfig {
private final DynamicPermissionService dynamicPermissionService;
public SecurityConfig(DynamicPermissionService dynamicPermissionService) {
this.dynamicPermissionService = dynamicPermissionService;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// 创建自定义 Filter 实例
DynamicPermissionFilter dynamicPermissionFilter = new DynamicPermissionFilter(dynamicPermissionService);
http
// OAuth2 资源服务器配置
.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())))
// 配置自定义 Filter,放在 UsernamePasswordAuthenticationFilter 之后,确保认证信息已经解析完成
.addFilterAfter(dynamicPermissionFilter, UsernamePasswordAuthenticationFilter.class)
// 其他安全配置
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
// JWT 认证转换器,用于提取 JWT 中的权限信息
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
// 设置 JWT 中权限字段的 key,默认是 scope,可根据实际调整
authoritiesConverter.setAuthorityPrefix("");
authoritiesConverter.setAuthoritiesClaimName("permissions");
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
return converter;
}
}
注意事项
- 自定义 Filter 的执行顺序非常重要,如果放在 OAuth2 令牌校验 Filter 之前,会无法获取到正确的认证信息,导致校验逻辑失效。
- 动态权限规则的实现需要考虑性能问题,高频访问的接口权限规则建议做缓存,避免每次请求都查询数据库或者配置中心。
- 对于不需要权限校验的公开接口,需要在 Filter 中做白名单处理,或者在 Spring Security 的 authorizeHttpRequests 中提前放行,避免不必要的校验。
- 如果系统中同时存在多种认证方式,需要在 Filter 中做好认证类型的判断,避免非 OAuth2 认证请求出现异常。
常见问题排查
如果自定义 Filter 没有生效,可以先检查 Filter 的注册顺序是否正确,通过调试查看 Filter 的 doFilter 方法是否被调用。如果获取不到认证信息,大概率是 Filter 执行时机过早,需要调整 addFilterAfter 的参数,确保在 OAuth2 相关的认证 Filter 之后执行。如果权限校验逻辑不符合预期,可以先打印出用户权限列表和请求的路径、方法信息,对比动态权限规则的实现是否有问题。
Spring_SecurityOAuth2自定义Filter动态权限校验修改时间:2026-07-04 20:45:36