You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot + JWT:如何在其他微服务中复用令牌?

解决方案:网关统一鉴权后传递角色信息,后端服务直接复用权限控制

核心思路

网关通过auth-service完成JWT的有效性验证后,直接解析令牌中的角色信息,通过可信的自定义请求头传递给X、Y服务;后端服务无需重复验证JWT,仅需提取请求头中的角色信息构建认证上下文,即可直接使用@PreAuthorize("hasRole('ROLE_ADMIN')")这类注解做权限控制。


具体实现步骤

1. 网关层:验证JWT并传递角色信息

以Spring Cloud Gateway为例,实现全局过滤器完成JWT验证、角色解析与请求头注入:

@Component
public class JwtAuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private AuthServiceClient authServiceClient; // 调用auth-service的客户端

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String jwtToken = authHeader.substring(7);
            // 调用auth-service验证令牌有效性并获取角色列表
            AuthValidateResult result = authServiceClient.validateAndParseToken(jwtToken);
            
            if (result.isValid()) {
                // 将角色列表以自定义请求头传递给后端服务,多角色用逗号分隔
                ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
                        .header("X-User-Roles", String.join(",", result.getRoles()))
                        .build();
                return chain.filter(exchange.mutate().request(modifiedRequest).build());
            } else {
                // 验证失败返回401
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
        }
        // 无有效令牌返回401
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }

    @Override
    public int getOrder() {
        return -100; // 确保该过滤器优先级最高,先于路由转发执行
    }
}

关键防护:在网关路由规则中添加断言,拒绝外部请求携带X-User-Roles头,防止伪造:

spring:
  cloud:
    gateway:
      routes:
        - id: x-service-route
          uri: lb://x-service
          predicates:
            - Path=/x/**
            - Header=X-User-Roles, !.* # 拒绝外部请求带该头

2. X/Y服务:提取角色并配置权限控制

配置Spring Security,通过自定义过滤器从请求头提取角色,构建已认证上下文:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级权限控制
public class ServiceSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                // 添加自定义过滤器,在用户名密码认证前执行
                .addFilterBefore(new RolesHeaderAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    // 自定义过滤器:从请求头提取角色,构建认证信息
    private class RolesHeaderAuthenticationFilter extends OncePerRequestFilter {

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
            String rolesStr = request.getHeader("X-User-Roles");
            if (rolesStr != null) {
                // 将角色字符串转为权限对象列表
                List<GrantedAuthority> authorities = Arrays.stream(rolesStr.split(","))
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());
                
                // 构建已认证的Authentication对象,存入安全上下文
                Authentication auth = new UsernamePasswordAuthenticationToken(
                        request.getHeader("X-User-Id"), // 可同时传递用户ID等其他信息
                        null,
                        authorities
                );
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
            chain.doFilter(request, response);
        }
    }
}

3. 方法级权限控制使用

在X/Y服务的业务方法上直接使用注解:

@RestController
@RequestMapping("/x")
public class XController {

    // 仅管理员可访问
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @GetMapping("/admin/operation")
    public String adminOnlyOperation() {
        return "Admin operation executed";
    }

    // 普通用户可访问
    @PreAuthorize("hasRole('ROLE_USER')")
    @GetMapping("/user/operation")
    public String userOperation() {
        return "User operation executed";
    }
}

注意事项

  • 网关与后端服务之间的通信需做隔离(如内部网络访问)或TLS加密,防止请求头被篡改
  • 若JWT中包含用户ID、昵称等其他信息,可通过相同方式添加自定义请求头传递
  • 可根据实际需求扩展网关过滤器,支持令牌过期自动刷新等逻辑

内容的提问来源于stack exchange,提问作者jugor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 20:15:20