1. 概述
Spring Cloud Security 模块为 Spring Boot 应用提供了基于令牌的安全特性。它主要解决两个核心问题:
✅ 简化基于 OAuth2 的单点登录(SSO)实现
✅ 支持在资源服务器间传递令牌
✅ 通过嵌入式 Zuul 代理配置下游认证
本文将通过一个 Spring Boot 客户端应用、一个授权服务器和一个作为资源服务器的 REST API,演示如何配置这些特性。注意:为简化演示,我们只使用一个客户端应用——实际生产环境中至少需要两个客户端才能体现 SSO 的价值。
2. 快速搭建云安全应用
2.1 添加依赖
首先在客户端应用中添加核心依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
该依赖会自动引入
spring-cloud-starter-security
2.2 配置授权服务器
我们选择自建授权服务器(部署在 http://localhost:7070/authserver
),使用 JWT 令牌。同时需要配置资源服务器(端口 9000)提供用户信息接口:
spring:
security:
oauth2:
client:
registration:
baeldung:
client-id: authserver
client-secret: passwordforauthserver
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
baeldung:
token-uri: http://localhost:7070/authserver/oauth/token
authorization-uri: http://localhost:7070/authserver/oauth/authorize
user-info-uri: http://localhost:9000/user
2.3 启用 OAuth2 登录
在配置类中添加安全配置:
@Configuration
public class SiteSecurityConfigurer {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.oauth2Login();
// ...
}
}
⚠️ 所有需要认证的请求将自动重定向到授权服务器。确保 spring-boot-starter-security
已在类路径中。
3. 传递访问令牌
当客户端需要调用下游服务时,需要将 OAuth2 令牌转发出去。Spring Security 提供了 OAuth2AuthorizedClientService
,我们可以基于它创建 RestTemplate
拦截器:
@Bean
public RestOperations restTemplate(OAuth2AuthorizedClientService clientService) {
return new RestTemplateBuilder().interceptors((ClientHttpRequestInterceptor)
(httpRequest, bytes, execution) -> {
OAuth2AuthenticationToken token =
OAuth2AuthenticationToken.class.cast(SecurityContextHolder.getContext()
.getAuthentication());
OAuth2AuthorizedClient client =
clientService.loadAuthorizedClient(token.getAuthorizedClientRegistrationId(),
token.getName());
httpRequest.getHeaders()
.add(HttpHeaders.AUTHORIZATION, "Bearer " + client.getAccessToken()
.getTokenValue());
return execution.execute(httpRequest, bytes);
}).build();
}
✅ 配置后,请求会自动携带令牌,并在令牌过期时自动刷新。
4. 使用 RestTemplate 传递 OAuth 令牌
4.1 资源服务器接口
在资源服务器中定义受保护接口:
@GetMapping("/person")
@PreAuthorize("hasAnyRole('ADMIN', 'USER')")
public @ResponseBody Person personInfo(){
return new Person("张三", "北京", "中国", 29, "男");
}
4.2 客户端调用
在客户端应用中通过 RestTemplate
调用:
@Autowired
private RestOperations restOperations;
@GetMapping("/personInfo")
public ModelAndView person() {
ModelAndView mav = new ModelAndView("personinfo");
String personResourceUrl = "http://localhost:9000/person";
mav.addObject("person",
restOperations.getForObject(personResourceUrl, String.class));
return mav;
}
✅ 令牌会自动通过拦截器添加到请求头中。
5. 配置 Zuul 令牌中继
5.1 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
5.2 启用 Zuul 代理
在配置类添加注解:
@EnableZuulProxy
@Configuration
public class SiteSecurityConfigurer {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.oauth2Login();
// ...
}
}
5.3 路由配置
在 application.yml
中添加:
zuul:
sensitiveHeaders: Cookie,Set-Cookie
routes:
resource:
path: /api/**
url: http://localhost:9000
user:
path: /user/**
url: http://localhost:9000/user
✅ 所有发往 /api
的请求会被代理到资源服务器,同时自动传递令牌。用户信息接口 /user
也需要单独配置路由。