1、概述
Spring Cloud 的安全模塊可以為Spring Boot應用提供基於令牌的安全特性。具體講就是支持OAuth2協議來實現單點登錄(SSO),可以很方便地在資源服務之間傳遞身份令牌,以及使用嵌入式的ZUUL代理來配置下游服務的認證。
在這篇文章中,我們將介紹如何在Spring Boot 客戶端應用、身份認證服務與提供REST API的資源服務之間配置完成這些功能。實現系統的安全訪問控制、身份認證和單點登錄。
在這個例子中,我們使用了2個客戶端應用程序來演示雲安全特性中的SSO,只不過這2個應用是一樣的。
2、創建雲安全應用
首先在所有Spring Boot應用中配置SSO,需要增加spring-cloud-starter-oauth2依賴:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> <version>1.2.2.RELEASE</version> </dependency>
它會自動包含spring-cloud-starter-security依賴。
2.1配置一個應用作為身份認證服務
認證服務部署為http://localhost:7070/authserver。認證服務使用JWT令牌。由於有多個客戶端來連接Spring OAuth2 Auth Server,需要在配置類里
為inMemory生成器定義多個withClients。
@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("first")
.secret("passwordforauthserver") .redirectUris("http://localhost:8080/")
.authorizedGrantTypes("authorization_code", "refresh_token") .scopes("myscope")
.autoApprove(true)
.accessTokenValiditySeconds(30)
.refreshTokenValiditySeconds(1800) .and() .withClient("second")
.secret("passwordforauthserver") .redirectUris("http://localhost:8081/")
.authorizedGrantTypes("authorization_code", "refresh_token") .scopes("myscope")
.autoApprove(true)
.accessTokenValiditySeconds(30)
.refreshTokenValiditySeconds(1800); }
2.2 配置一個資源服務
代表受保護的服務。服務端口為9000。
提供REST服務接口供客戶端調用。
@RestController public class PersonInfoController { @GetMapping("/person") @PreAuthorize("hasAnyRole('ADMIN', 'USER')") public @ResponseBody Person personInfo() { return new Person("peter", "Beijing", "China", 29, "Male"); } }
同時提供一個/usr接口供客戶端來獲得用戶的憑證。
@RestController public class ResourceController { @RequestMapping("/user") public Principal user(Principal user) { return user; } }
2.3 客戶端應用,在配置類里增加安全注解:
@Configuration @EnableOAuth2Sso public class SiteSecurityConfigurer extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // ... } }
任何需要身份驗證的請求都將被重定向到授權服務器。為此,我們還必須定義服務器屬性:
security: oauth2: client: accessTokenUri: http://localhost:7070/authserver/oauth/token userAuthorizationUri: http://localhost:7070/authserver/oauth/authorize clientId: first clientSecret: passwordforauthserver resource: userInfoUri: http://localhost:9000/user
3、傳遞令牌
當傳遞令牌時,OAuth2客戶端將其接收到的OAuth2令牌轉發給資源服務。由於我們已經聲明了@EnableOauth2Sso注解,Spring Boot 會在請求上下文中添加一個OAuth2ClientContext
對象,因此我們可以在客戶端應用程序中創建自己的OAuth2RestTemplate。
@Bean public OAuth2RestOperations restOperations( OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) { return new OAuth2RestTemplate(resource, context); }
一旦我們配置了這個Bean,上下文就會將訪問令牌轉發給所請求的服務,並且如果令牌到期了也將會刷新令牌。
4、使用RestTemplate傳遞OAuth令牌
之前我們在客戶端應用程序中定義了一個OAuth2RestTemplate 類型的restOperations bean。因此可以使用OAuth2RestTemplate中的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依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency>
然后在配置類里添加@EnableZuulProxy 注解
@Configuration @EnableOAuth2Sso @EnableZuulProxy public class SiteSecurityConfigurer extends WebSecurityConfigurerAdapter { //... }
同時在application.yml 文件里添加Zuul的配置項
zuul: routes: resource: path: /api/** url: http://localhost:9000 user: path: /user/** url: http://localhost:9000/user
這樣任何訪問客戶端應用/api端點的請求被重定向到資源服務的URL。這些請求都會帶着OAuth令牌。
6、運行效果
直接訪問受保護的資源服務,顯示未認證
訪問客戶端http://localhost:8080/personInfo,重定向到身份認證頁面
輸入用戶名、密碼(user/user),顯示資源服務數據
通過Zuul代理訪問資源服務
7、總結
在這篇文章里,我們討論了如何使用Spring Cloud Security的OAuth2和Zuul來配置安全的認證服務和資源服務,以及使用Oauth2RestTemplate和嵌入的Zuul代理在服務之間傳遞OAuth2
令牌。