1. 概述
Spring Cloud Security模塊提供與Spring Boot應用程序中基於令牌的身份驗證相關的功能。
具體來説,它簡化了基於OAuth2的單點登錄(SSO)——包括支持在資源服務器之間傳遞令牌,以及使用嵌入式Zuul代理配置下游身份驗證。
在本文中,我們將探討如何使用Spring Boot客户端應用程序、授權服務器和作為資源服務器工作的REST API來配置這些功能。
請注意,對於此示例,我們只有一個客户端應用程序使用SSO來演示雲安全功能——但在典型的場景中,我們至少需要兩個客户端應用程序來證明單點登錄的需求。
2. 快速啓動雲安全應用
讓我們從配置 Spring Boot 應用中的 SSO 開始。
首先,我們需要添加 spring-cloud-starter-oauth2 依賴項:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>這還將引入 spring-cloud-starter-security 依賴項。
我們可以將任何社交網站配置為我們的網站的認證服務器,或者使用我們自己的服務器。 在我們的情況下,我們選擇了後者,並配置了一個作為授權服務器的應用程序——該應用程序部署在本地,地址為 http://localhost:7070/authserver。
我們的授權服務器使用 JWT 令牌。
此外,為了使任何客户端能夠檢索用户的憑據,我們需要配置我們的資源服務器,該服務器運行在 9000 端口,並配置一個可以提供這些憑據的端點。
在這裏,我們配置了一個名為 user 的端點,該端點位於 http://localhost:9000/user。
有關如何設置授權服務器和資源服務器的更多詳細信息,請參閲我們之前的文章此處。
現在,我們可以將該註解添加到客户端應用程序的配置類中:
@Configuration
public class SiteSecurityConfigurer {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.oauth2Login();
// ...
}
}所有需要身份驗證的請求將被重定向到授權服務器。 為了確保其正常工作,我們還需要定義服務器屬性:
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請注意,我們需要在類路徑中包含 spring-boot-starter-security,才能使上述配置生效。
3. 傳遞訪問令牌
在傳遞令牌時,OAuth2 客户端會將由其接收到的 OAuth2 令牌轉發到發出的資源請求。
Spring Security 暴露了一個 OAuth2AuthorizedClientService,這對於創建 RestTemplate 攔截器很有用。基於此,我們可以為我們的客户端應用程序創建自己的 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();
}一旦我們配置了 Bean ,上下文將會將訪問令牌轉發到請求的服務,並且如果令牌過期,也會刷新令牌。
4. 使用 RestTemplate 發送 OAuth 令牌
我們之前在客户端應用程序中定義了一個名為 restOperations 的 Bean,類型為 RestTemplate。因此,我們可以使用 RestTemplate 的 getForObject() 方法,將包含必要的令牌的請求發送到受保護的資源服務器。
首先,讓我們在資源服務器中定義一個需要身份驗證的端點:
@GetMapping("/person")
@PreAuthorize("hasAnyRole('ADMIN', 'USER')")
public @ResponseBody Person personInfo(){
return new Person("abir", "Dhaka", "Bangladesh", 29, "Male");
}
這是一個簡單的 REST 端點,它返回一個 Person 對象的 JSON 表示形式。
現在,我們可以使用 getForObject() 方法從客户端應用程序發送請求,該方法會將令牌轉發到資源服務器:
@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 用於令牌轉發
如果我們希望將令牌轉發到下游的代理服務,可以使用 Spring Cloud Zuul 內嵌反向代理。
首先,我們需要添加 Maven 依賴項,以便與 Zuul 配合工作:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>接下來,我們需要在客户端應用程序的配置類上添加 @EnableZuulProxy 註解:
@EnableZuulProxy
@Configuration
public class SiteSecurityConfigurer {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.oauth2Login();
// ...
}
}所有剩下的工作就是將 Zuul 配置屬性添加到我們的 application.yml 文件中:
zuul:
sensitiveHeaders: Cookie,Set-Cookie
routes:
resource:
path: /api/**
url: http://localhost:9000
user:
path: /user/**
url: http://localhost:9000/user客户端應用程序的 /api</em lang="en"> 端點接收到的任何請求都將被重定向到資源服務器的 URL。 此外,還需要提供用户憑據端點的 URL。
6. 結論
在本文中,我們探討了如何使用 Spring Cloud Security 與 OAuth2 和 Zuul 結合,配置安全授權和資源服務器,以及如何使用 RestTemplate 和嵌入式 Zuul 代理在服務器之間傳遞 OAuth2 令牌。