整合用戶篇—Oauth2理解與構造簡單的系統

        當你運營愈來愈多的項目,每一個項目的業務都不同,每一個須要使用到這些業務的用戶就須要註冊方能進行使用。若是用戶還須要使用其餘項目的功能,就必須還得註冊使用。形成反覆註冊,反覆登陸的不友好體驗。javascript

       此時不少人都會想着使用ucenter等方式來整合用戶,將用戶放到一個數據中心來管理。全部的項目都將使用這一個公共的數據中心來運行。ucenter和各個項目之間通訊將採用隱式api的方式進行通訊,全部的行爲都在後端進行,比如你的淘寶帳號能夠在天貓登陸同樣。在這裏,假設你也作了一個業務網站,你的網站用戶須要一鍵分享到他的淘寶主頁中、或者須要讀取賣家的商品信息,那麼總不能在你的網站讓用戶提供他的淘寶帳號密碼,這顯得尤其的不專業和不安全。oauth2就恰好解決了這一問題,讓用戶使用淘寶的快捷登陸進行受權後由淘寶提供的接口來返回用戶相關信息,並將這一信息在本身數據庫中生成對應的一份關聯起來,這一切就變得比較安全,比較合理了。php

        本文的主題是oauth2,oauth是一種協議,2是版本,即oauth2是一個協議標準,將多個項目整合起來,提供所需的接口通訊,oauth2能夠創建在ucenter上能夠創建在單獨業務上,總之他只是一種協議,具體關於該協議的介紹請直接瀏覽官網或者百科介紹html

        上文中有提到淘寶的快捷登陸,那麼對應就還有QQ快捷登陸(QQ互聯)、新浪快捷登陸、google帳號登陸等。這樣一來就可使得本來來自QQ、來自google的用戶無需在你的網站進行註冊後,直接間接使用原先的媒體帳號登陸就能夠在你的網站訪問。用google舉例,你的網站可使用google帳號快捷登陸,而且能夠得到google提供的一些接口服務,那麼將這一關係中的google稱爲server端,你的網站稱之爲client端,下文將client稱爲應用。
        當前主要是說本身構造一個oauth2 server端,就是上文中所提到的QQ互聯、微博等,能夠給本身的其餘應用或者開放給第三方應用來提供方便用戶管理的一個系統。前端


上圖是Oauth2運行流程,描述了四個角色之間交互的方式。摘自RFC 6749java

(A):Client 向 Resoure Owner 請求受權。能夠理解爲用戶發起請求。sql

(B):在Server端用戶贊成給予 Client 端受權,並返回至 Client 端告知。數據庫

(C):Client攜帶Authorization Grant 向Authorization Server請求獲取Access Token。後端

(D):受權驗證經過後,返回Access Token。api

(E):Client攜帶Access Token向Resource獲取相關資源,例如頭像、暱稱、相冊等。跨域

(F):Access Token 驗證 經過後 Resource Server 所需獲取資源。

 

瞭解了基本的流程後,來簡單構造一下數據表。

/**
 * app 應用
 */
CREATE TABLE uc_app(
app_id int primary key auto_increment,
app_sign varchar(20) NOT NULL DEFAULT '' COMMENT '應用惟一ID',
app_secret varchar(60) NOT NULL DEFAULT '' COMMENT '通訊密碼',
app_name varchar(20) NOT NULL DEFAULT '' COMMENT '應用名稱',
app_desc varchar(60) NOT NULL DEFAULT '' COMMENT '應用描述',
app_providers varchar(60) NOT NULL DEFAULT '' COMMENT '提供商',
app_url varchar(255) NOT NULL DEFAULT '' COMMENT '官方主頁',
app_type tinyint NOT NULL DEFAULT '1' COMMENT '應用類型 1.官方應用 2.第三方應用',
app_status tinyint NOT NULL DEFAULT '1' COMMENT '應用狀態 1.正常運行 2.中止運行 5.審覈中',
allow_domain varchar(255) NOT NULL DEFAULT '' COMMENT '受權域名',
allow_ip varchar(100) NOT NULL DEFAULT '' COMMENT '容許IP',
release_time int(10) NOT NULL DEFAULT '0' COMMENT '發佈時間',
expire_time int(10) NOT NULL DEFAULT '0' COMMENT '失效時間',
UNIQUE KEY (`app_sign`)
);

/**
 * user_key 用戶受權code
 */
CREATE TABLE uc_user_key(
id int primary key auto_increment,
code_key varchar(60) NOT NULL DEFAULT '',
app_sign varchar(20) NOT NULL DEFAULT '' COMMENT '應用惟一ID',
user_id int NOT NULL DEFAULT '0' COMMENT '用戶ID',
redirect_uri varchar(255) NOT NULL DEFAULT '' COMMENT '回調URI',
scope varchar(20) NOT NULL DEFAULT '' COMMENT '申請權限範圍',
state varchar(20) NOT NULL DEFAULT '' COMMENT '客戶端狀態',
release_time int(10) NOT NULL DEFAULT '0' COMMENT '生成時間',
expire_time int(10) NOT NULL DEFAULT '0' COMMENT '失效時間',
status tinyint NOT NULL DEFAULT '0' COMMENT '狀態:0.待驗證 1.已驗證 2.已過時',
UNIQUE KEY(`code_key`)
);

/**
 *	uc_user_token
 *	用戶訪問權限表
 */
CREATE TABLE uc_user_token(
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL DEFAULT '0' COMMENT '用戶ID',
app_sign VARCHAR(20) NOT NULL DEFAULT '' COMMENT '應用惟一標識',
access_token VARCHAR(60) NOT NULL DEFAULT '' COMMENT '權限操做TOKEN',
scope VARCHAR(20) NOT NULL DEFAULT '' COMMENT '所申請權限',
release_time INT(10) NOT NULL DEFAULT '0' COMMENT '生效時間',
expire_time INT(10) NOT NULL DEFAULT '0' COMMENT '失效時間',
UNIQUE KEY(user_id, app_sign)
);

 

程序流程

1.第三方應用選擇快速登陸,跳轉至Server端進行受權登陸。

url:http://ucenter.example.com/authorize?response_type=code&client_id=f2292b656df429d4&state=xyz&
redirect_uri=http%3a%2f%2fclient.example.com%2fuser.php

若是是已經登陸狀態,則自動跳過登陸過程,不然展示登陸頁面,引導用戶進行登陸。

2.用戶登陸完畢以後,生成一個相對用戶相對client_id相對回調信息的記錄到數據庫,並將這條記錄的惟一值code返回給應用。

url:http://client.example.com/user?code=Q8GlvX4lS3Fy3KBHpQro&state=xyz

3.用戶攜帶上一步獲取的code, 到服務端進行受權,獲取通訊口令。

參數說明
參數 是否必須 含義
grant_type 必須 受權類型,此值固定爲「authorization_code」
client_id 必須 第三方應用的app_sign。
client_secret 必須 第三方應用的app_secret。
code 必須 上一步返回的authorization code。
redirect_uri 必須 與上一步中的回調地址一致。
url:http://ucenter.example.com/token
Method:POST
Form Data
grant_type:authorization_code
code:Q8GlvX4lS3Fy3KBHpQro
redirect_uri:http://client.example.com/user.php

4.服務端校驗code事後,頒發通訊令牌 access_token 。

Response: 
{
    response: - {
        code: 200,
        msg: "success"
    },
    datas: - {
       "access_token":"kIJAkUd31F58nGpknvoW3L4r3S21koNd9Lk0rfcn",
       "token_type":"example",
       "expires_in":3600,//有效期
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",//可選項,存在表示下一次使用該令牌獲取資源
    }
}

5.應用使用access_token 向服務端提供的接口來獲取數據,例如獲取當前登陸用戶信息。

url:http://ucenter.example.com/user/getInfo?access_token=kIJAkUd31F58nGpknvoW3L4r3S21koNd9Lk0rfcn

6.服務端驗證受權以後,將所需數據返回給應用。

Response: 
{
    response: - {
        code: 200,
        msg: "success"
    },
    datas: - {
        user_id: 5,
        user_name: "ellermister",
        full_name: "E先生",
        headimg: "http://q.qlogo.cn/headimg_dl?bs=qq&dst_uin=11733685&spec=100",
        address: "北京市北四環西路58號理想國際大廈"
    }
}

 

7.更新通訊令牌方式借用第3步的,參數 grant_type 更改成 refresh_token,去掉code、redirect_uri,返回結果中增長 refresh_token 項,以後再次獲取資源將使用refresh_token做爲新的令牌。

 

補充下本身所遇到的問題,在前端實現上,會將用戶從C站點引導到S站點進行登陸並受權,隨後返回C站點。

此過程當中比較合理也是常見的過程則是,在C站點上點擊登陸,彈窗S站點,登錄受權完畢後關閉S站點,C站點進行刷新便可登陸成功。該問題涉及JS跨域刷新父窗口,解決方案以下。

//C站點彈出JS
window.open('http://ucenter.example.com/authorize?response_type=code&
client_id=f2292b656df429d4&state=xyz&redirect_uri=http%3A%2F%2Fclient%2Eexample%2Ecom%2Fuser.php',
'彈出層頁面','top=100,left=400,width=800,height=600,toolbar=no,menubar=no,scrollbars=yes,
resizable=yes,location=no,status=no');

//S站點受權登陸後,執行JS
url = 'http://client.example.com/user.php?code=xxxx&state=xyz';//回調URL
try{  
        window.parent.opener.location.reload();  
        window.parent.close();  
}catch(e){
        window.parent.opener.location = url;  
        window.parent.opener = null;  
        window.parent.close();  
}

 

從登陸到受權到獲取資源基本流程也便是如此,期間的access_token 是針對用戶的,固然也能夠是針對全局的,相似於微信的作法。根據實際項目狀況,作法不可能一成不變,本文只是提供了一個基本的流程,讓初次瞭解的人可以快速的瞭解這個受權過程。

本文僅供參考理解,若有錯誤,請指正!

 

延伸閱讀:

The OAuth 2.0 Authorization Framework  https://tools.ietf.org/html/rfc6749

理解OAuth 2.0  http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

OAuth 2 開發人員指南  http://www.oschina.net/translate/oauth-2-developers-guide

相關文章
相關標籤/搜索