Spring Boot OAuth2:实现指定微服务无认证访问受保护资源
嘿,这个需求在Spring Boot微服务的权限配置里挺常见的,我给你几个不同场景下的解决方案,你可以根据自己的部署环境和安全要求来选:
解决方案:让auth-service无需认证访问db-service的受保护接口
假设你的db-service已经集成了Spring Security来保护接口,核心思路就是在db-service的Security配置中,为auth-service的请求单独添加放行规则,同时保证其他请求依然处于受保护状态。
方法一:基于请求来源放行(适合固定环境)
如果auth-service和db-service部署在固定环境里(比如同一内网、K8s集群),可以直接针对auth-service的IP或者自定义服务标识来放行请求,既安全又精准:
- 在db-service的
SecurityFilterChain配置类中添加规则:
@Configuration @EnableWebSecurity public class DbServiceSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 方式1:根据auth-service的固定IP放行(比如本地测试的127.0.0.1,或者内网IP) .requestMatchers("/api/users/**").hasIpAddress("127.0.0.1") // 方式2:根据自定义请求头标识放行(推荐微服务集群使用) .requestMatchers("/api/users/**").hasHeader("X-Service-Name", "auth-service") // 其他所有请求必须认证 .anyRequest().authenticated() ) // 保留你原本的认证方式,比如JWT、Basic Auth等 .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); } }
- auth-service调用时记得带上对应的请求头(如果用header方式):
@Service public class DbServiceClient { private final RestTemplate restTemplate; public DbServiceClient(RestTemplate restTemplate) { this.restTemplate = restTemplate; } public User getUserByUsername(String username) { HttpHeaders headers = new HttpHeaders(); headers.set("X-Service-Name", "auth-service"); HttpEntity<Void> entity = new HttpEntity<>(headers); return restTemplate.exchange( "http://db-service:8002/api/users/{username}", HttpMethod.GET, entity, User.class, username ).getBody(); } }
方法二:直接放行特定接口(适合完全信任的测试环境)
如果你的环境是完全封闭、内部完全信任的,也可以直接放行db-service中那个查询用户的接口,不需要验证来源:
@Configuration @EnableWebSecurity public class DbServiceSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 直接放行查询用户的接口 .requestMatchers("/api/users/{username}").permitAll() // 其他接口依然受保护 .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); } }
⚠️ 敲黑板:这种方式要谨慎用!只要知道接口地址的客户端都能访问,只适合内部测试或者完全封闭的环境。
方法三:服务间认证(生产环境最佳实践)
如果是生产环境,更推荐用OAuth2客户端凭证模式做服务间认证,既保证安全性,又能实现可控的服务间访问:
- 在db-service的Security配置中,开启资源服务器并指定权限范围:
@Configuration @EnableWebSecurity public class DbServiceSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 只允许带有internal权限的请求访问该接口 .requestMatchers("/api/users/**").hasAuthority("SCOPE_internal") .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); } }
- 在auth-service中配置OAuth2客户端,获取访问db-service的凭证:
# auth-service的application.yml spring: security: oauth2: client: registration: db-service-client: client-id: auth-service-client client-secret: your-secure-secret authorization-grant-type: client_credentials scope: internal provider: db-service-provider: token-uri: http://your-auth-center:8001/oauth2/token # 填统一认证中心的token地址,或者db-service自身的token端点
- auth-service调用时自动携带认证token:
@Service public class DbServiceClient { private final OAuth2AuthorizedClientService authorizedClientService; private final RestTemplate restTemplate; public DbServiceClient(OAuth2AuthorizedClientService authorizedClientService, RestTemplate restTemplate) { this.authorizedClientService = authorizedClientService; this.restTemplate = restTemplate; } public User getUserByUsername(String username) { OAuth2AuthorizedClient client = authorizedClientService.loadAuthorizedClient( "db-service-client", "client-credentials" ); HttpHeaders headers = new HttpHeaders(); headers.setBearerAuth(client.getAccessToken().getTokenValue()); HttpEntity<Void> entity = new HttpEntity<>(headers); return restTemplate.exchange( "http://db-service:8002/api/users/{username}", HttpMethod.GET, entity, User.class, username ).getBody(); } }
这种方式是生产环境的标准做法,既能防止未授权访问,又能清晰管控服务间的权限。
内容的提问来源于stack exchange,提问作者t.piwowarczyk
相关产品推荐
相关产品推荐

