Claude Code Plugins

Community-maintained marketplace

Feedback
0
0

IOE-DREAM微服务认证架构规范。涵盖Gateway集中认证(JWT验证、Token黑名单)、业务层授权(@PermissionCheck注解)、Spring Security依赖管理(业务服务排除)、Gateway身份头转发(X-User-Id等HTTP头)、RBAC规则引擎。使用时机:实现权限验证、配置Gateway、调试认证问题时。

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name microservice-auth
description IOE-DREAM微服务认证架构规范。涵盖Gateway集中认证(JWT验证、Token黑名单)、业务层授权(@PermissionCheck注解)、Spring Security依赖管理(业务服务排除)、Gateway身份头转发(X-User-Id等HTTP头)、RBAC规则引擎。使用时机:实现权限验证、配置Gateway、调试认证问题时。

microservice-auth - 微服务认证技能

版本: v1.0.0 更新时间: 2025-01-05 适用范围: IOE-DREAM微服务认证与授权


🎯 触发条件

当用户进行以下操作时,应调用本技能:

  1. 实现认证授权功能
  2. 使用@PermissionCheck注解
  3. 配置Spring Security依赖
  4. Gateway路由配置
  5. JWT Token验证
  6. 用户身份信息获取
  7. 权限规则设计
  8. 提到"认证"、"授权"、"权限"、"Gateway"、"JWT"等关键词时

📚 核心认证架构

1. Gateway集中认证架构

架构原则

认证在Gateway层面统一处理,授权在业务层灵活控制

┌─────────────────────────────────────────────────────────┐
│                    Gateway网关层(8088)               │
│  ┌─────────────────────────────────────────────────┐  │
│  │  1. JWT Token验证                               │  │
│  │     - 签名验证                                   │  │
│  │     - 过期检查                                   │  │
│  │     - 黑名单检查                                 │  │
│  └─────────────────────────────────────────────────┘  │
│  ┌─────────────────────────────────────────────────┐  │
│  │  2. 用户身份识别                                 │  │
│  │     - 解析JWT获取userId、username、roles        │  │
│  │     - 查询用户详细信息                           │  │
│  │     - 查询用户权限列表                           │  │
│  └─────────────────────────────────────────────────┘  │
│  ┌─────────────────────────────────────────────────┐  │
│  │  3. 身份头转发                                   │  │
│  │     - X-User-Id: 123456                         │  │
│  │     - X-User-Name: admin                        │  │
│  │     - X-User-Roles: admin,manager,user          │  │
│  │     - X-User-Permissions: access:device:query   │  │
│  └─────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│                   业务微服务层                         │
│  ┌─────────────────────────────────────────────────┐  │
│  │  业务层授权(@PermissionCheck注解)              │  │
│  │  - 路径匹配: /api/access/device/**              │  │
│  │  - 权限验证: 检查X-User-Permissions头           │  │
│  │  - 角色验证: 检查X-User-Roles头                 │  │
│  └─────────────────────────────────────────────────┘  │
│  ┌─────────────────────────────────────────────────┐  │
│  │  用户身份信息获取                                │  │
│  │  - 从HTTP头获取X-User-Id                        │  │
│  │  - 从HTTP头获取X-User-Name                      │  │
│  │  - 从HTTP头获取X-User-Roles                     │  │
│  └─────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘

核心设计

1. Gateway集中认证职责:

  • ✅ JWT Token验证(签名、过期、黑名单)
  • ✅ 用户身份识别(解析JWT、查询用户信息)
  • ✅ 权限列表查询(查询用户权限列表)
  • ✅ 身份头转发(X-User-Id、X-User-Name等)

2. 业务层授权职责:

  • ✅ 权限验证(@PermissionCheck注解)
  • ✅ 角色验证(@PermissionCheck注解)
  • ✅ 路径匹配(路径与权限映射)
  • ✅ 用户身份信息获取(从HTTP头获取)

3. 无状态设计:

  • ✅ 业务服务无Session
  • ✅ 完全依赖Gateway转发的身份信息
  • ✅ 不依赖Spring Security的Session机制

2. Gateway身份头转发

转发的HTTP头

X-User-Id: 123456                              # 用户ID
X-User-Name: admin                              # 用户名
X-User-Roles: admin,security_manager,user       # 角色列表(逗号分隔)
X-User-Permissions: access:device:query,access:area:view  # 权限列表(逗号分隔)

Gateway转发配置

// ioedream-gateway-service/src/main/java/net/lab1024/sa/gateway/filter/AuthenticationFilter.java

@Component
@Slf4j
public class AuthenticationFilter implements GlobalFilter, Ordered {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    private UserServiceClient userServiceClient;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = extractToken(exchange.getRequest());

        if (StringUtils.isBlank(token)) {
            return unauthorized(exchange, "Missing token");
        }

        try {
            // 1. 验证JWT Token
            if (!jwtTokenProvider.validateToken(token)) {
                return unauthorized(exchange, "Invalid token");
            }

            // 2. 解析用户ID
            Long userId = jwtTokenProvider.getUserIdFromToken(token);

            // 3. 查询用户信息
            UserInfoResponse userInfo = userServiceClient.getUserInfo(userId);

            // 4. 添加身份头到请求
            ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
                .header("X-User-Id", String.valueOf(userId))
                .header("X-User-Name", userInfo.getUsername())
                .header("X-User-Roles", String.join(",", userInfo.getRoles()))
                .header("X-User-Permissions", String.join(",", userInfo.getPermissions()))
                .build();

            // 5. 继续过滤器链
            return chain.filter(exchange.mutate().request(modifiedRequest).build());

        } catch (Exception e) {
            log.error("[Gateway] Token验证失败: {}", e.getMessage());
            return unauthorized(exchange, "Authentication failed");
        }
    }

    private String extractToken(ServerHttpRequest request) {
        String bearerToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    private Mono<Void> unauthorized(ServerWebExchange exchange, String message) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);

        String body = "{\"code\":401,\"message\":\"" + message + "\"}";
        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(body.getBytes());
        return exchange.getResponse().writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return -100; // 确保在其他过滤器之前执行
    }
}

3. @PermissionCheck权限注解

注解定义

// microservices-common-permission/src/main/java/net/lab1024/sa/common/permission/PermissionCheck.java

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionCheck {

    /**
     * 权限标识(多个用逗号分隔)
     *
     * 示例:
     * - 单个权限: "access:device:query"
     * - 多个权限(满足其一即可): "access:device:query,access:device:add"
     */
    String permission() default "";

    /**
     * 角色标识(多个用逗号分隔)
     *
     * 示例:
     * - 单个角色: "admin"
     * - 多个角色(满足其一即可): "admin,security_manager"
     */
    String role() default "";

    /**
     * 资源路径(可选,用于路径匹配)
     *
     * 示例:"/api/access/device/**"
     */
    String resource() default "";
}

注解处理器

// microservices-common-permission/src/main/java/net/lab1024/sa/common/permission/PermissionCheckAspect.java

@Aspect
@Component
@Slf4j
public class PermissionCheckAspect {

    @Override
    public Object proceed(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 获取请求对象
        ServletRequestAttributes attributes =
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 2. 从HTTP头获取用户身份信息
        String userId = request.getHeader("X-User-Id");
        String roles = request.getHeader("X-User-Roles");
        String permissions = request.getHeader("X-User-Permissions");

        if (StringUtils.isBlank(userId)) {
            throw new BusinessException("未登录或登录已过期");
        }

        // 3. 获取@PermissionCheck注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        PermissionCheck permissionCheck =
            signature.getMethod().getAnnotation(PermissionCheck.class);

        if (permissionCheck == null) {
            // 没有权限注解,直接放行
            return joinPoint.proceed();
        }

        // 4. 权限验证
        if (!hasPermission(permissionCheck, roles, permissions)) {
            log.warn("[权限验证] 用户{}无权限访问{}",
                userId, signature.getMethod().getName());
            throw new BusinessException("权限不足");
        }

        // 5. 权限验证通过,继续执行
        return joinPoint.proceed();
    }

    /**
     * 权限验证
     */
    private boolean hasPermission(PermissionCheck permissionCheck,
                                  String roles, String permissions) {
        // 1. 角色验证
        String requiredRole = permissionCheck.role();
        if (StringUtils.isNotBlank(requiredRole)) {
            List<String> roleList = Arrays.asList(requiredRole.split(","));
            List<String> userRoles = Arrays.asList(roles.split(","));

            boolean hasRole = roleList.stream().anyMatch(userRoles::contains);
            if (!hasRole) {
                return false;
            }
        }

        // 2. 权限验证
        String requiredPermission = permissionCheck.permission();
        if (StringUtils.isNotBlank(requiredPermission)) {
            List<String> permissionList = Arrays.asList(requiredPermission.split(","));
            List<String> userPermissions = Arrays.asList(permissions.split(","));

            boolean hasPermission = permissionList.stream()
                .anyMatch(userPermissions::contains);
            if (!hasPermission) {
                return false;
            }
        }

        return true;
    }
}

4. Controller使用@PermissionCheck

标准用法

// microservices/ioedream-access-service/src/main/java/net/lab1024/sa/access/controller/AccessDeviceController.java

@RestController
@RequestMapping("/api/access/device")
@Slf4j
public class AccessDeviceController {

    @Autowired
    private AccessDeviceService deviceService;

    /**
     * 查询设备列表
     *
     * 权限:access:device:query
     */
    @GetMapping("/query")
    @PermissionCheck(permission = "access:device:query")
    public ResponseDTO<PageResult<AccessDeviceVO>> queryDevice(
            @Valid AccessDeviceQueryForm form) {

        PageResult<AccessDeviceVO> pageResult = deviceService.queryDevice(form);
        return ResponseDTO.ok(pageResult);
    }

    /**
     * 添加设备
     *
     * 权限:access:device:add
     * 角色:admin 或 security_manager
     */
    @PostMapping("/add")
    @PermissionCheck(permission = "access:device:add", role = "admin,security_manager")
    public ResponseDTO<Long> addDevice(@Valid @RequestBody AccessDeviceAddForm form) {

        Long deviceId = deviceService.addDevice(form);
        return ResponseDTO.ok(deviceId);
    }

    /**
     * 更新设备
     *
     * 权限:access:device:update
     */
    @PostMapping("/update")
    @PermissionCheck(permission = "access:device:update")
    public ResponseDTO<Boolean> updateDevice(@Valid @RequestBody AccessDeviceUpdateForm form) {

        Boolean result = deviceService.updateDevice(form);
        return ResponseDTO.ok(result);
    }

    /**
     * 删除设备
     *
     * 权限:access:device:delete
     * 角色:仅admin
     */
    @PostMapping("/delete")
    @PermissionCheck(permission = "access:device:delete", role = "admin")
    public ResponseDTO<Boolean> deleteDevice(@RequestBody Long deviceId) {

        Boolean result = deviceService.deleteDevice(deviceId);
        return ResponseDTO.ok(result);
    }
}

5. 业务层获取用户身份信息

工具类

// microservices-common-permission/src/main/java/net/lab1024/sa/common/permission/UserContextHolder.java

public class UserContextHolder {

    /**
     * 获取当前用户ID
     */
    public static Long getUserId() {
        HttpServletRequest request = getHttpServletRequest();
        String userId = request.getHeader("X-User-Id");
        if (StringUtils.isBlank(userId)) {
            throw new BusinessException("未登录或登录已过期");
        }
        return Long.parseLong(userId);
    }

    /**
     * 获取当前用户名
     */
    public static String getUserName() {
        HttpServletRequest request = getHttpServletRequest();
        String userName = request.getHeader("X-User-Name");
        return userName;
    }

    /**
     * 获取当前用户角色列表
     */
    public static List<String> getUserRoles() {
        HttpServletRequest request = getHttpServletRequest();
        String roles = request.getHeader("X-User-Roles");
        if (StringUtils.isBlank(roles)) {
            return Collections.emptyList();
        }
        return Arrays.asList(roles.split(","));
    }

    /**
     * 获取当前用户权限列表
     */
    public static List<String> getUserPermissions() {
        HttpServletRequest request = getHttpServletRequest();
        String permissions = request.getHeader("X-User-Permissions");
        if (StringUtils.isBlank(permissions)) {
            return Collections.emptyList();
        }
        return Arrays.asList(permissions.split(","));
    }

    /**
     * 检查当前用户是否有指定角色
     */
    public static boolean hasRole(String role) {
        List<String> roles = getUserRoles();
        return roles.contains(role);
    }

    /**
     * 检查当前用户是否有指定权限
     */
    public static boolean hasPermission(String permission) {
        List<String> permissions = getUserPermissions();
        return permissions.contains(permission);
    }

    private static HttpServletRequest getHttpServletRequest() {
        ServletRequestAttributes attributes =
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            throw new BusinessException("无法获取请求上下文");
        }
        return attributes.getRequest();
    }
}

Service层使用示例

// microservices/ioedream-access-service/src/main/java/net/lab1024/sa/access/service/impl/AccessDeviceServiceImpl.java

@Service
@Slf4j
public class AccessDeviceServiceImpl implements AccessDeviceService {

    @Autowired
    private AccessDeviceManager deviceManager;

    @Override
    public PageResult<AccessDeviceVO> queryDevice(AccessDeviceQueryForm form) {
        // 获取当前用户ID
        Long userId = UserContextHolder.getUserId();

        // 获取当前用户角色
        List<String> roles = UserContextHolder.getUserRoles();

        log.info("[门禁管理] 用户{}查询设备列表,角色:{}", userId, roles);

        // 业务逻辑:权限范围过滤
        if (!UserContextHolder.hasRole("admin")) {
            // 非管理员只能查看授权区域的设备
            form.setAreaIds(getUserAuthorizedAreaIds(userId));
        }

        return deviceManager.queryDevice(form);
    }

    private List<Long> getUserAuthorizedAreaIds(Long userId) {
        // 查询用户授权的区域ID列表
        // ... 省略实现
        return new ArrayList<>();
    }
}

6. Spring Security依赖管理

Gateway服务(保留Spring Security)

<!-- ioedream-gateway-service/pom.xml -->
<dependencies>
    <!-- Gateway服务保留Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- JWT -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.12.6</version>
    </dependency>

    <!-- 其他依赖... -->
</dependencies>

业务服务(排除Spring Security)

<!-- ioedream-access-service/pom.xml -->
<dependencies>
    <!-- ❌ 业务服务不得引入Spring Security -->
   <!--
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
-->

    <!-- ✅ 使用common-permission模块 -->
    <dependency>
        <groupId>net.lab1024</groupId>
        <artifactId>microservices-common-permission</artifactId>
        <version>${project.version}</version>
    </dependency>

    <!-- 其他依赖... -->
</dependencies>

细粒度模块(common-security)

<!-- microservices-common-security/pom.xml -->
<dependencies>
    <!-- common-security保留Spring Security(用于Gateway) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- JWT -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.12.6</version>
    </dependency>

    <!-- ❌ 不传递给业务服务 -->
    <!-- 业务服务通过Gateway转发身份信息,不直接依赖common-security -->
</dependencies>

⚠️ 禁止事项

认证架构层面

  1. 禁止业务服务引入Spring Security:

    • ❌ 业务服务pom.xml中不得有spring-boot-starter-security依赖
    • ❌ 业务服务不得使用@PreAuthorize注解
    • ❌ 业务服务不得使用SecurityContextHolder获取用户信息
  2. 禁止绕过Gateway直接访问业务服务:

    • ❌ 客户端不得直接调用业务服务(必须通过Gateway)
    • ❌ 业务服务不得暴露非/api开头的内部接口
  3. 禁止业务服务实现认证逻辑:

    • ❌ 业务服务不得验证JWT Token
    • ❌ 业务服务不得查询用户信息(应由Gateway查询)

授权规范层面

  1. 禁止使用错误的权限注解:

    • ❌ 不得使用@PreAuthorize(应使用@PermissionCheck)
    • ❌ 不得使用@Secured(应使用@PermissionCheck)
  2. 禁止权限标识不规范:

    • ❌ 权限标识不清晰(如"query"、"add")
    • ❌ 权限标识不含模块前缀(如"device:query"应为"access:device:query")

身份信息获取层面

  1. 禁止从Session获取用户信息:

    • ❌ 不得使用request.getSession().getAttribute("userId")
    • ❌ 不得使用SecurityContextHolder.getContext().getAuthentication()
  2. 禁止硬编码用户信息:

    • ❌ 不得在代码中固定userId、userName
    • ❌ 不得绕过权限检查

✅ 认证授权检查清单

Gateway配置检查

  • Gateway保留Spring Security依赖
  • JWT Token验证功能完整
  • 身份头转发功能完整(X-User-Id等)
  • 用户信息查询功能完整
  • 黑名单检查功能完整

业务服务配置检查

  • 业务服务排除Spring Security依赖
  • 业务服务引入common-permission模块
  • Controller使用@PermissionCheck注解
  • Service使用UserContextHolder获取用户信息
  • 无@PreAuthorize注解

权限规范检查

  • 权限标识规范(模块:功能:操作)
  • 角色标识规范(admin、manager、user等)
  • 权限验证逻辑完整
  • 权限不足时抛出BusinessException

依赖管理检查

  • Gateway依赖common-security
  • 业务服务依赖common-permission
  • 业务服务不依赖common-security
  • 无循环依赖

🔍 相关技能

  • architecture-core: 架构设计核心(细粒度模块架构、四层架构)
  • controller-dev: Controller开发规范(@PermissionCheck注解使用)
  • service-dev: Service开发规范(UserContextHolder使用)
  • api-design: API设计规范(路径权限映射)

维护者: IOE-DREAM架构委员会 最后更新: 2025-01-05 版本: v1.0.0