Spring Cloud入門教程系列:
- Spring Cloud入門教程(一):服務(wù)治理(Eureka)
- Spring Cloud入門教程(二):客戶端負(fù)載均衡(Ribbon)
- Spring Cloud入門教程(三):聲明式服務(wù)調(diào)用(Feign)
- Spring Cloud入門教程(四):微服務(wù)容錯(cuò)保護(hù)(Hystrix)
- Spring Cloud入門教程(五):API服務(wù)網(wǎng)關(guān)(Zuul) 上
- Spring Cloud入門教程(六):API服務(wù)網(wǎng)關(guān)(Zuul) 下
- Spring Cloud入門教程(七):分布式鏈路跟蹤(Sleuth)
- Spring Cloud入門教程(八):統(tǒng)一配置中心(Config)
- Spring Cloud入門教程(九):基于消息驅(qū)動(dòng)開發(fā)(Stream)
- Spring Cloud入門教程(十):消息總線(Bus)
本人和同事撰寫的《Spring Cloud微服務(wù)架構(gòu)開發(fā)實(shí)戰(zhàn)》一書也在京東、當(dāng)當(dāng)?shù)葧晟霞埽蠹铱梢渣c(diǎn)擊這里前往購買,多謝大家支持和捧場(chǎng)!
安全,幾乎在任何應(yīng)用開發(fā)中都是繞不過去的一個(gè)基礎(chǔ)功能。當(dāng)我們將應(yīng)用轉(zhuǎn)移到微服務(wù)架構(gòu)時(shí),安全將會(huì)更加復(fù)雜。在2016年David Borsos在倫敦的微服務(wù)大會(huì)上提出了以下四種方案:
單點(diǎn)登錄(SSO): 每個(gè)微服務(wù)都需要和認(rèn)證服務(wù)交互,但這將產(chǎn)生大量非常瑣碎的網(wǎng)絡(luò)流量和重復(fù)的工作,當(dāng)動(dòng)在應(yīng)用中存在數(shù)十個(gè)或更多微服務(wù)時(shí),該方案的弊端就非常明顯;
分布式會(huì)話(Session)方案: 該方案將用戶認(rèn)證的信息存儲(chǔ)在共享存儲(chǔ)中(如:Redis),并使用用戶會(huì)話的ID作為key來實(shí)現(xiàn)的簡(jiǎn)單分布式哈希映射。當(dāng)用戶訪問微服務(wù)時(shí),可以通過會(huì)話的ID從從共享存儲(chǔ)中獲取用戶認(rèn)證信息。該方案在大部分時(shí)候非常不錯(cuò),但其主要缺點(diǎn)在于共享存儲(chǔ)需要一定保護(hù)機(jī)制,此時(shí)相應(yīng)的實(shí)現(xiàn)就會(huì)相對(duì)復(fù)雜;
客戶端令牌(Token)方案: 令牌在客戶端生成,并由認(rèn)證服務(wù)器進(jìn)行簽名,令牌中包含足夠的信息,以便各微服務(wù)可以使用。令牌會(huì)附加到每個(gè)請(qǐng)求上,為微服務(wù)提供用戶身份驗(yàn)證。該解決方案的安全性相對(duì)較好,但由于令牌由客戶端生成并保存,因此身份驗(yàn)證注銷非常麻煩,一個(gè)折衷解決方案就是通過短期令牌和頻繁檢查認(rèn)證服務(wù)來驗(yàn)證令牌是否有效等。對(duì)于客戶端令牌JSON Web Tokens(JWT)是一個(gè)非常好的選擇;
客戶端令牌與API網(wǎng)關(guān)結(jié)合: 使用該方案意味著所有請(qǐng)求都通過網(wǎng)關(guān),從而有效地隱藏了微服務(wù)。在請(qǐng)求時(shí),網(wǎng)關(guān)將原始用戶令牌轉(zhuǎn)換為內(nèi)部會(huì)話。這樣也就可以網(wǎng)關(guān)對(duì)令牌進(jìn)行注銷,從而解決上一種方案存在的問題。
在本文中我們將著重介紹基于令牌的解決方案,而基于令牌的解決方案最好的選擇就是OAuth2.0。
1. OAuth2.0
關(guān)于OAuth2.0在維基百科中描述如下:
開放授權(quán)(OAuth)是一個(gè)開放標(biāo)準(zhǔn),允許用戶讓第三方應(yīng)用訪問該用戶在某一網(wǎng)站上存儲(chǔ)的私密的資源(如照片,視頻,聯(lián)系人列表),而無需將用戶名和密碼提供給第三方應(yīng)用。
OAuth允許用戶提供一個(gè)令牌,而不是用戶名和密碼來訪問他們存放在特定服務(wù)提供者的數(shù)據(jù)。每一個(gè)令牌授權(quán)一個(gè)特定的網(wǎng)站(例如,視頻編輯網(wǎng)站)在特定的時(shí)段(例如,接下來的2小時(shí)內(nèi))內(nèi)訪問特定的資源(例如僅僅是某一相冊(cè)中的視頻)。這樣,OAuth讓用戶可以授權(quán)第三方網(wǎng)站訪問他們存儲(chǔ)在另外服務(wù)提供者的某些特定信息,而非所有內(nèi)容。
OAuth 2.0是OAuth協(xié)議的下一版本,但不向下兼容OAuth 1.0。OAuth 2.0關(guān)注客戶端開發(fā)者的簡(jiǎn)易性,同時(shí)為Web應(yīng)用,桌面應(yīng)用和手機(jī),和起居室設(shè)備提供專門的認(rèn)證流程。
對(duì)于讓我們首先了解一下OAuth2.0中的幾個(gè)關(guān)鍵術(shù)語:
- Resource Owner: 資源所有者,我們可以直接理解為:用戶(User);
- User Agent: 用戶代理,對(duì)于Web應(yīng)用可以直接理解為瀏覽器;
- Authorization server: 認(rèn)證服務(wù)器,即提供用戶認(rèn)證和授權(quán)的服務(wù)器,可以是獨(dú)立服務(wù)器;
- Resource server: 資源服務(wù)器,這里我們可以理解為需要保護(hù)的微服務(wù)。
然后,讓我們看一下OAuth2.0的認(rèn)證流程圖(摘自RFC6749):
認(rèn)證流程步驟如下:
- (A)用戶打開客戶端以后,客戶端請(qǐng)求用戶給予授權(quán);
- (B)用戶同意授權(quán)給客戶端;
- (C)客戶端使用上一步獲得的授權(quán),向認(rèn)證服務(wù)器申請(qǐng)令牌;
- (D)認(rèn)證服務(wù)器對(duì)客戶端進(jìn)行認(rèn)證以后,確認(rèn)無誤,同意發(fā)放令牌;
- (E)客戶端使用令牌,向資源服務(wù)器申請(qǐng)獲取資源;
- (F)資源服務(wù)器確認(rèn)令牌無誤,同意向客戶端開放資源。
從流程上可以得知客戶端必須在得到用戶授權(quán)后才能夠從認(rèn)證服務(wù)中獲取到令牌。OAuth2.0針對(duì)客戶端授權(quán)提供了下面四種授權(quán)方式:
- 授權(quán)碼模式(authorization code): 該種模式是功能最完整、流程最嚴(yán)密的授權(quán)模式;
- 簡(jiǎn)化模式(implicit): 該模式不需要通過第三方應(yīng)用程序的服務(wù)器,跳過了"授權(quán)碼"這個(gè)步驟,直接在瀏覽器中向認(rèn)證服務(wù)器申請(qǐng)令牌,因此稱為簡(jiǎn)化模式;
- 密碼模式(password): 用戶向客戶端提供自己的用戶名和密碼,客戶端通過這些信息直接向認(rèn)證服務(wù)器獲取授權(quán);
- 客戶端模式(client credentials): 指客戶端以自己的名義,而不是以用戶的名義向認(rèn)證服務(wù)器獲取認(rèn)證,這種方式下認(rèn)證服務(wù)器會(huì)將客戶端作為一個(gè)用戶來對(duì)待。
2. 實(shí)現(xiàn)微服務(wù)安全
下面讓我們著手來實(shí)現(xiàn)示例項(xiàng)目的安全管控。
2.1 搭建認(rèn)證服務(wù)器
首先,我們會(huì)搭建一個(gè)認(rèn)證服務(wù)器(Auth Server)。該服務(wù)器也是一個(gè)標(biāo)準(zhǔn)的Spring Boot框架應(yīng)用。
2.1.1 編寫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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>twostepsfromjava.cloud</groupId>
<artifactId>twostepsfromjava-cloud-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../parent</relativePath>
</parent>
<artifactId>auth-server</artifactId>
<name>MS Blog Projects(Security): Auth Server</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在該配置文件中我們需要引入spring-cloud-security
和spring-security-oauth2
依賴。
2.1.2 實(shí)現(xiàn)客戶端管理
對(duì)于OAuth2.0應(yīng)用來說需要實(shí)現(xiàn)一個(gè)客戶端認(rèn)證管理,這里我們直接繼承AuthorizationServerConfigurerAdapter
,并通過內(nèi)存管理的方式增加了一個(gè)客戶端應(yīng)用,代碼如下:
package io.twostepsfromjava.cloud.auth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
/**
* OAuth2Server 配置
*
* @author CD826(CD826Dong@gmail.com)
* @since 1.0.0
*/
@Configuration
public class OAuthConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("malldemo")
.secret("pgDBd99tOX8d")
.authorizedGrantTypes("authorization_code", "refresh_token", "implicit", "password", "client_credentials")
.scopes("webmall");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
客戶端ID我們?cè)O(shè)置為malldemo
,secret則設(shè)置為:pgDBd99tOX8d
,同時(shí)我們還為客戶端授權(quán)了authorization_code
, refresh_token
, implicit
, password
, client_credentials
認(rèn)證模式。并且在接下來的示例中我們會(huì)使用授權(quán)碼模式和密碼模式來進(jìn)行測(cè)試。
2.1.3 實(shí)現(xiàn)用戶認(rèn)證和授權(quán)的管理
對(duì)于使用過Spring Security的同學(xué)來說對(duì)Security中用戶認(rèn)證和授權(quán)的管理應(yīng)該不會(huì)陌生,這里也不再細(xì)講,不熟悉的同學(xué)可以自行搜索來了解。示例中代碼如下:
package io.twostepsfromjava.cloud.auth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* OAuth2 安全配置
*
* @author CD826(CD826Dong@gmail.com)
* @since 1.0.0
*/
@Configuration
@Order(org.springframework.boot.autoconfigure.security.SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class OAuthWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
@Override
@Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user001")
.password("pwd001")
.roles("USER")
.and()
.withUser("admin")
.password("pwdAdmin")
.roles("USER", "ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
在上面的代碼中,我們依然通過內(nèi)存方式進(jìn)行管理,并創(chuàng)建了兩個(gè)用戶:
- user001: 是一個(gè)普通用戶,只有
USER
角色; - admin: 是一個(gè)管理員用戶,擁有
USER
和ADMIN
角色。
在上面的代碼中我們還指定了所有訪問都需要認(rèn)真,并且開啟了httpBasic認(rèn)證,通過這個(gè)當(dāng)一個(gè)未認(rèn)證用戶訪問時(shí)就可以通過瀏覽器彈出一個(gè)認(rèn)證對(duì)話框,可以讓用戶輸入用戶名和密碼進(jìn)行認(rèn)證。
2.1.4 實(shí)現(xiàn)應(yīng)用引導(dǎo)類
對(duì)于我們所要實(shí)現(xiàn)的認(rèn)證服務(wù)器來說最重要的就是需要在應(yīng)用引導(dǎo)類中增加@EnableAuthorizationServer
注解,通過該注解就可以啟動(dòng)Spring Cloud Security,并且為我們提供一系列端點(diǎn),從而實(shí)現(xiàn)OAuth2.0的認(rèn)證。這些端點(diǎn)分別為:
-
/oauth/authorize
: 授權(quán)端點(diǎn); -
/oauth/token
: 獲取訪問令牌端點(diǎn); -
/oauth/confirm_access
: 用戶確認(rèn)授權(quán)提交端點(diǎn); -
/oauth/error
: 認(rèn)證服務(wù)器錯(cuò)誤信息獲取端點(diǎn); -
/oauth/check_token
: 用于資源服務(wù)訪問的令牌解析端點(diǎn); -
/oauth/token_key
: 如果使用JWT令牌,則公開用于令牌驗(yàn)證的公鑰。
2.1.5 實(shí)現(xiàn)用戶信息加載端點(diǎn)
對(duì)于認(rèn)證服務(wù)來說我們還需要提供一個(gè)用戶信息加載端點(diǎn),這樣其它微服務(wù)就可以使用令牌從認(rèn)證服務(wù)器獲取認(rèn)證用戶的信息,從而能夠?qū)崿F(xiàn)用戶認(rèn)證及鑒權(quán)處理。具體實(shí)現(xiàn)代碼如下:
package io.twostepsfromjava.cloud.auth.api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* 獲取當(dāng)前認(rèn)證用戶的信息
*
* @author CD826(CD826Dong@gmail.com)
* @since 1.0.0
*/
@RestController
public class AuthEndpoint {
protected Logger logger = LoggerFactory.getLogger(AuthEndpoint.class);
@RequestMapping(value = { "/auth/user" }, produces = "application/json")
public Map<String, Object> user(OAuth2Authentication user) {
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet( user.getUserAuthentication().getAuthorities()));
return userInfo;
}
}
該段代碼將從Spring Security中獲取到當(dāng)前用戶信息,并轉(zhuǎn)化成一個(gè)Map對(duì)象返回。
2.1.6 編寫配置文件
# 定義應(yīng)用端口
server.port=8290
# 定義應(yīng)用名稱
spring.application.name=authserver
logging.level.org.springframework=INFO
logging.level.io.twostepsfromjava=DEBUG
2.2 完善用戶微服務(wù)
接下來我們將對(duì)用戶微服務(wù)進(jìn)行修改,增加安全處理功能。
2.2.1 引入Security依賴
對(duì)于用戶微服務(wù)來說是一個(gè)Resource server,也就是資源服務(wù)器,當(dāng)訪問到某些資源時(shí)需要進(jìn)行用戶認(rèn)證及鑒權(quán),因此需要引入對(duì)Spring Cloud Security的依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
2.2.2 完善應(yīng)用引導(dǎo)類修改
在Spring Cloud Security中我們只需要對(duì)需要進(jìn)行安全管理的應(yīng)用增加@EnableResourceServer
注解來開啟安全管控:
package io.twostepsfromjava.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
/**
* TwoStepsFromJava Cloud -- User Service 服務(wù)器
*
* @author CD826(CD826Dong@gmail.com)
* @since 1.0.0
*/
@EnableDiscoveryClient
@EnableResourceServer
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
當(dāng)引入了spring-cloud-security
并在應(yīng)用引導(dǎo)類中增加了@EnableResourceServer
注解時(shí),應(yīng)用就會(huì)開啟默認(rèn)的安全管控處理,如果你想在自己的應(yīng)用中增加更細(xì)的安全管控,那么需要繼承WebSecurityConfigurerAdapter
并實(shí)現(xiàn)安全相關(guān)配置,這個(gè)在這里就不細(xì)講了。
2.2.3 完善應(yīng)用配置
我們需要在應(yīng)用配置文件中增加獲取認(rèn)證用戶信息的端點(diǎn),具體配置如下:
# 保持原來的配置不變
# OAuth2
security.oauth2.resource.user-info-uri=http://localhost:8290/auth/user
所配置的端點(diǎn)也就是之前我們?cè)谡J(rèn)證服務(wù)器所實(shí)現(xiàn)的獲取當(dāng)前登錄用戶信息的端點(diǎn)。
2.2.4 增加獲取當(dāng)前用戶信息的端點(diǎn)
為了進(jìn)行測(cè)試,我們需要在用戶微服務(wù)中增加一個(gè)獲取當(dāng)前已登錄用戶信息的端點(diǎn),代碼如下:
@RequestMapping(value = "/my", method = RequestMethod.GET)
public UserDto myDetail() {
Map curUser = (Map) SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
String userName = (String)curUser.get("username");
return new UserDto(userName, userName, "/avatar/default.png", "");
}
這里的代碼非常簡(jiǎn)單,就是直接從SecurityContextHolder
中獲取到當(dāng)前已登錄用戶的信息,并構(gòu)造成一個(gè)UserDto
,然后返回。
Ok,到此用戶微服務(wù)的改造就完成了。如果你現(xiàn)在啟動(dòng)用戶微服務(wù)并進(jìn)行訪問上面所提供的端點(diǎn),就會(huì)看到如下返回:
也就是說,現(xiàn)在需要權(quán)限才可以訪問該端點(diǎn)。那么我們?nèi)绾翁峁?quán)限呢?
之前在講OAuth2.0的時(shí)候我們提到客戶端授權(quán)模式有四種,那么我們來看看如何使用這些授權(quán)模式來實(shí)現(xiàn)具體的用戶認(rèn)證處理。
2.3 通過授權(quán)碼模式(authorization code)訪問
首先,讓我們來看看如何通過OAuth2.0所提供的最全面的授權(quán)流程授權(quán)碼模式來實(shí)現(xiàn)用戶認(rèn)證。
我們需要依次啟動(dòng):服務(wù)治理服務(wù)器(Eureak Server)、認(rèn)證服務(wù)器(Auth Server)和用戶微服務(wù)。
第一步,我們首先來構(gòu)造獲取訪問令牌的Url:
http://localhost:8290/oauth/authorize?response_type=code&client_id=malldemo&redirect_uri=http://localhost:8260&scope=webmall&state=63879
對(duì)于該Url說明如下:
-
/oauth/authorize
: 這個(gè)是獲取授權(quán)碼的端點(diǎn); -
client_id
: 客戶端的ID,在請(qǐng)求的Url中必選包含。請(qǐng)注意一下我們這里給的值為:malldemo
,這個(gè)就是我們?cè)谡J(rèn)證服務(wù)器中配置客戶端列表是所配置的; -
response_type
: 表示授權(quán)類型,也是必選的,對(duì)于授權(quán)碼模式,這里固定填寫為:code
; -
scope
: 表示申請(qǐng)的權(quán)限范圍,可以不填。這個(gè)是指當(dāng)有不同的客戶端時(shí)所授權(quán)是否復(fù)用; -
redirect_uri
: 授權(quán)成功后重定向到的URI; -
state
: 表示客戶端的當(dāng)前狀態(tài),可以指定任意值,不論授權(quán)是否成功認(rèn)證服務(wù)器都會(huì)原封不動(dòng)地返回這個(gè)值。
我們,接下來可以直接在瀏覽器中請(qǐng)求這個(gè)地址,然后返回的頁面如下:
這個(gè)一個(gè)瀏覽器自身的用戶登錄窗口。因?yàn)椋覀儧]有登錄過,所以認(rèn)證服務(wù)器通過httpBasic
開啟瀏覽器登錄模式,這樣當(dāng)用戶尚未認(rèn)證時(shí)就會(huì)彈出如上的登錄窗口。
我們?cè)诘卿洿翱谥休斎?code>user001和pwd001
也就是之前認(rèn)證服務(wù)器中所配置的用戶列表中的一個(gè),然后就會(huì)跳轉(zhuǎn)到如下頁面:
這里就是用戶是否授權(quán)的界面。在該界面中我們可以看到所傳入的客戶端應(yīng)用的ID和授權(quán)范圍都會(huì)顯示出來。這里我們點(diǎn)擊【Approve】,然后瀏覽器就會(huì)跳轉(zhuǎn)到redirect_uri
所指定的地址,這時(shí)候仔細(xì)觀察所跳轉(zhuǎn)回來的地址,如下:
http://localhost:8260/?code=tn8n8F&state=63879
可以發(fā)現(xiàn)在地址包含了兩個(gè)參數(shù):
-
code
: 為認(rèn)證服務(wù)所發(fā)放的授權(quán)碼。該碼的有效期很短,默認(rèn)為10分鐘,并且客戶端只能使用該碼一次,否則會(huì)被認(rèn)證服務(wù)器拒絕。還需要說明一點(diǎn)就是該碼與客戶端ID及重定向URI,是一一對(duì)應(yīng)關(guān)系; -
state
: 這個(gè)就是上面我們請(qǐng)求時(shí)所傳入的參數(shù),認(rèn)證服務(wù)器會(huì)原封不動(dòng)的返回來。
Ok,第一步我們已經(jīng)獲取到了授權(quán)碼。那么第二步就是根據(jù)這個(gè)授權(quán)碼來獲取訪問令牌,在獲取訪問令牌時(shí)我們使用Postman來進(jìn)行,界面截圖如下:
這該截圖中我們需要填寫以下參數(shù):
-
/oauth/token
: 這個(gè)是獲取訪問令牌的端點(diǎn); -
grant_type
: 表示使用的授權(quán)模式,必須填寫,對(duì)于授權(quán)碼模式值固定為"authorization_code"; -
code
: 也就是上一步我們所獲得的授權(quán)碼; -
redirect_uri
: 獲取成功后重定向到的URI,同樣我們需要設(shè)置為之前所給的地址; -
client_id
: 客戶端ID,必須填寫,而且需要與之前保持一致。
此外,在上面的截圖中我們還需要填寫授權(quán)認(rèn)證信息,這個(gè)因?yàn)槲覀冋J(rèn)證服務(wù)器開啟了權(quán)限驗(yàn)證,這里填寫客戶端的ID和secret即可。此時(shí)服務(wù)器就會(huì)把客戶端作為一個(gè)用戶來對(duì)待,從而能夠訪問/oauth/token
端點(diǎn)。
P.S. 這里這么做主要時(shí)簡(jiǎn)化測(cè)試方法,使得我們上一步訪問時(shí)可以彈出認(rèn)證對(duì)話框。但實(shí)際生產(chǎn)使用時(shí)不應(yīng)這么做,需要自己定義用戶登錄頁面、授權(quán)頁面。
上面的請(qǐng)求,我們可以獲得如下返回:
返回的內(nèi)容如下:
-
access_token
: 這個(gè)是所獲取訪問令牌; -
token_type
: 令牌類型,Spring Cloud OAuth默認(rèn)返回的值為bearer
; -
expires_in
: 表示令牌過期時(shí)間,單位為秒,默認(rèn)為12小時(shí); -
refresh_token
: 更新令牌,過期后可以用來獲取下一次的訪問令牌; -
scope
: 權(quán)限范圍,一般與客戶端申請(qǐng)范圍一致。
有了訪問令牌下一步我就可以訪問用戶微服務(wù)了,訪問方式如下:
訪問時(shí)我們需要在Header中指定Authorization
,并且將值設(shè)置為上一步所獲取到的訪問令牌即可,這樣我們就可以得到正確的數(shù)據(jù)返回了,如上圖所示。
這樣我們就完成了通過授權(quán)碼模式實(shí)現(xiàn)用戶認(rèn)證的測(cè)試。
2.4 通過密碼模式(password)訪問
如果認(rèn)證服務(wù)器是第三方的,那么使用上面的流程問題不大。如果認(rèn)證服務(wù)器是我們自己搭建的,比如該示例,那么上面的授權(quán)顯有點(diǎn)復(fù)雜。接下來讓我們看看如何使用密碼模式來簡(jiǎn)化。
我們按照下圖方式來直接請(qǐng)求認(rèn)證服務(wù)器獲取訪問令牌:
在上圖中我們需要同時(shí)設(shè)置客戶端的ID、secret及訪問用戶的用戶名和密碼,并將grant_type
設(shè)置為password
,這樣就可以直接獲取到訪問令牌,如下圖所示:
然后,我們根據(jù)所獲取到的訪問令牌再次訪問用戶微服務(wù),可以獲取如下界面:
這說明,用戶認(rèn)證也是成功的。
可見,通過密碼模式可以大大簡(jiǎn)化用戶認(rèn)證流程,但是需要用戶信任客戶端的情況下才會(huì)提供,因此這種方式適合客戶端與認(rèn)證服務(wù)器是同一個(gè)應(yīng)用的情況下。
對(duì)于其它客戶端授權(quán)模式這里就不再一一進(jìn)行測(cè)試了。本文中所提到的示例你可以在這里下載。