Spring Cloud Security OAuth2(五)
- 開篇描述
- 父工程
-
- 建立UAA授權服務工程
- 建立Order資源服務工程
- 授權伺服器配置
-
- EnableAuthorizationServer
- 配置用戶端詳細資訊ClientDetailsServiceConfigurer
- 管理令牌
- 令牌通路端點配置
-
- 配置授權類型(Grant Types)
- 配置授權端點的URL(Endpoint URLs)
- 令牌端點的安全限制
- web安全配置
- 授權模式
-
- 授權碼模式
-
- 測試
- 簡化模式
-
- 測試
- 密碼模式
-
- 測試
- 用戶端模式
-
- 測試
- 資源服務測試
-
- 資源伺服器配置
- 驗證token
- 編寫資源
- 添加安全通路控制
- 測試
開篇描述
Spring-Security-OAuth2
是對OAuth2的一種實作,與Spring Cloud體系的內建也非常便利,使用它可以實作分布式認證授權解決方案。
OAuth2.0
的服務提供方涵蓋兩個服務,即 授權服務 (Authorization Server,也叫認證服務) 和 資源服務 (Resource Server) ,使用 Spring Security OAuth2 的時候你可以選擇把它們在同一個應用程式中實作,也可以選擇建立使用同一個授權服務的多個資源服務
授權服務 (Authorization Server) 應包含對接入端以及登入使用者的合法性進行驗證并頒發
token
等功能,對令牌的請求端點由 Spring MVC 控制器進行實作,下面是配置一個認證服務必須要實作的endpoints:
- AuthorizationEndpoint 服務于認證請求。預設 URL:
。/oauth/authorize
- TokenEndpoint 服務于通路令牌的請求。預設 URL:
。/oauth/token
資源服務 (Resource Server) ,應包含對資源的保護功能,對非法請求進行攔截,對請求中token進行解析鑒權等,下面的過濾器用于實作 OAuth 2.0 資源服務:
- OAuth2AuthenticationProcessingFilter用來對請求給出的身份令牌解析鑒權。
模拟建立uaa授權服務(也可叫認證服務)和order訂單資源服務。
認證流程如下:
- 用戶端請求UAA授權服務進行認證
- 認證通過後由UAA頒發令牌(token)
- 用戶端攜帶令牌token請求資源服務
- 資源服務校驗令牌的合法性,合法即傳回資源資訊。
父工程
項目結構:
建立maven工程作為父工程,依賴如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.zysheep</groupId>
<artifactId>distributed-security</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>distributed-security-uaa</module>
<module>distributed-security-order</module>
</modules>
<packaging>pom</packaging>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.1.3.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
建立UAA授權服務工程
建立
distributed-security-uaa
作為授權服務工程,包含對接入端以及登入使用者的合法性進行驗證并頒發
token
等功能。依賴如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>distributed-security</artifactId>
<groupId>cn.zysheep</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>distributed-security-uaa</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
建立Order資源服務工程
建立
distributed-security-order
作為資源服務工程,通路本工程的資源需要認證通過。依賴如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>distributed-security</artifactId>
<groupId>cn.zysheep</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>distributed-security-order</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
授權伺服器配置
EnableAuthorizationServer
可以用
@EnableAuthorizationServer
注解并繼承
AuthorizationServerConfigurerAdapter
來配置OAuth2.0 授權伺服器。
在Config包下建立AuthorizationServer:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
//................
}
AuthorizationServerConfigurerAdapter
要求配置以下幾個類,這幾個類是由Spring建立的獨立的配置對象,它們會被Spring傳入
AuthorizationServerConfigurer
中進行配置。
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {}
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {}
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {}
}
- ClientDetailsServiceConfigurer:用來配置用戶端詳情服務(ClientDetailsService),用戶端詳情資訊在這裡進行初始化,你能夠把用戶端詳情資訊寫死在這裡或者是通過資料庫來存儲調取詳情資訊。
- AuthorizationServerEndpointsConfigurer:用來配置令牌(token)的通路端點和令牌服務(token services)。
- AuthorizationServerSecurityConfigurer:用來配置令牌端點的安全限制
配置用戶端詳細資訊ClientDetailsServiceConfigurer
ClientDetailsServiceConfigurer 能夠使用記憶體或者JDBC來實作用戶端詳情服務(ClientDetailsService), ClientDetailsService負責查找ClientDetails,而ClientDetails有幾個重要的屬性如下清單:
- clientId:(必須的)用來辨別客戶的Id。
- secret:(需要值得信任的用戶端)用戶端安全碼,如果有的話。
- scope:用來限制用戶端的通路範圍,如果為空(預設)的話,那麼用戶端擁有全部的通路範圍。
- authorizedGrantTypes:此用戶端可以使用的授權類型,預設為空。
- authorities:此用戶端可以使用的權限(基于Spring Security authorities)。
用戶端詳情(Client Details)能夠在應用程式運作的時候進行更新,可以通過通路底層的存儲服務(例如将用戶端詳情存儲在一個關系資料庫的表中,就可以使用JdbcClientDetailsService)或者通過自己實作 ClientRegistrationService接口(同時你也可以實作 ClientDetailsService 接口)來進行管理。
暫時使用記憶體方式存儲用戶端詳情資訊,配置如下:
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory() //使用in‐memory存儲
.withClient("c1") // client_id
.secret(new BCryptPasswordEncoder().encode("secret"))
.resourceIds("res1")
.authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token")
// 該client允許的授權類型 authorization_code,password,refresh_token,implicit,client_credentials
.scopes("all")// 允許的授權範圍
.autoApprove(false)
//加上驗證回調位址
.redirectUris("http://www.baidu.com");
}
管理令牌
AuthorizationServerTokenServices 接口定義了一些操作使得你可以對令牌進行一些必要的管理,令牌可以被用來加載身份資訊,裡面包含了這個令牌的相關權限。
自己可以建立 AuthorizationServerTokenServices 這個接口的實作,則需要繼承DefaultTokenServices 這個類, 裡面包含了一些有用實作,你可以使用它來修改令牌的格式和令牌的存儲。預設的,當它嘗試建立一個令牌的時候,是使用随機值來進行填充的,除了持久化令牌是委托一個 TokenStore接口來實作以外,這個類幾乎幫你做了所有的事情。并且 TokenStore 這個接口有一個預設的實作,它就是 InMemoryTokenStore ,如其命名,所有的令牌是被儲存在了記憶體中。除了使用這個類以外,你還可以使用一些其他的預定義實作,下面有幾個版本,它們都實作了TokenStore接口:
- InMemoryTokenStore:這個版本的實作是被預設采用的,它可以完美的工作在單伺服器上(即通路并發量 壓力不大的情況下,并且它在失敗的時候不會進行備份),大多數的項目都可以使用這個版本的實作來進行 嘗試,你可以在開發的時候使用它來進行管理,因為不會被儲存到磁盤中,是以更易于調試。
- JdbcTokenStore:這是一個基于JDBC的實作版本,令牌會被儲存進關系型資料庫。使用這個版本的實作時, 你可以在不同的伺服器之間共享令牌資訊,使用這個版本的時候請注意把"spring-jdbc"這個依賴加入到你的 classpath當中。
- JwtTokenStore:這個版本的全稱是 JSON Web Token(JWT),它可以把令牌相關的資料進行編碼(是以對于後端服務來說,它不需要進行存儲,這将是一個重大優勢),但是它有一個缺點,那就是撤銷一個已經授 權令牌将會非常困難,是以它通常用來處理一個生命周期較短的令牌以及撤銷重新整理令牌(refresh_token)。 另外一個缺點就是這個令牌占用的空間會比較大,如果你加入了比較多使用者憑證資訊。JwtTokenStore 不會儲存任何資料,但是它在轉換令牌值以及授權資訊方面與 DefaultTokenServices 所扮演的角色是一樣的。
1.定義TokenConfig
在config包下定義TokenConfig,我們暫時先使用InMemoryTokenStore,生成一個普通的令牌。
@Configuration
public class TokenConfig {
@Bean public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
2.定義AuthorizationServerTokenServices
在AuthorizationServer中定義AuthorizationServerTokenServices
@Autowired
private TokenStore tokenStore;
@Autowired
private ClientDetailsService clientDetailsService;
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service=new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
service.setSupportRefreshToken(true);
service.setTokenStore(tokenStore);
service.setAccessTokenValiditySeconds(7200); // 令牌預設有效期2小時
service.setRefreshTokenValiditySeconds(259200); // 重新整理令牌預設有效期3天
return service;
}
令牌通路端點配置
AuthorizationServerEndpointsConfigurer
這個對象的執行個體可以完成令牌服務以及令牌endpoint配置
配置授權類型(Grant Types)
AuthorizationServerEndpointsConfigurer 通過設定以下屬性決定支援的授權類型(Grant Types):
- authenticationManager:認證管理器,當你選擇了資源所有者密碼(password)授權類型的時候,請設定這個屬性注入一個 AuthenticationManager 對象。
- userDetailsService:如果你設定了這個屬性的話,那說明你有一個自己的 UserDetailsService 接口的實作, 或者你可以把這個東西設定到全局域上面去(例如 GlobalAuthenticationManagerConfigurer 這個配置對 象),當你設定了這個之後,那麼 “refresh_token” 即重新整理令牌授權類型模式的流程中就會包含一個檢查,用來確定這個賬号是否仍然有效,假如說你禁用了這個賬戶的話。
- authorizationCodeServices:這個屬性是用來設定授權碼服務的(即 AuthorizationCodeServices 的執行個體對 象),主要用于 “authorization_code” 授權碼類型模式
- implicitGrantService:這個屬性用于設定隐式授權模式,用來管理隐式授權模式的狀态。
- tokenGranter:當你設定了這個東西(即 TokenGranter 接口實作),那麼授權将會交由你來完全掌控,并且會忽略掉上面的這幾個屬性,這個屬性一般是用作拓展用途的,即标準的四種授權模式已經滿足不了你的 需求的時候,才會考慮使用這個。
配置授權端點的URL(Endpoint URLs)
AuthorizationServerEndpointsConfigurer 這個配置對象有一個叫做 pathMapping() 的方法用來配置端點URL鍊 接,它有兩個參數:
- 第一個參數:String 類型的,這個端點URL的預設連結。
- 第二個參數:String 類型的,你要進行替代的URL連結。
以上的參數都将以 “/” 字元為開始的字元串,架構的預設URL連結如下清單,可以作為這個 pathMapping() 方法的 第一個參數:
- /oauth/authorize:授權端點。
- /oauth/token:令牌端點。
- /oauth/confirm_access:使用者确認授權送出端點。
- /oauth/error:授權服務錯誤資訊端點。
- /oauth/check_token:用于資源服務通路的令牌解析端點
- /oauth/token_key:提供公有密匙的端點,如果你使用JWT令牌的話。
需要注意的是授權端點這個URL應該被Spring Security保護起來隻供授權使用者通路
在AuthorizationServer配置令牌通路端點
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.authorizationCodeServices(authorizationCodeServices)
.tokenServices(tokenServices())
.allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET);
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
//設定授權碼模式的授權碼如何 存取,暫時采用記憶體方式
return new InMemoryAuthorizationCodeServices();
}
令牌端點的安全限制
AuthorizationServerSecurityConfigurer
:用來配置令牌端點(Token Endpoint)的安全限制,在AuthorizationServer中配置如下
//用來配置令牌端點的安全限制
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()") // /oauth/token_key 安全配置
.checkTokenAccess("permitAll()") // /oauth/check_token 安全配置
.allowFormAuthenticationForClients(); // 允許表單認證
}
web安全配置
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// 使用記憶體身份資訊,不接入資料庫,接入資料庫和(四)一樣
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager detailsManager = new InMemoryUserDetailsManager();
detailsManager.createUser(User.withUsername("zhangsan").password(new BCryptPasswordEncoder().encode("123")).authorities("save","update").build());
detailsManager.createUser(User.withUsername("lisi").password(new BCryptPasswordEncoder().encode("456")).authorities("save").build());
return detailsManager;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//安全攔截機制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
授權模式
- authorization_code:授權碼模式
- password: 密碼模式
- client_credentials: 用戶端模式
授權碼模式
1.資源擁有者打開用戶端,用戶端要求資源擁有者給予授權,它将浏覽器被重定向到授權伺服器,重定向時會附加用戶端的身份資訊。 如:
/uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com
- client_id:用戶端準入辨別。
- response_type:授權碼模式固定為code。
- scope:用戶端權限。
- redirect_uri:跳轉uri,當授權碼申請成功後會跳轉到此位址,并在後邊帶上code參數(授權碼)。
2.浏覽器出現向授權伺服器授權頁面,之後将使用者同意授權。
3.授權伺服器将授權碼(AuthorizationCode)轉經浏覽器發送給client(通過redirect_uri)。
4.用戶端拿着授權碼向授權伺服器索要通路access_token,請求如下:
/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=5PgfcD&redirect_uri=http://w ww.baidu.com
- client_id:用戶端準入辨別。
- client_secret:用戶端秘鑰。
- grant_type:授權類型,填寫authorization_code,表示授權碼模式
- code:授權碼,就是剛剛擷取的授權碼,注意:授權碼隻使用一次就無效了,需要重新申請
- redirect_uri:申請授權碼時的跳轉url,一定和申請授權碼時用的redirect_uri一緻。
5.授權伺服器傳回令牌(access_token)
這種模式是四種模式中最安全的一種模式。一般用于client是Web伺服器端應用或第三方的原生App調用資源服務 的時候。因為在這種模式中access_token不會經過浏覽器或移動端的App,而是直接從服務端去交換,這樣就最大限度的減小了令牌洩漏的風險。
測試
浏覽器通路認證頁面:
http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com
然後輸入模拟的賬号和密碼點登陸之後進入授權頁面:
确認授權後,浏覽器會重定向到指定路徑(oauth_client_details表中的web_server_redirect_uri)并附加驗證碼? code=5GeNSr(每次不一樣),
最後使用該驗證碼擷取token。
post http://localhost:53020/uaa/oauth/token
簡化模式
1.資源擁有者打開用戶端,用戶端要求資源擁有者給予授權,它将浏覽器被重定向到授權伺服器,重定向時會 附加用戶端的身份資訊。如:
/uaa/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com
參數描述同授權碼模式 ,注意response_type=token,說明是簡化模式。
2.浏覽器出現向授權伺服器授權頁面,之後将使用者同意授權。
3.授權伺服器将授權碼将令牌(access_token)以Hash的形式存放在重定向uri的fargment中發送給浏覽器。
一般來說,簡化模式用于沒有伺服器端的第三方單頁面應用,因為沒有伺服器端就無法接收授權碼。
測試
浏覽器通路認證頁面:
http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com
然後輸入模拟的賬号和密碼點登陸之後進入授權頁面
确認授權後,浏覽器會重定向到指定路徑(oauth_client_details表中的web_server_redirect_uri)并以Hash的形式存放在重定向uri的fargment中,如:
密碼模式
1.資源擁有者将使用者名、密碼發送給用戶端
2.用戶端拿着資源擁有者的使用者名、密碼向授權伺服器請求令牌(access_token),請求如下
/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=zhangsan&password=123
- client_id:用戶端準入辨別
- client_secret:用戶端秘鑰
- grant_type:授權類型,填寫password表示密碼模式
- username:資源擁有者使用者名
- password:資源擁有者密碼
3.授權伺服器将令牌(access_token)發送給client
這種模式十分簡單,但是卻意味着直接将使用者敏感資訊洩漏給了client,是以這就說明這種模式隻能用于client是我 們自己開發的情況下。是以密碼模式一般用于我們自己開發的,第一方原生App或第一方單頁面應用
測試
POST http://localhost:53020/uaa/oauth/token
用戶端模式
1.用戶端向授權伺服器發送自己的身份資訊,并請求令牌(access_token)
2.确認用戶端身份無誤後,将令牌(access_token)發送給client,請求如下
/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials
- client_id:用戶端準入辨別。
- client_secret:用戶端秘鑰。
- grant_type:授權類型,填寫client_credentials表示用戶端模式
這種模式是最友善但最不安全的模式。是以這就要求我們對client完全的信任,而client本身也是安全的。因 此這種模式一般用來提供給我們完全信任的伺服器端服務。比如,合作方系統對接,拉取一組使用者資訊。
測試
POST http://localhost:53020/uaa/oauth/token
資源服務測試
資源伺服器配置
@EnableResourceServer 注解到一個 @Configuration 配置類上,并且必須使用 ResourceServerConfigurer 這個 配置對象來進行配置(可以選擇繼承自 ResourceServerConfigurerAdapter 然後覆寫其中的方法,參數就是這個對象的執行個體),下面是一些可以配置的屬性:
ResourceServerSecurityConfigurer中主要包括:
- tokenServices:ResourceServerTokenServices 類的執行個體,用來實作令牌服務。
- tokenStore:TokenStore類的執行個體,指定令牌如何通路,與tokenServices配置可選
- resourceId:這個資源服務的ID,這個屬性是可選的,但是推薦設定并在授權服務中進行驗證
HttpSecurity配置這個與Spring Security類似:
- 請求比對器,用來設定需要進行保護的資源路徑,預設的情況下是保護資源服務的全部路徑。
- 通過http.authorizeRequests()來設定受保護資源的通路規則
@EnableResourceServer
注解自動增加了一個類型為 OAuth2AuthenticationProcessingFilter 的過濾器鍊
在order資源服務中編寫ResouceServerConfig:
@Configuration @EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
public static final String RESOURCE_ID = "res1";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).tokenServices(tokenService()).stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**")
.access("#oauth2.hasScope('all')")
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
驗證token
ResourceServerTokenServices 是組成授權服務的另一半,如果你的授權服務和資源服務在同一個應用程式上的 話,你可以使用 DefaultTokenServices ,這樣的話,你就不用考慮關于實作所有必要的接口的一緻性問題。如果你的資源伺服器是分離開的,那麼你就必須要確定能夠有比對授權服務提供的 ResourceServerTokenServices,它知道如何對令牌進行解碼。
令牌解析方法:
使用 DefaultTokenServices 在資源伺服器本地配置令牌存儲、解碼、解析方式
使用 RemoteTokenServices 資源伺服器通過 HTTP 請求來解碼令牌,每次都請求授權伺服器端點 /oauth/check_token
使用授權服務的 /oauth/check_token 端點你需要在授權服務将這個端點暴露出去,以便資源服務可以進行通路, 這在咱們授權服務配置中已經提到了,下面是一個例子,在這個例子中,我們在授權服務中配置了 /oauth/check_token 和 /oauth/token_key 這兩個端點:
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")// /oauth/token_key 安全配置
.checkTokenAccess("permitAll()") // /oauth/check_token 安全配置
}
在資源 服務配置RemoteTokenServices ,在ResouceServerConfig中配置:
public ResourceServerTokenServices tokenService() {
//使用遠端服務請求授權伺服器校驗token,必須指定校驗token 的url、client_id,client_secret
RemoteTokenServices service = new RemoteTokenServices();
service.setCheckTokenEndpointUrl("http://localhost:53020/uaa/oauth/check_token");
service.setClientId("c1");
service.setClientSecret("secret");
return service;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).tokenServices(tokenService()).stateless(true);
/**
* tokenServices:ResourceServerTokenServices 類的執行個體,用來實作令牌服務。
* tokenStore:TokenStore類的執行個體,指定令牌如何通路,與tokenServices配置可選
* resourceId:這個資源服務的ID,這個屬性是可選的,但是推薦設定并在授權服務中進行驗證
*/
}
編寫資源
在controller包下編寫LoginController,此controller表示訂單資源的通路類
@Controller
public class LoginController {
@GetMapping("/login-success")
@ResponseBody
public String loginSuccess() {
return "login-success";
}
@GetMapping("/user/save")
@ResponseBody
@PreAuthorize("hasAuthority('save')") // 擁有save權限才可以通路
public String saveUser() {
return "save resources";
}
@GetMapping("/user/update")
@ResponseBody
@PreAuthorize("hasAuthority('update')") // 擁有update權限才可以通路
public String updateUser() {
return "update resources";
}
@GetMapping("/user/delete")
@ResponseBody // 可匿名通路,即不需要認證,就可以通路
public String deleteUser() {
return "delete resources";
}
@GetMapping("/user/getUsername")
@ResponseBody
@PreAuthorize("isAuthenticated()") // 需要認證(登入)才可以通路
public String getUsername() {
return "getUsername resources";
}
@GetMapping("/user/get")
@ResponseBody
@PreAuthorize("hasAuthority('get')") // 擁有get權限才可以通路
public String getUser() {
return "get resources";
}
@GetMapping("/user/getUserList")
@ResponseBody
@PreAuthorize("hasAuthority('save') and hasAuthority('update')") // 擁有save和update權限才可以通路
public String getUserList() {
return "getUserList resources";
}
}
添加安全通路控制
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// .antMatchers("/**").access("#oauth2.hasScope('all')")
.antMatchers("/user/**").authenticated()
.anyRequest().permitAll()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
測試
1.申請令牌
使用用戶端模式申請令牌
2.請求資源
按照
oauth2.0
協定要求,請求資源需要在請求頭中攜帶
token
,如下:
token的參數名稱為:
Authorization
,值為:Bearer token值
如果token錯誤,則授權失敗,如下
通路沒有權限的資源
3.校驗token檢視使用者資訊
POST http://localhost:53020/uaa/oauth/check_token