第三方登陸是如今常見的登陸方式,免註冊且安全方便快捷。javascript
本篇文章將以Github爲例,介紹如何在本身的站點添加第三方登陸模塊。html
OAuth(開放受權)是一個開放標準,容許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源(如照片,視頻,聯繫人列表),而無需將用戶名和密碼提供給第三方應用。
更多關於OAuth2.0的信息請訪問 OAuth 2.0 — OAuthvue
實際使用只須要知道:java
詳細的認證過程請訪問官方文檔 Authorization options for OAuth Apps,這裏我對通常的web app請求認證的過程作一下總結。ios
用戶點擊登陸按鈕跳轉至Github提供的受權界面,並提供參數:客戶端ID(稍後會介紹到),回調頁面等。git
GET https://github.com/login/oauth/authorize
github
參數:web
名稱 | 類型 | 描述 |
---|---|---|
client_id |
string |
必需。GitHub的客戶端ID 。 |
redirect_uri |
string |
回調地址,默認返回申請時設置的回調地址。 |
scope |
string |
以空格分隔的受權列表。eg:user repo 不提供則默認返回這兩種。 |
state |
string |
客戶端提供的一串隨機字符。它用於防止跨站請求僞造攻擊。 |
allow_signup |
string |
若是用戶未註冊Github,是否提供註冊相關的信息,默認是true 。 |
客戶端以POST的方式訪問GIthub提供的地址,提供參數code等。ajax
POST https://github.com/login/oauth/access_token
vue-cli
參數:
名稱 | 類型 | 描述 |
---|---|---|
client_id |
string |
必需。GitHub的客戶端ID 。 |
client_secret |
string |
必需。Github提供的一串隨機字符串。 |
code |
string |
必需。上一步收到的code |
redirect_uri |
string |
以前提供redirect_uri |
state |
string |
以前提供的state |
Github返回數據, 包含accesstoken。根據不一樣的Accept標頭返回不一樣格式,推薦json。
Accept: application/json {"access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", "scope":"repo,gist", "token_type":"bearer"}
客戶端以GET的方式訪問Github提供的地址,在參數中加入或在head中加入accesstoken。
GET https://api.github.com/user?access_token=...
curl -H "Authorization: token OAUTH-TOKEN" https://api.github.com/user
大部分的第三方登陸都參考了Github的認證方法。
不用多說,Vue.js。這裏我主要總結一下第三方登陸組件的設計流程。
以博客系統爲例,可分爲三類:
組件的職能可歸納爲如下幾點:
code
和正確state
出現。若是出現開始身份認證。更全面的,出於方便考慮以及Auth2.0的特性,accesstoken
能夠存放至cookie以實現必定時間內免登錄且不用擔憂密碼泄露。響應的在用戶認證成功和登出時須要對cookie進行設置和清除操做。
那麼開始最主要組件auth
的編寫
首先進行準備工做,訪問Github -> settings -> Developer settings 填寫相關信息建立 Oauth App。
注意: 此處設置的 Authorization callback URL
即爲客戶端的回調頁面。客戶端申請攜帶的參數與這個地址不一樣會報相應的錯誤。
獲得client信息後就能夠在auth
組件內設置字段了。
" @/components/GithubAuth.vue " data () { return { client_id: 'your client ID', client_secret: 'your client secret', scope: 'read:user', // Grants access to read a user's profile data. state: 'your state', getCodeURL: 'https://github.com/login/oauth/authorize', getAccessTokenURL: '/github/login/oauth/access_token', getUserURl: 'https://api.github.com/user', redirectURL: null, code: null, accessToken: null, signState: false } }
模板中加入登陸按鈕, 保存以前的地址至cookie以便登陸後回調,跳轉至受權頁面。
<a v-if="!signState" href="#" v-on:click="saveURL">登陸</a>
saveURL: function () { if (Query.parse(location.search).state !== this.state) { this.$cookie.set('redirectURL', location.href, 1) location.href = this.getCodeURL } }
A Vue.js plugin for manipulating cookies. --- vue-cookieParse and stringify URL. ---query strings
組件建立後,檢查地址欄是否存在有效code
。若是存在則進行相應處理,獲取有效accesstoken
存入cookie,頁面回調至登陸以前保存的地址。 若是不存在則檢查cookie內是否存在accesstoken
獲取用戶信息。
注意: 須要計算獲得的屬性務必在computed
下定義。
computed: { formatCodeURL: function () { return this.getCodeURL + ('?' + Query.stringify({ client_id: this.client_id, scope: this.scope, state: this.state })) } }
created: function () { this.getCode() // when code in url if (this.code) this.getAccessToken() else { // if no code in top, get accessToken from cookie this.accessToken = this.$cookie.get('accessToken') if (this.accessToken) this.getUser() } }
獲取地址欄攜帶的code
參數的處理:getCode()
getCode: function () { this.getCodeURL += ('?' + Query.stringify({ client_id: this.client_id, scope: this.scope, state: this.state })) let parse = Query.parse(location.search) if (parse.state === this.state) { this.code = parse.code } }
利用code
獲取accesstoken
的處理: getAccessToken()
getAccessToken: function () { this.axios.post(this.getAccessTokenURL, { client_id: this.client_id, client_secret: this.client_secret, code: this.code, state: this.state }).then((response) => { this.accessToken = response.data.access_token if (this.accessToken) { // save to cookie 30 days this.$cookie.set('accessToken', this.accessToken, 30) this.redirectURL = this.$cookie.get('redirectURL') if (this.redirectURL) { location.href = this.redirectURL } } }) }
A small wrapper for integrating axios to Vuejs. --- vue-axios
要說的是,由於axios是基於promise的異步操做,因此使用時應當特別注意。頁面跳轉放在回調函數裏是爲了防止promise還未返回時頁面就發生跳轉。
重要 :包括ajax
,fetch
在內的向後臺提交資源的操做都存在跨域問題。瀏覽器同源政策及其規避方法(阮一峯)。 這裏利用了代理的方法,使用vue-cli時可經過配置文件臨時設置代理規避跨域問題。在生產環境下須要配置服務器代理至其餘域名。
" $/config/index.js " proxyTable: { '/github': { target: 'https://github.com', changeOrigin: true, pathRewrite: { '^/github': '/' } }
/github
會在請求發起時被解析爲target
。設置完成後中斷熱重載從新編譯,從新編譯,從新編譯。
利用accesstoken
獲取用戶信息的處理:getUser()
getUser: function () { this.axios.get(this.getUserURl + '?access_token=' + this.accessToken) .then((response) => { let data = response.data this.signState = true // call parent login event this.$emit('loginEvent', { login: data.login, avatar: data.avatar_url, name: data.name }) }) // invaild accessToken .catch((error) => { console.log(error) this.$cookie.delete('accessToken') }) }
請求用戶信息成功後觸發了loginEvent
事件,並以當前用戶信息做爲參數傳遞出去。
用戶登出的處理: logout()
<a v-else v-on:click="logout" href="#">註銷</a>
logout: function () { this.$cookie.delete('accessToken') this.signState = false this.$emit('logoutEvent') }
清理cookie,觸發用戶登出事件。
引入auth組件並註冊爲子組件。
import GithubAuth from '@/components/GithubAuth' Vue.component('auth', GithubAuth)
設置子組件觸發事件後的處理函數
<auth v-on:loginEvent="login"v-on:logoutEvent="logout"></auth>
methods: { login: function (user) { this.user = user }, logout: function () { this.user = null } }
初始化一個空的user字段後等待auth
喚起事件,改變user字段的值。
data () { return { user: null } }
由於Vue.js是響應式的,props
中設置user字段,初始化時傳入便可。
<router-view v-bind:user="user"></router-view>
props: ['user']
以上僅是我我的總結出的一些方法,若有更好的解決方法或是錯誤的地方請指出,感激涕零!