Shiro在微服务与云原生环境中的适配实践

1. 无状态化支持:JWT替代Session方案

概念解析

在传统单体应用中,Shiro默认使用Session管理用户状态,但在微服务架构中,Session会导致以下问题:

  • 服务间状态同步困难
  • 横向扩展时Session共享开销
  • 不符合RESTful无状态原则

JWT解决方案

public class JWTRealm extends AuthorizingRealm {
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        String jwt = (String) token.getPrincipal();
        // 验证JWT签名、有效期等
        DecodedJWT decoded = JWT.require(Algorithm.HMAC256("secret")).build().verify(jwt);
        return new SimpleAuthenticationInfo(decoded.getSubject(), jwt, getName());
    }
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 从JWT claims中解析权限信息
        String jwt = principals.toString();
        DecodedJWT decoded = JWT.decode(jwt);
        Set<String> roles = parseRoles(decoded.getClaim("roles"));
        return new SimpleAuthorizationInfo(roles);
    }
}

实践建议

  1. JWT配置要点

    • 使用HS256或RS256算法确保签名安全
    • 设置合理的过期时间(建议2-4小时)
    • 将用户基础信息和必要权限放入claims
  2. Token刷新机制

图1

2. 配置中心集成:动态权限规则加载

实现方案

微服务环境下,权限规则需要支持动态更新而无需重启服务:

@RefreshScope
public class DynamicFilterChainManager {
    @Autowired
    private ConfigService configService;
    
    @Scheduled(fixedDelay = 30000)
    public void refreshFilterChain() {
        Map<String, String> newRules = configService.loadFilterRules();
        DefaultFilterChainManager manager = getFilterChainManager();
        
        manager.getFilterChains().clear();
        newRules.forEach((url, filterChain) -> 
            manager.createChain(url, filterChain));
    }
}

配置中心对接示例

配置中心类型集成方式监听机制
Nacos@NacosValue注解+监听NamespaceConfigService.listener
Apollo@ApolloConfig+ConfigChangeListener长轮询机制
ConsulConsul KV API + WatchHTTP长轮询

最佳实践

  1. 权限规则采用版本号控制,支持快速回滚
  2. 变更时先灰度发布到测试环境验证
  3. 结合Spring Cloud Bus实现集群级通知

3. 服务间鉴权:内部API的签名验证

签名验证流程

图2

实现代码示例

public class ServiceAuthFilter extends AccessControlFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest request, 
            ServletResponse response, Object mappedValue) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        // 1. 获取必要参数
        String serviceId = httpRequest.getHeader("X-Service-Id");
        String timestamp = httpRequest.getHeader("X-Timestamp");
        String signature = httpRequest.getHeader("X-Signature");
        
        // 2. 时效性验证(防止重放攻击)
        if (System.currentTimeMillis() - Long.parseLong(timestamp) > 300000) {
            return false;
        }
        
        // 3. 重构签名并比对
        String secret = getSecretByServiceId(serviceId);
        String expectedSig = HmacUtils.hmacSha256Hex(secret, 
            serviceId + timestamp + httpRequest.getQueryString());
        
        return expectedSig.equals(signature);
    }
}

关键优化点

  1. 使用非对称加密提升安全性(如RSA替换HMAC)
  2. 为每个服务分配独立密钥,定期轮换
  3. 结合服务网格(如Istio)实现双向TLS

4. Kubernetes适配:Pod身份认证场景

ServiceAccount集成方案

当Shiro运行在K8s Pod中时,可以利用ServiceAccount实现自动认证:

# shiro-config.yml
securityManager:
  realms:
    - type: com.example.K8sServiceAccountRealm
  authenticator:
    authenticationStrategy: atLeastOneSuccessful
public class K8sServiceAccountRealm extends AuthorizingRealm {
    private final String namespace = System.getenv("NAMESPACE");
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        K8sToken k8sToken = (K8sToken) token;
        
        // 调用K8s API验证Token
        ApiClient client = Config.defaultClient();
        AuthenticationV1Api api = new AuthenticationV1Api(client);
        try {
            TokenReview review = new TokenReview()
                .spec(new TokenReviewSpec().token(k8sToken.getCredentials()));
            TokenReview status = api.createTokenReview(review, null);
            
            if (status.getStatus().getAuthenticated()) {
                return new SimpleAuthenticationInfo(
                    status.getStatus().getUser().getUsername(),
                    k8sToken.getCredentials(),
                    getName());
            }
        } catch (ApiException e) {
            throw new AuthenticationException("K8s验证失败", e);
        }
        return null;
    }
}

典型云原生场景

  1. Init Container授权

图3

  1. 跨命名空间访问控制

    // 基于K8s RBAC的权限控制
    @RequiresPermissions("namespace:dev:read")
    public List<Pod> getPods() {
        return k8sClient.pods().inNamespace("dev").list();
    }

生产建议

  1. 为不同环境(dev/test/prod)配置独立的ClusterRole
  2. 使用Vault动态管理敏感凭证
  3. 通过Admission Controller限制Pod的权限范围

总结对比

传统方案云原生适配方案优势对比
Session会话JWT无状态令牌消除服务端状态存储,便于水平扩展
静态权限配置配置中心动态加载实现秒级生效的权限规则更新
IP白名单服务间签名验证细粒度的服务身份认证
固定凭证K8s ServiceAccount自动化的凭证管理与轮换

通过以上改造,Shiro可以完美融入云原生技术栈,在保持API兼容性的同时获得微服务架构所需的弹性与可观测性能力。

评论已关闭