SpringCloudSecurity微服务安全

wen java案例 2

本文目录导读:

SpringCloudSecurity微服务安全

  1. 最主流的微服务安全架构模式
  2. 具体技术实现(Spring Cloud 主流技术栈)
  3. 微服务安全的挑战与最佳实践

Spring Cloud Security 主要用来解决微服务架构下的安全认证和授权问题,在微服务中,服务是分散的、无状态的,传统的基于 Session 的认证方式(如 Spring Security+HttpSession)不再适用。

核心难点在于:

  1. 用户状态不共享:服务 A 认证了,服务 B 不知道。
  2. 服务间调用安全:服务 A 调用服务 B,B 如何验证 A 是合法的,而不是黑客伪造的请求?
  3. 统一认证 vs. 独立校验:谁负责颁发“通行证”?谁负责检查“通行证”?

Spring Cloud Security 通常与 Spring Cloud GatewaySpring Security OAuth2(以及后续的 Spring Authorization Server)结合,构建出一套完整的解决方案。


最主流的微服务安全架构模式

目前业界最成熟、最常用的模式是:JWT + OAuth2 + 网关统一认证 + 资源服务本地校验

架构角色

  1. 客户端(Client):浏览器、手机 App、第三方应用。
  2. 网关(Gateway):系统的统一入口,负责身份认证(验证用户名密码)和初步校验(JWT Token 合法性),并将解析后的用户信息传递到下游。
  3. 授权服务器(Authorization Server):负责颁发 Token(通行证),登录成功就给一个 JWT。
  4. 资源服务器(Resource Server):业务微服务(订单服务、用户服务),不负责认证,只负责校验 Token 的权限。

认证流程(标准 OAuth2 密码模式/客户端模式)

sequenceDiagram
    participant 客户端
    participant 网关 as 网关(Gateway)
    participant 授权服务 as 授权服务器
    participant 订单服务 as 订单服务(资源)
    客户端->>+网关: 1. 发送登录请求(用户名, 密码)
    网关->>+授权服务: 2. 转发认证请求到 Authorization Server
    授权服务-->>-网关: 3. 验证成功,返回 JWT Token
    网关-->>-客户端: 4. 返回 JWT Token
    客户端->>+网关: 5. 发送业务请求(Header: Bearer JWT)
    网关->>网关: 6. Token 合法性校验(签名、过期时间)
    网关->>+订单服务: 7. 转发请求(携带解析出的用户信息, 如 userId)
    订单服务->>订单服务: 8. 资源服务本地校验(角色、权限)
    订单服务-->>-网关: 9. 返回业务数据
    网关-->>-客户端: 10. 返回数据

具体技术实现(Spring Cloud 主流技术栈)

引入核心依赖

pom.xml 中,核心依赖通常包括:

<!-- Spring Boot 与 Security (核心) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- OAuth2 资源服务器 (用于校验 JWT Token) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<!-- (可选) 如果你自己搭建授权服务器 -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
</dependency>

授权服务器配置 (Authorization Server)

负责颁发 JWT Token,通常使用 @EnableAuthorizationServer(Spring Security OAuth2 旧版)或 Spring Authorization Server(新版,Spring Security 5.7+ 推荐)。

核心配置(简化版):

@Configuration
@EnableAuthorizationServer // 旧版注解,新版需用 NimbusJwkMatcher 等
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
               .withClient("my-client")           // 客户端 ID
               .secret("{noop}my-secret")         // 客户端秘钥
               .authorizedGrantTypes("password", "client_credentials", "refresh_token")
               .scopes("read", "write")
               .accessTokenValiditySeconds(3600); // Token 过期时间
    }
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
            .authenticationManager(authenticationManager) // 注入 AuthenticationManager
            .accessTokenConverter(jwtAccessTokenConverter()); // 配置 JWT 转换器
    }
    // 配置 JWT 签名密钥(通常用非对称密钥 RSA,这里为演示用对称密钥)
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("my-secret-key-which-is-very-long"); // 正式环境请用 RSA
        return converter;
    }
}

网关安全配置 (Gateway + Security)

网关负责拦截所有入站请求,进行 Token 校验,并将用户信息传递给下游。

@Configuration
@EnableWebFluxSecurity // 注意:Gateway 是 WebFlux,用这个注解
public class GatewaySecurityConfig {
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange(exchanges -> exchanges
                .pathMatchers("/auth/**", "/login/**", "/public/**").permitAll() // 白名单
                .anyExchange().authenticated() // 其他所有请求都需要认证
            )
            .oauth2ResourceServer(OAuth2ResourceServerSpec::jwt); // 开启 JWT 校验
            // .csrf().disable(); // 微服务架构通常禁用 CSRF
        return http.build();
    }
}

传递用户信息到下游:

网关在转发请求给微服务时,需要将解析出的用户信息(如用户ID、角色)放到 Header 中。

@Component
public class UserInfoFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 从 SecurityContext 获取 Authentication 对象
        return ReactiveSecurityContextHolder.getContext()
            .map(SecurityContext::getAuthentication)
            .flatMap(auth -> {
                if (auth instanceof JwtAuthenticationToken) {
                    JwtAuthenticationToken jwtAuth = (JwtAuthenticationToken) auth;
                    Jwt jwt = jwtAuth.getToken();
                    String userId = jwt.getClaimAsString("user_id");
                    // 将用户信息放入请求头,转发给下游微服务
                    ServerHttpRequest request = exchange.getRequest().mutate()
                        .header("X-User-Id", userId)
                        .header("X-User-Roles", auth.getAuthorities().toString())
                        .build();
                    return chain.filter(exchange.mutate().request(request).build());
                }
                return chain.filter(exchange);
            });
    }
}

微服务(资源服务器)配置

每个微服务只需要校验 Token 的合法性,并基于用户信息做权限校验。

@Configuration
@EnableWebSecurity // 注意:普通微服务是 Servlet,用这个注解
public class ResourceServerConfig {
    @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
    private String jwkSetUri;
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                // 示例:只有 ADMIN 角色才能访问 /admin/**
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt
                    .jwtAuthenticationConverter(jwtAuthenticationConverter())
                )
            );
        return http.build();
    }
    // 自定义 JWT 解析,从 Token 中提取角色
    private JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter grantedAuthorities = new JwtGrantedAuthoritiesConverter();
        grantedAuthorities.setAuthorityPrefix("ROLE_");    // 设置角色前缀
        grantedAuthorities.setAuthoritiesClaimName("roles"); // JWT 中存放角色的 Claim 名称
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(grantedAuthorities);
        return converter;
    }
}

服务间调用安全 (内部调用)

当服务 A 调用服务 B 时,也需要携带 Token,可以通过 RestTemplateWebClient 拦截器实现。

// 使用 RestTemplate 调用另一个微服务
@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}
// 在调用逻辑中:从当前上下文获取 Token,并放入 Header
public void callServiceB() {
    // 获取当前用户的 Token
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String token = ((JwtAuthenticationToken) authentication).getToken().getTokenValue();
    HttpHeaders headers = new HttpHeaders();
    headers.setBearerAuth(token); // 设置 Bearer Token
    HttpEntity<String> entity = new HttpEntity<>(headers);
    restTemplate.exchange("http://service-b/api/data", HttpMethod.GET, entity, String.class);
}

微服务安全的挑战与最佳实践

挑战 解决方案
Token 如何不用每次都查数据库验证? 使用 JWT,JWT 自带签名,网关和服务端只需要验证签名即可,无需查库做 RPC 调用。
如何高效管理密钥? 使用 非对称密钥 (RSA),授权服务器签发 Token(持有私钥),所有服务通过公钥验签(可以放在授权服务器的 /actuator/jwk-set 端点,配置 jwks-url),避免每次更新密钥都要重启所有服务。
如何避免重复解密 Token 影响性能? 只在网关层做一次完整的 JWT 校验,下游微服务不解析 JWT,而是信任网关传递的 X-User-* Header,但这种方式有安全风险(内网可能被攻击)。推荐:每个服务自行解析 JWT(性能损耗很小,安全级别更高)。
如何支持第三方登录? 使用 OAuth2 授权码模式,先跳转到第三方(如微信、GitHub),拿到授权码,再换取 Token。
如何实现 API 级权限? 结合 @PreAuthorize@PostAuthorize@Secured 注解,在方法级别进行细粒度控制。

Spring Cloud Security 在微服务中的最佳实践就是 JWT + OAuth2 + 网关统一认证 + 资源服务本地校验

  • 授权服务器:专门负责签发票据(JWT)。
  • 网关:守卫入口,负责转发和初步校验(白名单、Token 结构)。
  • 资源服务器(微服务):只校验签名和权限,无状态,易扩展。

这种架构保证了前后端分离、服务无状态、易于水平扩展,是构建生产级微服务安全体系的基石。

抱歉,评论功能暂时关闭!