林昶
林昶
Published on 2025-10-23 / 82 Visits
2
0

微服务网关鉴权方案说明与使用文档

1. 方案概述

本方案为基于 Spring Cloud Gateway 的微服务网关鉴权解决方案,提供了一套完整的 API 访问控制机制。方案核心特点包括:

  • JWT 令牌验证​:基于标准的 JWT 令牌进行身份认证

  • 多级缓存策略​:本地缓存(Caffeine)+ Redis 分布式缓存

  • 灵活白名单​:支持 Ant 风格通配符的白名单配置

  • 细粒度权限控制​:基于用户-角色-权限的访问控制模型

  • 高性能设计​:优化的缓存策略减少权限服务调用

2. 架构设计

3. 核心组件

3.1 AuthGlobalFilter

网关全局过滤器,负责:

  • 白名单检查

  • JWT 令牌解析与验证

  • 权限检查

  • 请求转发与拦截

3.2 ApiPermissionService

权限服务接口,提供:

  • 用户权限查询

  • 权限缓存管理

  • 权限验证逻辑

3.3 AuthProperties

白名单配置类,从配置文件加载白名单路径

4. 配置说明

4.1 配置文件示例 (application.yml)

server:
  port: 8080

#取openfeign的kernel地址
spring:
  cloud:
    openfeign:
      client:
        config:
          kernel-biz:
            url: ${KERNEL_BIZ:http://localhost:8015}

auth:
  ignore-urls: # 白名单配置
    - /**/v3/api-docs
    - /**/public/**
    - /api/auth/login
    - /swagger-ui/**
    - /actuator/health
    - /api/static/**

4.2 配置项说明

配置项

说明

示例值

spring.cloud.openfeign.client.config.kernel-biz.url

权限服务地址

http://localhost:8015

auth.ignore-urls

白名单路径列表

[/**/v3/api-docs, /**/public/**]

spring.data.redis.host

Redis 主机地址(依赖redis)

localhost

spring.data.redis.port

Redis 端口(依赖redis)

6379

5. 白名单配置规则

5.1 通配符说明

通配符

说明

示例

?

匹配单个字符

/user?匹配 /user1, /userA

*

匹配任意数量字符(不包括路径分隔符)

/api/*匹配 /api/user, /api/product

**

匹配任意数量字符(包括路径分隔符)

/**/public/**匹配 /any/path/public/sub

5.2 常用配置模式

需求

配置模式

说明

匹配特定路径

/exact/path

精确匹配

匹配任意前缀

/**/target

/**/v3/api-docs

匹配目录下所有内容

/public/**

匹配 /public/开头的所有路径

匹配单级目录

/api/*/info

匹配 /api/user/info, /api/product/info

6. 权限缓存策略

6.1 多级缓存架构

本地缓存 (Caffeine) → Redis 缓存 → 权限服务

6.2 缓存键设计

user_perms:{tenantId}:{renterId}:{userId}

示例:user_perms:tenant123:renter456:user789

6.3 缓存更新机制

  1. 本地缓存​:

    • 有效期:10 分钟

    • 最大容量:1000 个用户

    • 更新策略:从 Redis 缓存加载

  2. Redis 缓存​:

    • 有效期:30 分钟

    • 数据结构:Set(存储权限集合)

    • 更新策略:从权限服务加载

6.4 缓存刷新

当用户权限变更时,调用以下接口清除缓存:

@PostMapping("/refresh-permissions")
public void refreshPermissions(
    @RequestParam String userId,
    @RequestParam String tenantId,
    @RequestParam String renterId) {
    
    permissionService.clearUserCache(userId, tenantId, renterId);
}

7. JWT 要求

7.1 使用华为MBM系统的JWT Token

解析出来的Token内容示例

{
  "iss": "iMES",
  "iat": 1761186544,
  "exp": 1761906544,
  "iAccount": "linchang",
  "iTenantId": "067cfa58c8404a908780bda7e934e1b1",
  "userName": "林昶",
  "UserId": "1959914898518122496"
}

7.2 使用到的声明

声明

说明

示例

iAccount

用户 ID

"linchang"

iTenantId

租户ID

067cfa58c8404a908780bda7e934e1b1

8. API 接口

8.1 权限服务接口

端点

方法

参数

说明

/v1/api-permission/has-api-permission

GET

userId, tenantId, renterId, apiUrl

检查单个 API 权限

/v1/api-permission/get-user-api-permissions

GET

userId, tenantId, renterId

获取用户所有 API 权限

/v1/api-permission/refresh-permissions

POST

userId, tenantId, renterId

刷新用户权限缓存

8.2 网关错误响应

状态码

含义

响应体

401 Unauthorized

未授权/令牌无效

{"code":401,"message":"Invalid token"}

403 Forbidden

权限不足

{"code":403,"message":"Permission denied"}

500 Internal Server Error

权限服务不可用

{"code":500,"message":"Permission service unavailable"}

9. 种子数据

9.1 API种子数据

# application.yml
# 产品线名称(必填),根据微服务产品线填写,目前有public/aps/mdm/les/mes/mpm/qms/otb这些产品线,项目上微服务自行定义
product: public

# 种子数据配置
hwmbm:
  seedData:
    enabled: true

微服务启动时会自动扫描系统里所有API接口,生成标准化种子数据。大部分情况下API种子数据无需自定义,如果有特殊需求也可自定义

public class ApiSeedData implements SeedDataInitial<ApiDefinition> {
    @Override
    public List<ApiDefinition> getSeedData() {
        return Arrays.asList(
                new ApiDefinition() {{
                    setApiId("api_appBusinessPackage_tree");
                    setAppModule("imes.center.msm");
                    setContextPath("/api/appBusinessPackage/tree");
                    setGroupCode("appBusinessPackage");
                    setGroupName("业务包管理");
                    setApiCode("tree");
                    setApiName("业务包树");
                    setApiKey("imom.public.devops.appBusinessPackage.tree");
                }}
        );
    }
}

执行发布种子数据

确认API已经录入华为系统

9.1 栏目绑定API种子数据

同时,由于华为MBM系统的API权限配置是关联在按钮上的,所以所有的API需要配置在栏目按钮中,配置已经集成进栏目种子数据,示例如下

public class CatalogSeedData implements SeedDataInitial<CatalogDefinition> {
    @Override
    public List<CatalogDefinition> getSeedData() {
        return Arrays.asList(
                new CatalogDefinition() {{
                    setCatalogName("查询");
                    setCatalogKey("imes.center.msm.mobile.appBusinessPackage.pageMain.btnSearch");
                    setCatalogCode("btnSearch");
                    setCatalogType(CatalogTypeEnum.BUTTON);
                    setParentPath("imes.center.msm.mobile.appBusinessPackage.pageMain");
                    setAppModule("imes.center.msm");
                    setSeq(1);
                    //设置按钮绑定的API接口,支持多个
                    setApiKeys(List.of("imom.public.devops.appBusinessPackage.tree"));
                }}

        );
    }
}

执行发布栏目种子数据

即可看到华为栏目上绑定了API权限

配置角色权限的时候,角色有按钮权限时即表示同时拥有了按钮关联的API权限。

10. CDC缓存刷新机制

10.1 技术架构

┌─────────────┐    ┌─────────────┐    ┌───────────────┐
│   CDC系统    │───▶│ Redis Stream │───▶│ Kernel服务 │
└─────────────┘    └─────────────┘    └───────────────┘
                         │                    │
                         ▼                    ▼
┌─────────────────────────────────────────────────┐
│               缓存清理与消息分发                  │
│  ┌─────────────┐    ┌─────────────────┐         │
│  │ 清理Redis缓存 │───▶│ Redis Pub/Sub │         │
│  └─────────────┘    └─────────────────┘         │
└─────────────────────────────────────────────────┘
                         │
                         ▼
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   API网关    │◀──│ 清理本地缓存 │◀──│ 接收Pub/Sub │
└─────────────┘    └─────────────┘    └─────────────┘
  • 实时性:权限变更秒级生效

  • 准确性:精确清理受影响用户缓存

  • 高性能:多级缓存架构,减少数据库压力

  • 可扩展:支持多网关实例水平扩展

10.2 基础配置

mdm主数据微服务负责CDC及消息通知发送,配置文件需配上华为msm服务数据源及冗余库数据源,同时启用数据同步

spring:
  autoconfigure:
    exclude:
      - com.sie.mbm.mom.framework.data.mybatis.MybatisPlusConfiguration
      - org.springframework.cloud.gateway.config.GatewayAutoConfiguration
      - org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration

mybatis-flex:
  datasource:
    #IMOM数据库
    imom:
      username: ${MYSQL_USER:root}
      password: ${MYSQL_PWD:123}
      url: jdbc:mysql://192.168.181.153:3306/xdm_dev?serverTimezone=UTC&characterEncoding=utf8
    #IMOM冗余数据库
    imom-redundant:
      username: ${MYSQL_USER:root}
      password: ${MYSQL_PWD:123}
      url: jdbc:mysql://192.168.181.153:3306/imom_redundant?serverTimezone=UTC&characterEncoding=utf8
    #MBM-msm只读库
    mbm-msm-reserve:
      username: ${MYSQL_USER:root}
      password: ${MYSQL_PWD:123}
      url: jdbc:mysql://192.168.181.153:3306/mbm_dev_msm?serverTimezone=UTC&characterEncoding=utf8

redundant:
  sync:
    enable: true

CDC配置中按如下配置,文件名为redundant-msm.json

{
  "sync": {
    "items": [
      {
        "name": "t_msm_catalog_api",
        "source": {
          "datasource": "mbm-msm-reserve",
          "name": "t_msm_catalog_api",
          "updateFieldName": "last_updated_date",
          "changeTrigger": true
        },
        "target": {
          "name": "t_msm_catalog_api"
        }
      },
      {
        "name": "t_msm_role_permission",
        "source": {
          "datasource": "mbm-msm-reserve",
          "name": "t_msm_role_permission",
          "updateFieldName": "last_updated_date",
          "changeTrigger": true
        },
        "target": {
          "name": "t_msm_role_permission"
        }
      },
      {
        "name": "t_msm_user_role",
        "source": {
          "datasource": "mbm-msm-reserve",
          "name": "t_msm_user_role",
          "updateFieldName": "last_updated_date",
          "changeTrigger": true
        },
        "target": {
          "name": "t_msm_user_role"
        }
      },
      {
        "name": "t_msm_api",
        "source": {
          "datasource": "mbm-msm-reserve",
          "name": "t_msm_api",
          "updateFieldName": "last_updated_date",
          "changeTrigger": true
        },
        "target": {
          "name": "t_msm_api"
        }
      },
      {
        "name": "t_msm_role",
        "source": {
          "datasource": "mbm-msm-reserve",
          "name": "t_msm_role",
          "updateFieldName": "last_updated_date",
          "changeTrigger": true
        },
        "target": {
          "name": "t_msm_role"
        }
      }
    ]
  }
}

同时,kernel微服务确认有配置华为msm只读库,以及确认common配置文件中有redis配置文件

mybatis-flex:
  datasource:
    #imom-kernel主库
    imom-kernel:
      username: ${MYSQL_USER:root}
      password: ${MYSQL_PWD:123}
      url: jdbc:mysql://192.168.181.153:3306/imom_kernel?serverTimezone=UTC&characterEncoding=utf8
      type: com.alibaba.druid.pool.DruidDataSource
      druid:
        max-wait: 10000
        stat-view-servlet:
          enabled: true
          url-pattern: /druid/*
          login-username: admin
          login-password: Sie123456
        filter:
          stat:
            enabled: true
            log-slow-sql: true
          wall:
            config:
              multi-statement-allow: true
    #mbm-msm只读库
    mbm-msm-reserve:
      username: ${MYSQL_USER:root}
      password: ${MYSQL_PWD:123}
      url: jdbc:mysql://192.168.181.153:3306/mbm_dev_msm?serverTimezone=UTC&characterEncoding=utf8

启动服务即可生效。

11. 常见问题排查

11.1 白名单不生效

  1. 检查配置文件格式是否正确

  2. 验证路径匹配规则:

    // 调试代码
    System.out.println("Checking path: " + path + " against pattern: " + pattern);
  3. 确认请求路径包含完整上下文

11.2 权限检查性能下降

  1. 检查 Redis 连接状态

  2. 监控缓存命中率

  3. 检查权限服务响应时间

11.3 Token 验证失败

  1. 检查 Token 是否过期

  2. 验证 Token 签名算法

  3. 确认 Token 包含必需声明

12. 扩展与定制

12.1 自定义权限策略

实现 ApiPermissionService接口:

@Service
public class CustomPermissionService implements ApiPermissionService {
    
    @Override
    public boolean hasApiPermission(String userId, String tenantId, 
                                   String renterId, String apiUrl) {
        // 自定义权限检查逻辑
    }
}

12.2 添加额外验证

AuthGlobalFilter中扩展:

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    // 在权限检查前添加额外验证
    if (needExtraVerification(exchange)) {
        return extraVerification(exchange);
    }
    
    // 原有逻辑...
}

附:培训视频


Comment