Java面試必會-微服務許可權認證
阿新 • • 發佈:2020-11-20
## 微服務身份認證方案
**1. 單點登入(SSO)**
這種方案意味著每個面向使用者的服務都必須與認證服務互動,這會產生大量非常瑣碎的網路流量和重複的工作,當動輒數十個微應用時,這種方案的弊端會更加明顯。
**2. 分散式 Session 方案**
分散式會話方案原理主要是將關於使用者認證的資訊儲存在共享儲存中,且通常由使用者會話作為 key 來實現的簡單分散式雜湊對映。當用戶訪問微服務時,使用者資料可以從共享儲存中獲取。在某些場景下,這種方案很不錯,使用者登入狀態是不透明的。同時也是一個高可用且可擴充套件的解決方案。這種方案的缺點在於共享儲存需要一定保護機制,因此需要通過安全連結來訪問,這時解決方案的實現就通常具有相當高的複雜性了。
**3. 客戶端 Token 方案**
令牌在客戶端生成,由身份驗證服務進行簽名,並且必須包含足夠的資訊,以便可以在所有微服務中建立使用者身份。令牌會附加到每個請求上,為微服務提供使用者身份驗證,這種解決方案的安全性相對較好,但身份驗證登出是一個大問題,緩解這種情況的方法可以使用短期令牌和頻繁檢查認證服務等。對於客戶端令牌的編碼方案,Borsos 更喜歡使用 JSON Web Tokens(JWT),它足夠簡單且庫支援程度也比較好。
**4. 客戶端 Token 與 API 閘道器結合**
這個方案意味著所有請求都通過閘道器,從而有效地隱藏了微服務。 在請求時,閘道器將原始使用者令牌轉換為內部會話 ID 令牌。在這種情況下,登出就不是問題,因為閘道器可以在登出時撤銷使用者的令牌。
## 微服務常見安全認證方案
https://www.infoq.cn/article/identity-authentication-of-architecture-in-micro-service
### HTTP 基本認證
HTTP Basic Authentication(HTTP 基本認證)是 HTTP 1.0 提出的一種認證機制。HTTP 基本認證的過程如下:
1. 客戶端傳送 HTTP Request 給伺服器。
2. 因為 Request 中沒有包含 Authorization header,伺服器會返回一個 401 Unauthozied 給客戶端,並且在 Response 的 Header “WWW-Authenticate” 中新增資訊。
3. 客戶端把使用者名稱和密碼用 BASE64 加密後,放在 Authorization Header 中傳送給伺服器, 認證成功。
4. 伺服器將 Authorization Header 中的使用者名稱密碼取出,進行驗證, 如果驗證通過,將根據請求,傳送資源給客戶端。
### 基於 Session 的認證
基於 Session 的認證應該是最常用的一種認證機制了。使用者登入認證成功後,將使用者相關資料儲存到 Session 中,單體應用架構中,預設 Session 會儲存在應用伺服器中,並且將 Session ID 返回到客戶端,儲存在瀏覽器的 Cookie 中。
但是在分散式架構下,Session 存放於某個具體的應用伺服器中自然就無法滿足使用了,簡單的可以通過 Session 複製或者 Session 粘制的方案來解決。
Session 複製依賴於應用伺服器,需要應用伺服器有 Session 複製能力,不過現在大部分應用伺服器如 Tomcat、JBoss、WebSphere 等都已經提供了這個能力。
除此之外,Session 複製的一大缺陷在於當節點數比較多時,大量的 Session 資料複製會佔用較多網路資源。Session 粘滯是通過負載均衡器,將統一使用者的請求都分發到固定的伺服器節點上,這樣就保證了對某一使用者而言,Session 資料始終是正確的。不過這種方案依賴於負載均衡器,並且只能滿足水平擴充套件的叢集場景,無法滿足應用分割後的分散式場景。而且對於大量使用者的系統而言,tomcat需要更大的空間來存放session,浪費空間。
1、session複製
2.可以讓客戶端儲存session,使用者儲存自己的session的資訊到cookie中,節省服務端資源。但cookie可能會暴露重要資訊,每個瀏覽器對於cookie的長度也不同
3、還可以使用hash一致性解決,對ip進行hash並且負載均衡,使用者一直訪問同一個伺服器,即同一個session,只需修改nginx配置
4、統一儲存:所有session存到資料庫或者快取等
```java
// session共享:session放大作用域,使得所有服務都可以公用其中一個服務生成的session
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("gulimall.com");
cookieSerializer.setCookieName("GULISESSION");
return cookieSerializer;
}
```
在微服務架構下,每個微服務拆分的粒度會很細,並且不只有使用者和微服務打交道,更多還有微服務間的呼叫。這個時候上述兩個方案都無法滿足,就要求必須要將 Session 從應用伺服器中剝離出來,存放在外部進行集中管理。可以是資料庫,也可以是分散式快取,如 Memchached、Redis 等。這正是 David Borsos 建議的第二種方案,分散式 Session 方案。
![image-20201022210636899](C:\Users\qq285\AppData\Roaming\Typora\typora-user-images\image-20201022210636899.png)
### 基於 Token 的認證
隨著 Restful API、微服務的興起,基於 Token 的認證現在已經越來越普遍。Token 和 Session ID 不同,並非只是一個 key。Token 一般會包含使用者的相關資訊,通過驗證 Token 就可以完成身份校驗。像 Twitter、微信、QQ、GitHub 等公有服務的 API 都是基於這種方式進行認證的,一些開發框架如 OpenStack、Kubernetes 內部 API 呼叫也是基於 Token 的認證。基於 Token 認證的一個典型流程如下:
![image-20201113210509670](C:\Users\qq285\AppData\Roaming\Typora\typora-user-images\image-20201113210509670.png)
![img](C:\Users\qq285\AppData\Roaming\Typora\typora-user-images\image-20201022211338243.png)
1. 使用者輸入登入資訊(或者呼叫 Token 介面,傳入使用者資訊),傳送到身份認證服務進行認證(身份認證服務可以和服務端在一起,也可以分離,看微服務拆分情況了)。
2. 身份驗證服務驗證登入資訊是否正確,返回介面(一般介面中會包含使用者基礎資訊、許可權範圍、有效時間等資訊),客戶端儲存介面,可以儲存在 Session 或者資料庫中。
3. 使用者將 Token 放在 HTTP 請求頭中,發起相關 API 呼叫。
4. 被呼叫的微服務,驗證 Token 許可權。
5. 服務端返回相關資源和資料。
基於 Token 認證的好處如下:
1. 服務端無狀態:Token 機制在服務端不需要儲存 session 資訊,因為 Token 自身包含了所有使用者的相關資訊。
2. 效能較好,因為在驗證 Token 時不用再去訪問資料庫或者遠端服務進行許可權校驗,自然可以提升不少效能。
3. 支援移動裝置。
4. 支援跨程式呼叫,Cookie 是不允許垮域訪問的,而 Token 則不存在這個問題。
下面會重點介紹兩種基於 Token 的認證方案 JWT/Oauth2.0。
## JWT
JSON Web Token 是一種緊湊的,URL 安全的方式,表示要在雙方之間傳輸的宣告。JWT 一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源,也可以增加一些額外的其它業務邏輯所必須的宣告資訊,該 Token 也可直接被用於認證,也可被加密。
#### JWT 認證流程
1. 客戶端呼叫登入介面(或者獲取 token 介面),傳入使用者名稱密碼。
2. 服務端請求身份認證中心,確認使用者名稱密碼正確。
3. 服務端建立 JWT,返回給客戶端。
4. 客戶端拿到 JWT,進行儲存(可以儲存在快取中,也可以儲存在資料庫中,如果是瀏覽器,可以儲存在 Cookie 中)在後續請求中,在 HTTP 請求頭中加上 JWT。
5. 服務端校驗 JWT,校驗通過後,返回相關資源和資料。
### JWT 結構
JWT 是由三段資訊構成的,第一段為頭部(Header),第二段為載荷(Payload),第三段為簽名(Signature)。每一段內容都是一個 JSON 物件,將每一段 JSON 物件採用 BASE64 編碼,將編碼後的內容用. 連結一起就構成了 JWT 字串:header.payload.signature
**1. 頭部(Header)**
頭部用於描述關於該 JWT 的最基本的資訊,例如其型別以及簽名所用的演算法等。這也可以被表示成一個 JSON 物件。
```
{
"typ": "JWT",
"alg": "HS256" // 在頭部指明瞭簽名演算法是 HS256 演算法。
}
```
**2. 載荷(payload)**
載荷就是存放有效資訊的地方。有效資訊包含三個部分:
- 標準中註冊的宣告
- 公共的宣告
- 私有的宣告
##### 標準中註冊的宣告(建議但不強制使用):
- iss:JWT 簽發者
- sub:JWT 所面向的使用者
- aud:接收 JWT 的一方
- exp:JWT 的過期時間,這個過期時間必須要大於簽發時間
- nbf:定義在什麼時間之前,該 JWT 都是不可用的
- iat:JWT 的簽發時間
- jti:JWT 的唯一身份標識,主要用來作為一次性 token, 從而回避重放攻擊。
##### 公共的宣告 :
公共的宣告可以新增任何的資訊,一般新增**使用者的相關資訊**或其他業務需要的必要資訊. 但不建議新增敏感資訊,因為該部分在客戶端可解密。
##### 私有的宣告 :
私有宣告是提供者和消費者所共同定義的宣告,一般不建議存放敏感資訊,因為 base64 是**對稱解密**的,意味著該部分資訊可以歸類為明文資訊。
```js
{
"iss": "Online JWT Builder",
"iat": 1416797419,
"exp": 1448333419,
"aud": "www.primeton.com",
"sub": "[email protected]",
"GivenName": "dragon",
"Surname": "wang",
"admin": true
}
```
**3. 簽名(signature)**
建立簽名需要使用 Base64 編碼後的 header 和 payload 以及一個祕鑰。將 base64 加密後的 header 和 base64 加密後的 payload 使用. 連線組成的字串,通過 header 中宣告的加密方式進行加鹽 secret 組合加密,然後就構成了 jwt 的第三部分。
比如:`HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)`
JWT 的優點:
1. 跨語言,JSON 的格式保證了跨語言的支撐
2. 基於 Token,無狀態
3. 佔用位元組小,便於傳輸
### Token登出
Token 的登出,由於 Token 不儲存在服務端,由客戶端儲存,當用戶登出時,Token 的有效時間還沒有到,還是有效的。所以如何在使用者登出登入時讓 Token 登出是一個要關注的點。一般有如下幾種方式:
1. Token 儲存在 Cookie 中,這樣客戶端登出時,自然可以清空掉
2. 登出時,將 Token 存放到分散式快取中,每次校驗 Token 時區檢查下該 Token 是否已登出。不過這樣也就失去了快速校驗 Token 的優點。
3. 多采用短期令牌,比如令牌有效期是 20 分鐘,這樣可以一定程度上降低登出後 Token 可用性的風險。
## OAuth 2.0
![image-20201108150928054](C:\Users\qq285\AppData\Roaming\Typora\typora-user-images\image-20201108150928054.png)
(A)使用者開啟客戶端以後,客戶端要求使用者給予授權。
(B)使用者同意給予客戶端授權。
(C)客戶端使用上一步獲得的授權,向認證伺服器(如微信登入的認證伺服器)申請令牌。
(D)認證伺服器對客戶端進行認證以後,確認無誤,同意發放令牌。
(E)客戶端使用令牌,向資源伺服器申請獲取資源。
(F)資源伺服器確認令牌無誤,同意向客戶端開放資源。
#### 四大角色
1. 客戶端:客戶端是代表資源所有者對資源伺服器發出訪問受保護資源請求的應用程式。
2. 資源擁有者:資源擁有者是對資源具有授權能力的人。
3. 資源伺服器:資源所在的伺服器。
4. 授權伺服器:為客戶端應用程式提供不同的 Token,可以和資源伺服器在統一伺服器上,也可以獨立出去。
### 客戶端的授權模式
客戶端必須得到使用者的授權(Authorization Grant),才能獲得令牌(access token)。OAuth 2.0 定義了四種授權方式:authorizationcode、implicit、resource owner password credentials、client credentials。
**1. 授權碼模式(authorization code)**
授權碼模式(authorization code)是功能最完整、流程最嚴密的授權模式。它的特點就是通過客戶端的後臺伺服器,與"服務提供商"的認證伺服器進行互動。流程如下:
1. 使用者訪問客戶端,後者將前者導向認證伺服器。
2. 使用者選擇是否給予客戶端授權。
3. 假設使用者給予授權,認證伺服器將使用者導向客戶端事先指定的"重定向 URI"(redirection URI),同時附上一個授權碼。
4. 客戶端收到授權碼,附上早先的"重定向 URI",向認證伺服器申請令牌。這一步是在客戶端的後臺的伺服器上完成的,對使用者不可見。
5. 認證伺服器核對了授權碼和重定向 URI,確認無誤後,向客戶端傳送訪問令牌(access token)和更新令牌(refresh token)。
**2. 簡化模式(implicit)**
簡化模式(Implicit Grant Type)不通過第三方應用程式的伺服器,直接在瀏覽器中向認證伺服器申請令牌,跳過了"授權碼"這個步驟,因此得名。所有步驟在瀏覽器中完成,令牌對訪問者是可見的,且客戶端不需要認證。流程如下:
1. 客戶端將使用者導向認證伺服器。
2. 使用者決定是否給於客戶端授權。
3. 假設使用者給予授權,認證伺服器將使用者導向客戶端指定的"重定向 URI",並在 URI 的 Hash 部分包含了訪問令牌。
4. 瀏覽器向資源伺服器發出請求,其中不包括上一步收到的 Hash 值。
5. 資源伺服器返回一個網頁,其中包含的程式碼可以獲取 Hash 值中的令牌。
6. 瀏覽器執行上一步獲得的指令碼,提取出令牌。
7. 瀏覽器將令牌發給客戶端。
**3. 密碼模式(Resource Owner Password Credentials)**
密碼模式中,使用者向客戶端提供自己的使用者名稱和密碼。客戶端使用這些資訊,向"服務商提供商"索要授權。在這種模式中,使用者必須把自己的密碼給客戶端,但是客戶端不得儲存密碼。這通常用在使用者對客戶端高度信任的情況下,比如客戶端是作業系統的一部分,或者由一個著名公司出品。而認證伺服器只有在其他授權模式無法執行的情況下,才能考慮使用這種模式。流程如下:
1. 使用者向客戶端提供使用者名稱和密碼。
2. 客戶端將使用者名稱和密碼發給認證伺服器,向後者請求令牌。
3. 認證伺服器確認無誤後,向客戶端提供訪問令牌。
**4. 客戶端模式(Client Credentials)**
客戶端模式(Client Credentials Grant)指客戶端以自己的名義,而不是以使用者的名義,向"服務提供商"進行認證。嚴格地說,客戶端模式並不屬於 OAuth 框架所要解決的問題。
在這種模式中,使用者直接向客戶端註冊,客戶端以自己的名義要求"服務提供商"提供服務,其實不存在授權問題。流程如下:
1. 客戶端向認證伺服器進行身份認證,並要求一個訪問令牌。
2. 認證伺服器確認無誤後,向客戶端提供訪問令牌。
### 社交登入微博
![image-20201108153354375](C:\Users\qq285\AppData\Roaming\Typora\typora-user-images\image-20201108153354375.png)
## 單點登入
###### 即一處登入處處登入
在專案根目錄下本地jar打包:**mvn clean package -Dmaven.skip.test=true**
本地編譯:java -jar xxx.jar –server.port=8080
### Session複製單點登入流程
1、當請求頁面時,當前頁面被攔截,會判斷當前頁面的session是否含有指定的token
2、如果session包含指定token,表明登入還未過期,token儲存了使用者登入資訊,接著繼續請求當前頁面
3、如果session沒有指定token,會重定向到登入頁面,並請求後臺登入介面
4、接著登入服務會判斷當前頁面是否含有指定的cookie
5、有cookie則表明已登入過,cookie還未過期,再返回到之前的請求頁面
6、若沒有指定cookie表明還未登入或登入已經過期
7、使用者在登入頁面登入後會儲存cookie,並重定向到之前的請求頁面,會建立自己的session儲存當前登入狀態,且在url帶上cookie值作為token的值
### cookie+redis單點登入
### JWT單點登入SSO
#### 使用xxl-sso框架實現單