OAuth2.0 知多少

OAuth2.0 知多少
原文:OAuth2.0 知多少

1. 引言

周末逛簡書,看了一篇寫的極好的文章,點擊大紅心點贊,就直接給我跳轉到登錄界面了,原來點贊是需要登錄的。

簡書登錄界面

可是沒有我并沒有簡書賬號,一直使用的QQ的集成登錄。下面有一排社交登錄按鈕,我們可以用第三方社交賬號登陸即可。點擊QQ圖標,就給我跳轉到了QQ登錄授權頁面,如下圖:

QQ登錄授權頁面

從圖片上我們可以看到主要包括兩個部分,一個是左邊的用戶登錄,一個是右邊告知簡書將獲取哪些權限。輸入QQ賬號和密碼,點擊授權并登錄,就成功登錄到簡書了,并成功獲取到了我QQ的賬號和昵稱,如下圖:

簡書成功獲取到QQ昵稱和頭像

簡書集成的社交登錄,大大簡化了我們的注冊登錄流程,真是一號在手上網無憂啊。
這看似簡單的集成,但背后的技術原理『OAuth2.0』可沒那么簡單,那我們廢話不多說,一探究竟吧。

2. OAuth 2.0

OAuth 2.0是用于授權的行業標準協議。OAuth 2.0取代了在2006年創建的原始OAuth協議上所做的工作。OAuth 2.0專注于客戶端開發人員的簡單性,同時為Web應用程序,桌面應用程序,手機和客廳設備提供特定的授權流程。

在傳統的client-server認證模型中,客戶端請求訪問服務器上受限的資源(protected resource),需要通過使用資源所有者(resource owner)的憑證在服務器上進行認證。為了支持第三方應用程序訪問受限資源,資源所有者需要向第三方應用共享其憑證。這就會造成以下問題:

  1. 第三方應用為了后續使用,會存儲資源所有者的憑證主要是密碼。
  2. 服務端需要支持密碼認證,盡管密碼認證不安全。
  3. 第三方應用獲得對資源的過度訪問而不僅局限于受限資源,且資源所有者沒有辦法對其進行限制。
  4. 資源所有者無法收回權限,除非修改密碼。
  5. 如果第三方應用的密碼被破解,就會導致所有被該密碼保護的數據被泄露。

想一想這樣一個場景,如果簡書是直接使用QQ用戶名密碼登錄,簡書就很有可能會為了后續業務的需要而擅自保存QQ用戶名及密碼,簡書只要拿到了QQ用戶名密碼就可以訪問不僅僅QQ昵稱、頭像等信息,甚至可以獲取到QQ用戶的所有通訊錄列表。如果簡書的賬號密碼泄露,就會直接影響到QQ數據的安全。這是一個可怕的問題。

所以OAuth應運而生,來解決這一問題。

3. OAuth 2.0授權流程

下面我們就以簡書使用QQ授權登錄為例,來捋一捋OAuth 2.0的流程。
先來看看OAuth 2.0的流程,如下圖所示:
OAuth 2.0授權流程

這里面主要包含四個角色:

  1. Client:需要授權的客戶端,本文中就是【簡書】。
  2. Resource Owner:資源所有者,在本文中你可能會以為是 QQ,但要想清楚,QQ是屬于個人的,所以在本文中資源所有者是指【QQ用戶】。
  3. Authorization Server:認證服務器,本文中特指【QQ互聯平臺】。
  4. Resource Server:資源服務器,顧名思義,用來專門保存資源的服務器,接受通過訪問令牌進行訪問。本文特指【QQ用戶信息中心】。

3.1. 第一步:引導用戶到認證服務器

圣杰打開簡書網頁,簡書跳轉到登錄界面,要求用戶登錄。可是圣杰未在簡書注冊帳號,所以就點擊了QQ圖標,使用QQ帳號進行集成登錄。跳轉到QQ登錄界面后,QQ要求用戶授權。
這一步中簡書主要做了這樣一件事就是引導用戶到認證服務器。
很顯然【QQ互聯平臺】就是認證服務器。

如何引導?當然是頁面跳轉。
那認證服務器如何知道是簡書過來的認證請求?
當然是傳參。
那需要傳遞哪些參數呢?

  • response_type:表示響應類型,必選項,此處的值固定為"code";
  • client_id:表示客戶端的ID,用來標志授權請求的來源,必選項;
  • redirect_uri:成功授權后的回調地址;
  • scope:表示申請的權限范圍,可選項;
  • state:表示客戶端的當前狀態,可以指定任意值,認證服務器會原封不動地返回這個值。

咱們看看簡書實際發送的授權請求Url是:
https://graph.qq.com/oauth2.0/authorize?client_id=100410602 &redirect_uri=http://www.jianshu.com/users/auth/qq_connect/callback &response_type=code &state=bb38108d1aaf567c72da0f1167e87142d0e20cb2bb24ec5a

無圖無真相,咱們看看控制臺的網絡監控:

簡書跳轉到QQ登錄的認證請求

如圖所示,除了scope參數外,其他四個參數均有傳參。
此時你可能唯一對state參數比較迷惑,傳遞一個state參數,認證服務器會原封不動返回,那還干嘛要傳遞state參數呢?

我的理解是,簡書用一個guid加長版字符串來唯一標識一個授權請求。這樣才會正確獲取授權服務器返回的授權碼。

這里你可能會問了,既然我知道了這些參數,我豈不是可以偽造簡書認證請求,修改redirect_uri參數跳轉到個人的網站,然后不就可以獲取QQ授權?

跟我一樣太傻太天真,簡書在QQ互聯平臺申請時肯定已經預留備案了要跳轉返回的URL。QQ互聯平臺在收到簡書的授權請求時肯定會驗證回調Url的。

3.2. 第二步:用戶同意為第三方客戶端授權

這一步,對于用戶來說,只需要使用資源所有者(QQ)的用戶名密碼登錄,并同意授權即可。點擊授權并登錄后,授權服務器首先會post一個請求回服務器進行用戶認證,認證通過后授權服務器會生成一個授權碼,然后服務器根據授權請求的redirect_uri進行跳轉,并返回授權碼code和授權請求中傳遞的state
這里要注意的是:授權碼有一個短暫的時效

無圖無真相,咱們還是看一下控制臺網絡監控:

用戶授權并登錄

從圖中即可驗證我們上面所說,最終跳轉回簡書的Url為:
http://www.jianshu.com/users/auth/qq_connect/callback?code=093B9307E38DC5A2C3AD147B150F2AB3 &state=bb38108d1aaf567c72da0f1167e87142d0e20cb2bb24ec5a
和之前的授權請求URL進行對比,可以發現redirect_uristate完全一致。
code=093B9307E38DC5A2C3AD147B150F2AB3就是返回的授權碼。

3.3. 第三步:使用授權碼向認證服務器申請令牌

從這一步開始,對于用戶來說是察覺不到的。簡書后臺默默的在做后續的工作。

簡書拿到QQ互聯平臺返回的授權碼后,需要根據授權碼再次向認證服務器申請令牌(access token)。
到這里有必要再理清兩個概念:

  • 授權碼(Authorization Code):相當于授權服務器口頭告訴簡書,用戶同意授權使用他的QQ登錄簡書了。
  • 令牌(Access Token):相當于臨時身份證。

那如何申請令牌呢?
簡書需要后臺發送一個get請求到認證服務器(QQ互聯平臺)。
那要攜帶哪些必要信息呢?
是的,要攜帶以下參數:

  • grant_type:表示授權類型,此處的值固定為"authorization_code",必選項;
  • client_id:表示從QQ互聯平臺申請到的客戶端ID,用來標志請求的來源,必選項;
  • client_secret:這個是從QQ互聯平臺申請到的客戶端認證密鑰,機密信息十分重要,必選項;
  • redirect_uri:成功申請到令牌后的回調地址;
  • code:上一步申請到的授權碼。

根據以上信息我們可以模擬一個申請AccessToken的請求:
https://graph.qq.com/oauth2.0/token?client_id=100410602 &client_secret=123456jianshu &redirect_uri=http://www.jianshu.com/users/auth/qq_connect/callback &grant_type=authorization_code &code=093B9307E38DC5A2C3AD147B150F2AB3

發送完該請求后,認證服務器驗證通過后就會發放令牌,并返回到簡書后臺,其中應該包含以下信息:

  • access_token:令牌
  • expires_in:access token的有效期,單位為秒。
  • refresh_token:在授權自動續期步驟中,獲取新的Access_Token時需要提供的參數。

同樣,我們可以模擬出一個返回的token:
http://www.jianshu.com/users/auth/qq_connect/callback?access_token=548ADF2D5E1C5E88H4JH15FKUN51F &expires_in=36000&refresh_token=53AD68JH834HHJF9J349FJADF3

這個時候簡書還有一件事情要做,就是把用戶token寫到cookie里,進行用戶登錄狀態的維持。咱們還是打開控制器驗證一下。

保存token到cookie

從圖中可以看出簡書把用戶token保存在名為remember_user_token的cookie里。
不用打cookie的歪主意了,肯定是加密了的。
可以嘗試下手動把remember_user_token這條cookie刪除,保證刷新界面后需要你重新登錄簡書。

3.4. 第四步:向資源服務器申請資源

有了token,向資源服務器提供的資源接口發送一個get請求不就行了,資源服務器校驗令牌無誤,就會向簡書返回資源(QQ用戶信息)。

同樣咱們也來模擬一個使用token請求QQ用戶基本信息資源的URL:
https://graph.qq.com/user/get_user_info?client_id=100410602 &qq=2098769873 &access_token=548ADF2D5E1C5E88H4JH15FKUN51F

到這一步OAuth2.0的流程可以說是結束了,但是對于簡書來說還有重要的事情要做。那就是:
拿到token、reresh_token和用戶數據這么重要的東西不存數據庫傻呀?

3.5. 第五步:令牌延期(刷新)

你肯定對第四步返回的refresh_token比較好奇。
它是用來對令牌進行延期(刷新)的。為什么會有兩種說法呢,因為可能認證服務器會重新生成一個令牌,也有可能
對過期的令牌進行延期。

比如說,QQ互聯平臺為了安全性考慮,返回的access_token是有時間限制的,假如用戶某天不想授權了呢,總不能給了個access_token你幾年后還能用吧。我們上面模擬返回的令牌有效期為10小時。10小時后,用戶打開瀏覽器逛簡書,瀏覽器中用戶的token對應的cookie已過期。簡書發現瀏覽器沒有攜帶token信息過來,就明白token失效了,需要重新向認證平臺申請授權。如果讓用戶再點擊QQ進行登錄授權,這明顯用戶體驗不好。咋搞呢?refresh_token就派上了用場,可以直接跳過前面申請授權碼的步驟,當發現token失效了,簡書從瀏覽器攜帶的cookie中的sessionid找到存儲在數據庫中的refresh_token,然后再使用refresh_token進行token續期(刷新)。

那用refresh_token進行token續期需要怎么做呢?
同樣需要向認證服務器發送一個get請求。
需要哪些參數?

  • grant_type:表示授權類型,此處的值固定為"refresh_token",必選項;
  • client_id:表示從QQ互聯平臺申請到的客戶端ID,用來標志請求的來源,必選項;
  • client_secret:這個是從QQ互聯平臺申請到的客戶端認證密鑰,機密信息十分重要,必選項;
  • refresh_token:即申請令牌返回的refresh_token。

根據上述信息,我們又可以模擬一個令牌刷新的URL:
https://graph.qq.com/oauth2.0/token?client_id=100410602 &client_secret=123456jianshu &redirect_uri=http://www.jianshu.com/users/auth/qq_connect/callback &grant_type=refresh_token &refresh_token=53AD68JH834HHJF9J349FJADF3
那返回的結果呢?
和第四步返回的結果一樣。

這里你可能又有疑問了,那既然每次進行令牌延期后都會重新返回一個refresh_token,那豈不是我可以使用refresh_token無限延期?
天真如我啊,refresh_token也是有過期時間的。而這個過期時間具體是由認證服務器決定的。
一般來說refresh_token的過期時間要大于access_token的過期時間。只有這樣,access_token過期時,才可以使用refresh_token進行令牌延期(刷新)。

舉個簡單例子:
假設簡書從QQ互聯平臺默認獲取到的access_token的有效期是1天,refresh_token的有效期為一周。

用戶今天使用QQ登錄授權后,過了兩天再去逛簡書,簡書發現token失效,立馬用refresh_token刷新令牌,默默的完成了授權的延期。
假如用戶隔了兩周再去逛簡書,簡書一核對,access_tokenrefresh_token全都失效,就只能乖乖引導用戶到授權頁面重新授權,也就是回到OAuth2.0的第一步。

4.0 總結

本文以簡書通過QQ進行授權登錄為例,對OAuth2.0 的授權流程進行了梳理,希望通讀此文,對你有所幫助。

如果對OAuth2.0有所了解的話,你應該明白本文其實是對OAuth2.0中授權碼模式授權方式的講解。

如果想了解OAuth2.0其他幾種授權方式,建議參考理解OAuth 2.0?- 阮一峰的網絡日志。

posted on 2018-12-25 10:59 NET未來之路 閱讀(...) 評論(...) 編輯 收藏

轉載于:https://www.cnblogs.com/lonelyxmas/p/10172722.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/248781.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/248781.shtml
英文地址,請注明出處:http://en.pswp.cn/news/248781.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

五分鐘帶你摸透 Vue組件及組件通訊

一.組件化開發 組件 (Component) 是 Vue.js 強大的功能之一。組件可以擴展 HTML 元素,封裝可重用的代 碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器為它添加特殊功能。在vue中都是組件化開發的,組件化開發就是把一個完整的…

Parameter 'userName' not found. Available parameters are [1, 0, param1, param2]

Mapper接口的方法的參數沒有加:Param("xxx")注解,或者是xxx寫不對轉載于:https://www.cnblogs.com/linliquan/p/10987136.html

微信公眾號開發-接入

一 首先實現內網穿透,公眾號需要連接我們的服務器,內外無法訪問,所以先實現自己的內網可以測試時連接外網,下載natapp,選擇windows,順便下載config,ini 配置文件。注冊好購買免費的隧道 然后將token寫入配置…

Vue 項目上線優化

上線項目的優化 優化上線項目,首先在上線打包時我們通過babel插件將console清除,當然對項目打包后的體積的影響是微乎其微,對項目的入口文件的改善也是很有必要的,因為在開發階段和上線如果我們使用的是同一入口文件,…

Python并發編程—進程

多任務編程 1.意義: 充分利用計算機多核資源,提高程序的運行效率。 2.實現方案 :多進程 , 多線程 3.并行與并發 并發 : 同時處理多個任務,內核在任務間不斷的切換達到好像多個任務被同時執行的效果&#xf…

Vue 腳手架中的.eslintrc.js代碼規范 的解決

在我們使用Vue腳手架 創建項目時 尤其是團隊共同開發項目時 會按照一個共同的代碼規范來編程 創建Vue腳手架中有一個.eslintrc.js格式 但是在編程中我們通常會使用 shiftaltf 進行代碼格式化 但是由于格式化后的代碼 與Vue中的.eslintrc規范不協調 尤其是 “” ; 以…

innodb_locks_unsafe_for_binlog分析

mysql數據庫中默認的隔離級別為repeat-read. innodb默認使用了next-gap算法,這種算法結合了index-row鎖和gap鎖。正因為這樣的鎖算法,innodb在可重復讀這樣的默認隔離級別上,可以避免幻象的產生。 innodb_locks_unsafe_for_binlog最主要的作用…

emacs的使用方法

emacs的使用方法 emacs配置: 將文件命名為.emacs,把配置敲進去,放在home文件夾 emacs命令行: altx打開命令行 編譯: 在命令行輸入compile,回車,會出現make -k,刪掉它,輸入…

前端面試---Vue部分考點梳理

一. Vue的使用 1. Vue的基本使用 指令 插值 插值 表達式 指令 動態屬性 v-html 會有XSS風險 會覆蓋子組件 computed 和 watch computed 有緩存 data不變則不會重新計算watch 如何深度監聽watch 監聽引用類型時 拿不到oldVal v-for v-for 和 v-if 不能同時使用:key的值盡量…

.net core實現跨域

什么是跨域在前面已經講解過了,這里便不再講解,直接上代碼。 一、后臺API接口 用.net core創建一個Web API項目負責給前端界面提供數據。 二、前端界面 建立兩個MVC項目,模擬不同的ip,在view里面添加按鈕調用WEB API提供的接口進行…

TCP/IP簡介

TCP/IP簡介 OSI的“實現”:TCP/IP參考模型 并不完全符合OSI的七層參考模型,但我們可以理解為OSI的一種實現 TCP/IP協議簡述 在很多情況下,它只是利用IP協議進行通信時,所必須用到的協議群的統稱,具體來說,I…

Spring-Cloud 學習筆記-(4)負載均衡器Ribbon

目錄 Spring-Cloud 學習筆記-(4)負載均衡器Ribbon1、前言2、什么是負載均衡2.1、問題分析2.2、什么是Ribbon3、快速入門3.1、實現方式一3.1.1、修改代碼3.2、實現方式二3.2.1、啟動類3.2.2、調用代碼3.2.3、測試3.2.4、實現原理3.2.5、斷點調式3.3、修改…

‘仿微信發表朋友圈’項目中登錄功能的業務邏輯

登錄功能 手機號驗證碼都通過后端驗證后 返回用戶數據 登陸成功 成功后 調用store中的setUser方法 store中的setUser方法 將后端返回的用戶信息存儲到localStorage中 同時登錄成功后服務器會將token自動存入我們的cookie中 有過期時間 在我們請求需要登錄的接口時將cookie中的…

kubernetes--配置文件

轉載于:https://www.cnblogs.com/caiciadeliliang/p/10993388.html

微信動態中的背景圖更換

初衷: 圖一中的紅框中的部分,作為用戶自定義的背景圖,如果用戶沒有上傳也會為其自動設置一張背景圖,當用戶點擊時則會出現圖二中的選項 ,點擊取消則選項消失,點擊從相冊選擇則會跳轉本機的相冊&#xff0c…

大數據學習——akka自定義RPC

實現 package cn.itcast.akkaimport akka.actor.{Actor, ActorSystem, Props} import akka.actor.Actor.Receive import com.typesafe.config.ConfigFactoryimport scala.collection.mutableimport scala.concurrent.duration._class Master(val host: String, val port: Int) …

從Client應用場景介紹IdentityServer4(一)

從Client應用場景介紹IdentityServer4(一) 原文:從Client應用場景介紹IdentityServer4(一)一、背景 IdentityServer4的介紹將不再敘述,百度下可以找到,且官網的快速入門例子也有翻譯的版本。這里主要從Clie…

開發常用代碼筆記

Vue 使用moment插件對時間進行格式化(全局設置) 下載插件 npm install moment --save 在main.js中引入插件 import moment from ‘moment’ 在main.js中定義全局過濾器 Vue.filter(dataFilter,function (dataStr,patten YYYY-MM-DD HH:mm:ss) {retur…

springboot 參數校驗詳解

https://www.jianshu.com/p/89a675b7c900 在日常開發寫rest接口時,接口參數校驗這一部分是必須的,但是如果全部用代碼去做,顯得十分麻煩,spring也提供了這部分功能,本文來探究一下如何實現 1.配置 spring-boot-starter-web包自動依…

微信小程序——賬號及開發工具

1. 注冊微信小程序賬號 點擊我進入微信公眾平臺 進入后點擊立即注冊 注冊成功且登錄后進入小程序管理后臺 2. 安裝開發者工具 點擊進入開發文檔 進入安裝開發工具(穩定版本) 一路默認下一步進行安裝 3. 開發者工具的使用 使用注冊微信小程序的微信號…