1. 程式人生 > >Springboot SpringCloud整合OAuth2入門詳細教程

Springboot SpringCloud整合OAuth2入門詳細教程

關於OAuth2的解釋,有一篇比較出名的文章——理解OAuth 2.0 - 阮一峰的網路日誌(http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html),可以瞭解一下OAuth2的基礎知識。

簡單理解一下OAuth2,你要登入一個XX網站下片,但是下片需要註冊登入成為這個網站的會員,你不想註冊,還好網站提供了qq登入,你選擇qq登入。那麼問題來了,你選擇qq登入時,會跳轉到qq的登入頁面,輸入qq賬號密碼,注意,這時登入qq與這個XX網站是沒關係的,這是qq做的登入頁,登入時qq會問你是否允許該XXX網站獲取你的個人資訊如頭像、暱稱等,你勾選同意,登入成功後就又回到了XX網站的某個頁面了。這是一個什麼流程呢,其實是一個XX網站試圖獲取你個人資訊、然後問你是否同意的一個流程。你同意了,qq才會允許第三方網站獲取你的個人資訊。

類似的場景還有很多,譬如你授權雲列印平臺去列印你雲盤裡的照片,這些統統歸屬於你(使用者)授權第三方平臺(Client,客戶端)獲取服務方(資源伺服器)的資源。在這些場景下,你不需要在第三方平臺輸入你的賬號密碼,你所做的驗證都是在服務方的認證伺服器做的。認證通過後,認證伺服器會返回一個token給第三方,第三方就可以拿著token去訪問已經被授權訪問的資源了,第三方不需要知道你的賬號密碼。當然,第三方能訪問的資源的範圍會在認證時顯示出來給你看,你可以勾選哪些同意、哪些不同意,這樣能避免第三方獲取你太多的資訊。

百度OAuth2認證流程實戰

我們先來看看第三方平臺是怎麼工作的,我們自己充當一個第三方平臺,使用者不想在我的網站註冊,我讓他用百度賬號登入,授權百度的個人資訊給我。

這是百度的OAuth文件,http://developer.baidu.com/wiki/index.php?title=docs/oauth,要使用百度的OAuth認證,我們需要在百度開發者平臺去建立一個應用

這裡有API Key和Secret。

下圖是OAuth的整個流程。

現在我們來執行OAuth的第一步,客戶端要求使用者給予授權。也就是使用者到我們網站後,點選選擇使用百度賬號登入後,我們要做如下的事。

我們需要開啟如下網址:http://openapi.baidu.com/oauth/2.0/authorize?client_id=VaAykBZSIK33tD1yRK3XoPtx&redirect_uri=http://qq.com&response_type=code&scope=basic&display=popup

這個網址就是百度設定的授權頁面,裡面的引數解釋一下:

client_id就是你在百度開發者平臺建立應用後得到的client_id,必填。

response_type為code,代表授權模式,授權模式有4種,最常用的就是授權碼模式,我們也只看授權碼模式,別的不管,所以這裡固定值為code,必填。

scope代表授權範圍,可以客戶端自己設定不同的值代表不同的範圍,譬如abc代表只允許訪問使用者名稱頭像、def允許訪問相簿,是個可選項。

百度的文件裡講了授權許可權列表

這裡我們使用scope=basic

display這個引數不是OAuth裡的引數,是百度自己加的,屬於第三方平臺自己新加的引數,看介紹,百度的目的是讓回撥的網頁更美觀。

redirect_uri代表回撥的地址,也就是說當我們訪問上面的請求授權地址時,如果授權通過了,那麼就會自動開啟redirect_uri這個網址,並且把授權碼code新增到該網址的字尾。

我們作為一個第三方平臺,上一步請求百度授權的目的就是為了得到這個授權碼code。然後好拿著這個code,去進行下一步操作,去獲取token。

上面我寫的redirect_uri是http://qq.com,只是為了演示,如果是真的自己平臺,需要填自己的網址。

然後在安全設定裡,把授權回撥頁,填寫進行,儲存。注意,這裡填的回撥頁需要和上面的redirect_uri網址的一樣才行。

一樣OK,我們就可以去申請授權了,直接訪問http://openapi.baidu.com/oauth/2.0/authorize?client_id=VaAykBZSIK33tD1yRK3XoPtx&redirect_uri=http://qq.com&response_type=code&scope=basic&display=popup

可以看到出來了這樣一個介面,注意看右邊的“允許test應用進行以下操作”,這個就是上面配置的scope決定的。像這樣的介面很多,微信啊qq啊都有這樣的授權頁,你可以選擇勾選你想讓訪問的資訊範圍。

這裡我們輸入自己的百度賬號密碼,點登入即可。這裡你的百度賬號密碼第三方平臺是拿不到的,這是百度的登入頁。

登入成功後介面:

可以看到,百度回撥打開了我們填寫的redirect_url,並且把code覆在後面。如果這是你自己的域名伺服器,你就可以使用code引數接收到值,並且進行下一步獲取token的操作了。

下面我們來獲取token。

訪問https://openapi.baidu.com/oauth/2.0/token?grant_type=authorization_code&code=66bee35200ddf4dfb3bd3caca1969e4b&client_id=VaAykBZSIK33tD1yRK3XoPtx&client_secret=FgL5vYi1Fs11IprXB8LprHIv4fslTqsC&redirect_uri=http://qq.com

這裡面的grant_type是固定的,別的幾個都和上面一樣,多了個secret,從你的百度開發者平臺能看到。code就是上一步獲取到的code,注意code是有時限的,獲取到code後過一段時間就失效了,需要重新獲取code。

返回值如下:

這個是百度的文件:

可以看到百度返回了token,session_key、session_secret、refresh_token等欄位,貌似和OAuth官方描述的不一樣,可能百度自己有自己的考慮吧。下圖是阮一峰文章裡講的正常的返回值。

已經取得了token了,我們就可以呼叫百度的一些api了,譬如可以通過GET方法傳送如下請求包來呼叫獲取當前登入使用者的基本資料的開放API介面:GET https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token=1.a6b7dbd428f731035f771b8d15063f61.86400.1292922000-2346678-124328

把token換成上一步取到的token,通過這個api就能獲取到使用者基本資料了。

OK,上面的幾個步驟全部搞完,我們就已經完成了通過網頁來搞定一次OAuth認證的全過程了。

SpringBoot OAuth2客戶端實戰

下面我們來使用SpringBoot完成一次同樣的過程,來看看偉大的Spring為我們省略了哪幾個步驟。此時我們就是個第三方平臺,使用者想要訪問我們的內容,我們先讓使用者去登入他的百度賬號給我們授權,就是這麼個意思。

新建一個springboot專案,勾選web、Security,還要引入oauth2,最終的pom如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.tianyalei</groupId>
  6. <artifactId>test_oauth</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>test_oauth</name>
  10. <description>Demo project for Spring Boot</description>
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>1.5.8.RELEASE</version>
  15. <relativePath/> <!-- lookup parent from repository -->
  16. </parent>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20. <java.version>1.8</java.version>
  21. </properties>
  22. <dependencies>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-security</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-web</artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework.security.oauth</groupId>
  33. <artifactId>spring-security-oauth2</artifactId>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.springframework.boot</groupId>
  37. <artifactId>spring-boot-starter-test</artifactId>
  38. <scope>test</scope>
  39. </dependency>
  40. <dependency>
  41. <groupId>org.springframework.security</groupId>
  42. <artifactId>spring-security-test</artifactId>
  43. <scope>test</scope>
  44. </dependency>
  45. </dependencies>
  46. <build>
  47. <plugins>
  48. <plugin>
  49. <groupId>org.springframework.boot</groupId>
  50. <artifactId>spring-boot-maven-plugin</artifactId>
  51. </plugin>
  52. </plugins>
  53. </build>
  54. </project>
在啟動Application類上,加上@EnableOAuth2Sso註解
  1. @SpringBootApplication
  2. @EnableOAuth2Sso
  3. public class TestOauthApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(TestOauthApplication.class, args);
  6. }
  7. }
來定義一個controller
  1. import org.springframework.web.bind.annotation.RequestMapping;
  2. import org.springframework.web.bind.annotation.RestController;
  3. /**
  4. * @author wuweifeng wrote on 2017/10/18.
  5. */
  6. @RestController
  7. public class PublicController {
  8. @RequestMapping("/abc")
  9. public String index() {
  10. return "Hello abc";
  11. }
  12. }

然後在resource下的static目錄放一個index.html檔案,裡面就寫一個句話Hello就行了。

看起來沒什麼,就是個普通的工程,毫無特色是嗎?

啟動它,訪問localhost:8080試試,發現報錯了,我們原以為能進入index.html,但是並沒有,它報錯了:

而且還自動給跳轉到了localhost:8080/login這個網址去了,這是為什麼呢?

我們使用Chrome的開發者工具來看一下

當訪問localhost:8080時,302跳轉了,跳轉到了localhost:8080/login,這是spring OAuth2自動完成的,它攔截所有的請求,然後302到login,然後去做一件事,就是去OAuth的認證服務端去做認證,也就是帶著clientId,redirect_uri等等引數,去訪問server,去拿code,如果你還記得上面我們用瀏覽器請求百度code的步驟,你就明白。

那認證伺服器地址在哪呢,它不知道,所以它報錯了,異常裡說了,需要個地址。

我們在application.yml裡配置地址

  1. security:
  2. oauth2:
  3. client:
  4. client-id: VaAykBZSIK33tD1yRK3XoPtx
  5. client-secret: FgL5vYi1Fs11IprXB8LprHIv4fslTqsC
  6. access-token-uri: http://openapi.baidu.com/oauth/2.0/token
  7. user-authorization-uri: http://openapi.baidu.com/oauth/2.0/authorize
  8. resource:
  9. userInfoUri: https://openapi.baidu.com/rest/2.0/passport/users/getInfo
client下的那幾個引數都明白,我們文章上面都講過,還有幾個引數沒列出來,看下圖

可以看到tokenName也是可以自定義的,預設是"access_token",如果認證伺服器不是這個,譬如Facebook的就叫"oauth_token",那麼就配置tokenName。還有那兩個scheme是幹什麼的呢?看下圖DefaultClientAuthenticationHandler.java

它們是來決定發請求時是放在header裡還是用form表單提交過去。

client的幾個配置引數都懂了,還有個resource.userInfoUri,這個是幹什麼的呢,先不要管它,後面就知道了。

以上全部配置完畢,再啟動專案,訪問localhost:8080

看到百度給我們返回了這樣一個介面,正常情況下應該是一個百度登入的介面。看箭頭地方,redirect_uri=localhost:8080/login,這個地址我們並沒有配置,是spring OAuth它自動給我們加上的,如果你還記得的話,我們上面在百度的回撥地址配了個http://qq.com,下面我們把他修改為箭頭的地址

再次訪問localhost:8080.

好的,這下就是正常介面了,填寫百度賬號密碼,看看效果。

可以看到,進入到了我們之前新增的index.html裡了。再次訪問localhost:8080/abc,也正常進入到了abc方法的返回,不需要再次百度鑑權。說明百度正常授權,並且已經返回了token,只不過這些都由spring自動給我們處理了。

再來回憶一下步驟,發起認證請求,輸入百度賬號密碼成功後給我們返回code,我們拿著code再去請求百度token,百度返回token,我們拿著token去請求百度API介面。

現在看看spring的步驟,發起認證請求,輸入百度賬號密碼,over。spring幫我們自動處理了code和token相關的事情。

下面我們來看看application.yml裡resource.userInfoUri是幹什麼的,我們先把它註釋掉。然後重啟,訪問localhost:8080

發現訪問變的很漫長,最終出錯了,這是為什麼呢?

原來是spring在獲得token後,必須要呼叫一下resource.userInfoUri裡的介面,看看到底有沒有返回值,也就是要驗證一下token是不是正確的,這一步是它自動完成的。獲得token——呼叫介面獲取user details資訊,如果成功了,才算正常,然後才會返回到你最開始呼叫的localhost:8080。

所以這個userInfoUri是必須的,我們可以在百度的文件裡找到這個獲取使用者資訊的API介面,填上去就OK了。

這一篇詳細講了OAuth客戶端的工作流程,和整合springboot後的工作步驟,後面我們會自己搭建OAuth服務端。