如何使用自定义令牌系统保护Java Spring Boot API并实现用户权限管控
基于自建自定义令牌的Spring Boot API权限控制实现方案
整套方案完全适配自定义令牌体系,不需要依赖JWT等标准令牌格式,可直接复用自建令牌的存储、校验逻辑,实现令牌权限识别与Controller层的Admin访问控制。
一、自定义令牌认证过滤器:打通令牌校验到权限装载的链路
复用Spring Security的过滤器链路,避免手写重复的拦截逻辑:
- 继承
OncePerRequestFilter,确保单个请求周期内只执行一次令牌解析逻辑 - 从请求头(通常为
Authorization: Bearer <自定义令牌值>)提取携带的令牌字符串 - 调用自建的令牌校验服务:完成令牌有效性校验,同时拉取令牌绑定的权限类型(比如是Admin/普通User)
- 将校验通过的权限列表转换为Spring Security可识别的权限对象,存入
SecurityContextHolder,供后续接口层的权限校验环节使用 - 令牌无效、过期的请求直接返回401未认证响应,不进入后续业务流程
核心实现代码参考:
public class CustomTokenAuthFilter extends OncePerRequestFilter { @Autowired private CustomTokenService customTokenService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 提取请求中的自定义令牌 String token = request.getHeader("Authorization"); // 调用自建服务校验令牌并获取关联权限 TokenInfo tokenInfo = customTokenService.validateAndGetInfo(token); if (tokenInfo == null) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } // 转换为Spring Security标准权限列表,适配角色校验规则 List<GrantedAuthority> authorities = tokenInfo.getPermissionTypes() .stream() .map(perm -> new SimpleGrantedAuthority("ROLE_" + perm)) .collect(Collectors.toList()); // 构建认证上下文,供后续权限校验使用 Authentication auth = new UsernamePasswordAuthenticationToken( tokenInfo.getSubjectId, null, authorities ); SecurityContextHolder.getContext().setAuthentication(auth); filterChain.doFilter(request, response); } }
二、Spring Security基础链路配置
- 适配API场景:关闭表单登录、CSRF跨域校验,适配无状态的接口调用模式
- 指定自定义过滤器的执行顺序:将自定义令牌过滤器放在默认的用户名密码校验过滤器之前执行
- 配置基础路径拦截规则:将令牌获取、公开访问的接口设置为匿名可访问,其余所有接口默认要求完成认证才能访问
基础配置代码参考:
@Configuration @EnableMethodSecurity // Spring Security 6.x+ 开启方法级权限校验的注解,老版本替换为@EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .formLogin(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .requestMatchers("/auth/token").permitAll() // 放开令牌获取接口 .anyRequest().authenticated() // 其余接口默认要求认证 ) .addFilterBefore(customTokenAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }
三、Controller层的Admin权限访问控制
完全通过注解实现细粒度控制,不需要在业务方法内手写权限判断逻辑:
- 如果单个接口需要仅Admin可访问,在对应方法上加
@PreAuthorize("hasRole('ADMIN')")注解,框架会自动校验当前请求对应的权限列表,只有携带Admin权限的令牌才能调用成功,权限不足时直接返回403响应 - 如果整个Controller类下的所有接口都仅对Admin开放,直接在类上加上述权限注解,类内所有方法都会自动继承该校验规则
示例代码:
@RestController @RequestMapping("/api/admin") public class AdminController { // 仅Admin权限的令牌可调用该删除用户接口 @PreAuthorize("hasRole('ADMIN')") @DeleteMapping("/user/{id}") public Result deleteUser(@PathVariable Long userId) { // 具体业务逻辑 return Result.success(); } }
实现注意事项
- 权限命名需适配Spring Security的校验规则:角色类权限统一加
ROLE_前缀,避免hasRole注解匹配失效 - 不要将权限类型明文存在自定义令牌的可篡改字段中,必须在服务端完成令牌有效性校验后,再从服务端存储(数据库/Redis)拉取令牌绑定的权限,防止令牌被篡改后伪造Admin权限
- 令牌权限变更、过期、注销时,要同步清除服务端存储的对应权限数据,避免旧令牌残留的权限造成安全风险
- 不需要在每个接口中手动编写
if (userType.equals("ADMIN"))这类判断逻辑,完全复用Security上下文的权限校验能力,减少代码冗余
内容的提问来源于stack exchange,提问作者matthias-schneglberger
相关产品推荐
相关产品推荐

