开发者

SpringCloud实现权限管理(网关+jwt版)

开发者 https://www.devze.com 2025-10-31 10:46 出处:网络 作者: 一介输生
目录问题本质:为什么会有重复鉴权?传统单体架构流程微服务架构下的流程(1)创建 JWT 工具类(2)创建JwtFilter(3)配置 Spring Security首先要想一个问题:为什么微服务不能像普通的Spring Boot项目一样鉴权?其
目录
  • 问题本质:为什么会有重复鉴权?
    • 传统单体架构流程
    • 微服务架构下的流程
  • (1)创建 JWT 工具类
    • (2)创建JwtFilter
      • (3)配置 Spring Security

        首先要想一个问题:为什么微服务不能像普通的Spring Boot项目一样鉴权?其实并不是不能,而是不适合。 在微服务架构中使用 Spring Security + RBAC/JET 时,会面临 "重复鉴权" 的核心痛点。下面从技术原理到解决方案完整解析这个问题。

        问题本质:为什么会有重复鉴权?

        传统单体架构流程

        SpringCloud实现权限管理(网关+jwt版)

        所有权限校验集中在一个应用内完成 • 用户登录后,Session或Token在单应用中全局有效

        微服务架构下的流程

        SpringCloud实现权限管理(网关+jwt版)

        每个微服务都需要独立完成:

        1. 解析Token
        2. 查询数据库验证权限
        3. 构建SecurityContext

        假设有3个服务链式调用:

        客户端 → 网关 → 服务A → 服务B → 服务C 每个服务都重复: 查询用户数据(1次DB查询) 查询权限数据(1次DB查询) ​​总查询次数 = 3服务 × 2查询 = 6次​ • 在服务交叉的时候,导致 多次数据库查询重复计算,使数据库压力倍增和网络开销叠加。

        (1)创建 JWT 工具类

        在util模块下创建JWT 工具类

        @Compohttp://www.devze.comnent
        public class JwtTokenUtil {
            // 密钥,用于签名和验证 JWT,应妥善保管
            @Value("${jwt.secret}")
            private String secret;
            // JWT 的过期时间,这里设置为 10 小时
            @Value("${jwt.expiration}")
            private Long expiration;
        
            // 根据用户详细信息生成 JWT
            public String generateToken(Sy编程客栈sRoleNameUserId sysRoleNameUserId) {
                //自定义的声明
                Map<String, Object> claims = new HashMap<>();
                //鉴权所需的权限角色
                claims.put("identities",sysRoleNameUserId.getRoleNames());
                return createToken(claims, String.valueOf(sysRoleNameUserId.getUserId()));
            }
        
            // 创建 JWT 的具体方法
            private String createToken(Map<String, Object> claims, String subject) {
                return Jwts.builder()
                        .setClaims(claims)//自定义的声明
                        .setSubject(subject)//存的用户id
                        .setIssuedAt(new Date(System.currentTimeMillis()))
                        .setExpiration(new Date(System.currentTimeMillis() + expiration))
                        .signWith(SignatureAlgorithm.HS256, secret)
                        .compact();
            }
        
            //解析jwt
            public Claims extractAllClaims(String token) {
                return Jwts.parserBuilder()
                        .setSigningKey(secret)
                        .build()
                        .parseClaimsJws(token)
                        .getBody();
            }
        }
        

        JWT 的密钥(jwt.secret)应通过配置中心(如 Nacos)或环境变量注入,避免硬编码。这里选择环境变量注入: 在util模块下的application.yml配置文件里面指定

        jwt:
          secret: your-secret-key-here-must-be-at-least-256-bits-long
          expiration: 1000 * 60 * 60 * 10 # 10 hour
        

        (2)创建JwtFilter

        在gateway网关模块创建JwtFilter过滤器来验证并解析jwt,从而获取权限信息

        @Component
        public class JwtFilter implements GlobalFilter, Ordered {
        
            @Resource
            private JwtTokenUtil jwtTokenUtil;
        
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();
                HttpHeaders headers = request.getHeaders();
                List<String> authHeader = headers.get("Authorization");
                if (authHpythoneader != null && authHeader.get(0).startsWith("Bearer ")) {
                    String token = authHeader.get(0).substring(7);
                    // 验证 JWT 并提取角色信息
                    Claims claims = null;
                    try{
                        claims = jwtTokenUtil.extractAllClaims(token);
                    }catch (IllegalArgumentException e) {
                        //解析 JWT 时发生其他错误
                        System.out.phpprintln("解析 token 时发生其他错误");
                    } catch (ExpiredJwtException e) {
                        //JWT 已过期
                        System.out.println("token 已过期");
                    }
                    //取出权限角色列表
                    Object identitiesObj = claims.gejavascriptt("identities");
                    List<GrantedAuthority> authorities = new ArrayList<>();
                    if (identitiesObj instanceof List<?>) {
                        for (Object role : (List<?>) identitiesObj) {
                            if (role instanceof String) {
                                // 将字符串转换为 SimpleGrantedAuthority
                                authorities.add(new SimpleGrantedAuthority((String) role));
                            }
                        }
                    }
                    String userId = claims.getSubject();
                    //将 Authentication 对象设置到 SecurityContextHolder 中后,Spring Security 就能在后续的授权过程中使用这些权限信息了。
                    Authentication authentication = new UsernamePasswordAuthenticationToken(userId, null, authorities);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
                return chain.filter(exchange);
            }
        
            @Override
            public int getOrder() {
                return -1;
            }
        }
        

        (3)配置 Spring Security

        在网关gateway模块配置所有微服务整体的权限管理规则:

        @Configuration
        @EnableWebFluxSecurity
        public class SecurityConfig {
        
            @Bean
            public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
                http.csrf().disable()// 关闭 CSRF(跨站请求伪造)防护。在无状态 API(如 JWT 场景)中,CSRF 防护不必要(通常依赖 Authorization 头而非 Cookie)。 避免对 POST、PUT 等请求要求携带 CSRF Token。
                        .authorizeExchange()
                        .pathMatchers("/api/product/**").permitAll()
                        .pathMatchers("/admin/**").permitAll()
                        .anyExchange().authenticated();
                return http.build();
            }
        }
        

        如果各个微服务还需要独自的更细粒度的权限控制,只需要在单个微服务模块中单独配置一个Spring Security就行了。

        到此这篇关于SpringCloud实现权限管理(网关+jwt版)的文章就介绍到这了,更多相关SpringCloud 权限管理内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

        0

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        关注公众号