Vue.js 2.0 基於OAuth2.0的第三方登陸組件

第三方登陸是如今常見的登陸方式,免註冊且安全方便快捷。javascript

本篇文章將以Github爲例,介紹如何在本身的站點添加第三方登陸模塊。html

OAuth2.0

OAuth(開放受權)是一個開放標準,容許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源(如照片,視頻,聯繫人列表),而無需將用戶名和密碼提供給第三方應用。

更多關於OAuth2.0的信息請訪問 OAuth 2.0 — OAuthvue

實際使用只須要知道:java

  1. 提供方儲存了用戶的信息,ID,Name,Email等。
  2. 客戶端經過提供方指定的頁面發起請求,獲取token。
  3. 客戶端經過token得到用戶信息。

Github

詳細的認證過程請訪問官方文檔 Authorization options for OAuth Apps,這裏我對通常的web app請求認證的過程作一下總結。ios

GIthub的具體認證流程:

  1. 用戶點擊登陸按鈕跳轉至Github提供的受權界面,並提供參數:客戶端ID(稍後會介紹到),回調頁面等。git

    GET https://github.com/login/oauth/authorizegithub

    參數web

    名稱 類型 描述
    client_id string 必需。GitHub的客戶端ID 。
    redirect_uri string 回調地址,默認返回申請時設置的回調地址。
    scope string 以空格分隔的受權列表。eg:user repo 不提供則默認返回這兩種。
    state string 客戶端提供的一串隨機字符。它用於防止跨站請求僞造攻擊。
    allow_signup string 若是用戶未註冊Github,是否提供註冊相關的信息,默認是true
  2. 經過驗證後頁面跳轉至以前提供的回調頁面,url中包含相關參數:code和state。
  3. 客戶端以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
  4. Github返回數據, 包含accesstoken。根據不一樣的Accept標頭返回不一樣格式,推薦json。

    Accept: application/json
    {"access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", "scope":"repo,gist", "token_type":"bearer"}
  5. 客戶端以GET的方式訪問Github提供的地址,在參數中加入或在head中加入accesstoken。

    GET https://api.github.com/user?access_token=...
    curl -H "Authorization: token OAUTH-TOKEN" https://api.github.com/user
  6. Github返回用戶的json數據。

大部分的第三方登陸都參考了Github的認證方法。

Vue.js

不用多說,Vue.js。這裏我主要總結一下第三方登陸組件的設計流程。

組件

以博客系統爲例,可分爲三類:

  1. 主頁,全部組件的parent。
  2. 身份認證組件,需解耦,至少要喚起登陸和登出事件。
  3. 其餘須要身份認證的組件。

身份認證組件(auth)

組件的職能可歸納爲如下幾點:

  1. 未登錄狀態時顯示登錄按鈕,登錄狀態時顯示註銷按鈕。
  2. 點擊登陸按鈕時,頁面發生跳轉。
  3. 監視地址欄有無code和正確state出現。若是出現開始身份認證。
  4. 認證成功喚起登陸事件並將用戶信息傳遞出去。
  5. 用戶點擊登出喚起登出事件。

更全面的,出於方便考慮以及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-cookie

Parse 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還未返回時頁面就發生跳轉。

重要 :包括ajaxfetch在內的向後臺提交資源的操做都存在跨域問題。瀏覽器同源政策及其規避方法(阮一峯)。 這裏利用了代理的方法,使用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,觸發用戶登出事件。

主頁(app)

引入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']

結束語

以上僅是我我的總結出的一些方法,若有更好的解決方法或是錯誤的地方請指出,感激涕零!

相關文章
相關標籤/搜索