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: authserverpassword
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 令牌
我們之前已經定義了 Client 應用程序中類型的 restOperations 豆。因此,我們可以使用 RestTemplate 的 getForObject() 方法,將必要的令牌發送到受保護的資源服務器。
首先,讓我們定義資源服務器中需要身份驗證的端點:
@GetMapping("/person")
@PreAuthorize("hasAnyRole('ADMIN', 'USER')")
public @ResponseBody Person personInfo(){
return new Person("abir", "Dhaka", "Bangladesh", 29, "Male");
}
這是一個簡單的 REST 端點,返回 Person 對象的 JSON 表示形式。
現在,我們可以使用 Client 應用程序中的 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 內嵌反向代理。
首先,我們需要添加用於與 Zuul 協作的 Maven 依賴項:
<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 端點的請求都將被重定向到 Resource Server URL。 我們還需要提供用户憑據端點的 URL。
6. 結論
在本文中,我們探討了如何使用 Spring Cloud Security 與 OAuth2 和 Zuul 結合,配置安全授權和資源服務器,以及如何使用 RestTemplate 在服務器之間傳遞 OAuth2 令牌。