Spring Security技術棧學習筆記(十三)Spring Social集成第三方登錄驗證開發流程介紹

開發第三方登錄,我們必須首先要了解OAuth協議(本文所講述的OAuth協議指的是OAuth2協議),本文首先簡單介紹OAuth協議,然後基於Spring Social來闡述開發第三方登錄需要做哪些準備工作。

一、OAuth協議簡介

OAuth協議誕生背景

舉一個場景例子,有一個第三方服務提供方,可以給圖片提供美化的服務,而你的圖片存儲在百度雲盤上,那麼如何做到使第三方服務提供者對你的圖片進行美化呢?傳統的做法是將自己的百度雲盤賬戶和密碼授權給第三方服務提供者,他登錄到百度雲盤以後對你的圖片進行美化,但是這麼做有很多隱患,如下列表所示:

  • 第三方服務提供者爲了後續的持續服務,會保存自己的百度雲盤賬號和密碼信息;
  • 第三方服務提供者獲得了賬戶和密碼,就擁有了和用戶相同的權限,用戶的百度雲盤所有的信息都將暴露給第三方;
  • 第三方服務提供者的服務器一旦被黑客攻破,那麼用戶將面臨信息泄露的危險;
  • 用戶只有通過修改百度雲盤的密碼才能收回對第三方應用的授權,那麼這樣將所有的第三方的授權全部收回,不利於某些服務的正常進行。

OAuth的誕生就是爲了解決上面的問題,它從另一個角度實現了免密授權,大大提高了用戶隱私信息的安全性。開放授權(OAuth)是一個開放標準,允許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源(如照片,視頻,聯繫人列表),而無需將用戶名和密碼提供給第三方應用。OAuth允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據。每一個令牌授權一個特定的網站(例如,視頻編輯網站)在特定的時段(例如,接下來的2小時內)內訪問特定的資源(例如僅僅是某一相冊中的視頻)。這樣,OAuth讓用戶可以授權第三方網站訪問他們存儲在另外服務提供者的某些特定信息,而非所有內容。

OAuth的整體設計思路是在“客戶端”與“服務提供商”之間,設置了一個授權層(authorization layer)。“客戶端”不能直接登錄“服務提供商”,只能登錄授權層,以此將用戶與客戶端區分開來。“客戶端”登錄授權層所用的令牌(token),與用戶的密碼不同。用戶可以在登錄的時候,指定授權層令牌的權限範圍和有效期。“客戶端”登錄授權層以後,“服務提供商”根據令牌的權限範圍和有效期,向“客戶端”開放用戶儲存的部分資料。

OAuth協議中的角色關係

這裏貼出一張OAuth協議的角色關係圖,我們根據圖來描述各個角色之間的關係與執行原理。

  • Third-party application:第三方應用程序,本文中又稱“客戶端”(client),即上一節例子中的第三方服務提供商。
  • HTTP service providerHTTP服務提供商,本文中簡稱“服務提供商”,即上一節例子中的百度雲盤。
  • Resource Owner:資源所有者,本文中又稱“用戶”(user)。
  • User Agent:用戶代理,本文中就是指瀏覽器。
  • Authorization server:認證服務器,即服務提供商專門用來處理認證的服務器。
  • Resource server:資源服務器,即服務提供商存放用戶生成的資源的服務器。它與認證服務器,可以是同一臺服務器,也可以是不同的服務器。

描述了角色關係之後,我們一起來探討一下OAuth協議的執行流程:

  • 用戶打開客戶端以後,客戶端要求用戶給予授權。
  • 用戶同意給予客戶端授權。
  • 客戶端使用上一步獲得的授權,向認證服務器申請令牌。
  • 認證服務器對客戶端進行認證以後,確認無誤,同意發放令牌。
  • 客戶端使用令牌,向資源服務器申請獲取資源。
  • 資源服務器確認令牌無誤,同意向客戶端開放資源。
OAuth授權模式

OAuth2.0協議提供了四種授權模式,它們分別是:

  • 授權碼模式(authorization code
  • 簡化模式(implicit
  • 密碼模式(resource owner password credentials
  • 客戶端模式(client credentials

其中,授權碼模式(authorization code)是功能最完整、流程最嚴密的授權模式。它的特點就是通過客戶端的後臺服務器,與“服務提供商”的認證服務器進行互動。授權碼模式流程圖如下所示:

授權碼模式的基本步驟如下:

  • 用戶訪問客戶端,客戶端將用戶導向認證服務器。
  • 認證服務器向客戶端發送授權詢問請求,用戶選擇是否給予客戶端授權。
  • 假設用戶給予授權,認證服務器將用戶導向客戶端事先指定的“重定向URI”(redirection URI),同時附上一個授權碼。
  • 客戶端收到授權碼,附上早先的“重定向URI”,向認證服務器申請令牌。注意:這一步是在客戶端的後臺的服務器上完成的,對用戶不可見,用戶是無感的。
  • 認證服務器覈對了授權碼和重定向URI,確認無誤後,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)。

簡化模式(implicit grant type)不通過第三方應用程序的服務器,直接在瀏覽器中向認證服務器申請令牌,跳過了“授權碼”這個步驟,因此得名。所有步驟在瀏覽器中完成,令牌對訪問者是可見的,且客戶端不需要認證。
簡化模式的基本步驟如下:

  • 客戶端將用戶導向認證服務器。
  • 用戶決定是否給於客戶端授權。
  • 假設用戶給予授權,認證服務器將用戶導向客戶端指定的“重定向URI”,並在URIHash部分包含了訪問令牌。
  • 瀏覽器向資源服務器發出請求,其中不包括上一步收到的Hash值。
  • 資源服務器返回一個網頁,其中包含的代碼可以獲取Hash值中的令牌。
  • 瀏覽器執行上一步獲得的腳本,提取出令牌。
  • 瀏覽器將令牌發給客戶端。

密碼模式(Resource Owner Password Credentials Grant)中,用戶向客戶端提供自己的用戶名和密碼。客戶端使用這些信息,向“服務商提供商”索要授權。在這種模式中,用戶必須把自己的密碼給客戶端,但是客戶端不得儲存密碼。這通常用在用戶對客戶端高度信任的情況下,比如客戶端是操作系統的一部分,或者由一個著名公司出品。而認證服務器只有在其他授權模式無法執行的情況下,才能考慮使用這種模式。
密碼模式的基本步驟如下:

  • 用戶向客戶端提供用戶名和密碼。
  • 客戶端將用戶名和密碼發給認證服務器,向後者請求令牌。
  • 認證服務器確認無誤後,向客戶端提供訪問令牌。

客戶端模式(Client Credentials Grant)指客戶端以自己的名義,而不是以用戶的名義,向"服務提供商"進行認證。嚴格地說,客戶端模式並不屬於OAuth框架所要解決的問題。在這種模式中,用戶直接向客戶端註冊,客戶端以自己的名義要求"服務提供商"提供服務,其實不存在授權問題。
客戶端模式的基本步驟如下:

  • 客戶端向認證服務器進行身份認證,並要求一個訪問令牌。
  • 認證服務器確認無誤後,向客戶端提供訪問令牌。

這裏對OAuth協議的簡單介紹參考了阮一峯的網絡日誌中的一篇文章《理解OAuth2.0》,該文章詳細介紹了協議中信息傳輸的相關參數,感興趣的可以去學習一下,這裏僅僅是對OAuth協議進行簡要介紹。

二、Spring Social簡介

Spring Security認證成功的標準是在SecurityContext中存儲了用戶相關的Authentication實例對象,也就是說當一個用戶提供了正確信息給系統,系統帶着用戶的信息完成了一系列的校驗後,校驗通過後,將生成用戶信息相關的Authentication存儲到SecurityContext中。那麼如果使用第三方登錄,使用第三方如QQ、微信的用戶信息進行登錄驗證,這是如何做到的呢?道理也是一樣的,就是拿到第三方的用戶信息,使用第三方的用戶信息構建Authentication實例對象,並存儲到SecurityContext中,而在獲取第三方用戶信息的時候,必須遵循OAuth協議,OAuth協議規定的流程,必須嚴格執行,那麼Spring Social的作用就凸顯了,它誕生的一個很大作用就是封裝了OAuth協議規定的基礎流程。

Spring Social已經將前五步封裝了,開發者開發特定第三方登錄驗證,只需要實現第六步和第七步即可,最後組成一個SocialAuthenticationFilter集成到整個驗證的過濾器鏈上,當用戶選擇第三方登錄的時候,就會被該過濾器攔截,在過濾器中獲取第三方用戶信息,構建用戶信息相關的Authentication實例對象並存儲到SecurityContext中。

具體的開發原理和流程,我們一起跟着下面的圖片,來一步一步講解。

我們講解的順序是從右往左講,對圖片上的每一個類或者接口都進行一個詳細的講解。

  • ServiceProvider:它是一個接口,在包org.springframework.social下,它的存在就是爲了適配不同的第三方服務提供商,比如QQ、微信等。如果我們需要開發QQ登錄,那麼我們就需要爲QQ提供一個特定的ServiceProvider,而這個接口下有一個抽象實現AbstractOAuth2ServiceProvider,我們爲QQServiceProvider的時候只需要繼承AbstractOAuth2ServiceProvider類即可。抽象類AbstractOAuth2ServiceProvider有兩個屬性OAuth2OperationsApi,接下來分別介紹它們的作用。
  • OAuth2Operations:它是一個接口,在包org.springframework.social.oauth2下,它封裝了OAuth協議的前五步,也就是用戶授權,直到應用拿到第三方應用(QQ、微信)的訪問令牌,該接口有一個實現類OAuth2TemplateOAuth2Template完成了訪問第三方應用認證服務器、獲取授權碼、攜帶授權碼申請令牌、獲取令牌等核心步驟,在這裏,我們需要做的僅僅是配置一些特定第三方認證服務器的URL即可,因爲整個流程是遵循OAuth協議的,所以這些核心步驟需要攜帶的參數都是公共的,對開發者透明的。
  • Api:在包org.springframework.social下有一個ApiBinding接口,它主要是爲了幫助開發者完成第六步的一個接口,由於每一個第三方應用的用戶信息都是有區別的,比如用戶頭像的字段,在QQ裏面叫head_image,也許到了微信裏面,就叫image了,所以這裏是一個個性化的開發區域,需要對每一個第三方服務提供商開發一個特定的類來實現用戶數據的獲取,這裏Spring Social提供了一個抽象類AbstractOAuth2ApiBinding,儘可能地減少我們的開發成本,我們在開發獲取用戶信息的代碼的時候,只需要繼承這個抽象類即可。

以上的三段描述將與第三方服務提供商相關類和接口介紹完畢,那麼我們與第三方服務提供商交互完畢,拿到了用戶數據以後,那麼該如何完成認證的操作呢?那麼就得繼續來介紹上圖左邊的相關類和接口。

  • Connection:它是一個接口,在包org.springframework.social.connect下,它封裝了與用戶相關的信息,這些信息,比如DisplayName(顯示名稱),ProfileUrl(主頁地址),ImageUrl(頭像地址)等基本信息,這些信息是Spring Social所規定的用戶信息(固定字段),它有一個實現類OAuth2Connection,也就是說我們如何將拿到的用戶信息轉換成OAuth2Connection所封裝的用戶信息呢?那麼就必須得講解一下生成Connection實現類對象的工廠ConnectionFactory
  • ConnectionFactory:它是一個接口,在包org.springframework.social.connect下,它有一個實現類OAuth2ConnectionFactory,該類就可以完成對Connection的創建,而在OAuth2ConnectionFactory的構造方法中,就用到了ServiceProviderApiAdapterServiceProvider就是我們之前爲特定第三方服務提供商編寫的代碼,它提供了從第三方服務提供商獲取用戶信息,ApiAdapter是一個適配器,主要是完成了從第三方服務提供商獲取到的用戶信息到Spring Social規定的用戶信息的轉換工作,這個適配器也是需要我們自己編寫的內容之一。那麼有了ServiceProviderApiAdapter,就可以構建OAuth2ConnectionFactory對象,那麼就可以來創建Connection的實現類對象了。
  • UserConnection:UserConnectionSpring Social規定的一張數據庫表。當用戶在登錄系統的時候,選擇的是QQ登錄或者微信登錄,那麼系統是如何辨別該用戶使用QQ號登錄的時候,它對應到系統中的一個用戶呢?也就是說用戶使用的第三方賬戶A登錄,系統是如何做到與業務系統裏面的“張三”關聯起來的呢?那麼這個功勞就應該歸屬於UserConnection表了,它就是專門用來記錄第三方賬戶和業務系統內的賬戶之間的關係的一張表。
  • UsersConnectionRepository:它是一個接口,在包org.springframework.social.connect下,它專門封裝了UserConnection表的一些基礎操作,他有一個默認實現類JdbcUsersConnectionRepository,在該類的包下,有一個JdbcUsersConnectionRepository.sql文件,這個文件中有創建表的語句,需要我們自己拷貝出來創建對應的數據庫表,該表名可以是UserConnection,也可以在該名稱之前加上一個前綴。這裏貼出創建表的語句:
create table UserConnection (userId varchar(255) not null,
	providerId varchar(255) not null,
	providerUserId varchar(255),
	rank int not null,
	displayName varchar(255),
	profileUrl varchar(512),
	imageUrl varchar(512),
	accessToken varchar(512) not null,
	secret varchar(512),
	refreshToken varchar(512),
	expireTime bigint,
	primary key (userId, providerId, providerUserId));
create unique index UserConnectionRank on UserConnection(userId, providerId, rank);

這裏就完成了對Spring Social集成第三方登錄需要開發內容的基本介紹,我們將在本系列教程的第十四篇文章中介紹Spring Social集成QQ登錄驗證方式,將在第十五篇文章中介紹Spring Social集成微信登錄驗證方式,敬請期待。

Spring Security技術棧開發企業級認證與授權系列文章列表:

Spring Security技術棧學習筆記(一)環境搭建
Spring Security技術棧學習筆記(二)RESTful API詳解
Spring Security技術棧學習筆記(三)表單校驗以及自定義校驗註解開發
Spring Security技術棧學習筆記(四)RESTful API服務異常處理
Spring Security技術棧學習筆記(五)使用Filter、Interceptor和AOP攔截REST服務
Spring Security技術棧學習筆記(六)使用REST方式處理文件服務
Spring Security技術棧學習筆記(七)使用Swagger自動生成API文檔
Spring Security技術棧學習筆記(八)Spring Security的基本運行原理與個性化登錄實現
Spring Security技術棧學習筆記(九)開發圖形驗證碼接口
Spring Security技術棧學習筆記(十)開發記住我功能
Spring Security技術棧學習筆記(十一)開發短信驗證碼登錄
Spring Security技術棧學習筆記(十二)將短信驗證碼驗證方式集成到Spring Security
Spring Security技術棧學習筆記(十三)Spring Social集成第三方登錄驗證開發流程介紹
Spring Security技術棧學習筆記(十四)使用Spring Social集成QQ登錄驗證方式
Spring Security技術棧學習筆記(十五)解決Spring Social集成QQ登錄後的註冊問題
Spring Security技術棧學習筆記(十六)使用Spring Social集成微信登錄驗證方式

示例代碼下載地址:

項目已經上傳到碼雲,歡迎下載,內容所在文件夾爲chapter013

更多幹貨分享,歡迎關注我的微信公衆號:爪哇論劍(微信號:itlemon)
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章