Vue使用OAuth2簡化模式登入

SachinLea發表於2019-03-04

1. OAuth2簡化模式概述

  OAuth2是一個基於令牌的安全框架,主要使用在第三方認證登入場景,關於OAuth2的相關知識可以參考——理解OAuth 2.0,這裡暫不詳細介紹。   這裡簡單介紹一下簡化模式,簡化模式,可以通過客戶端名稱和一個redirect_uri,訪問認證伺服器,認證伺服器認證之後,直接返回一個令牌,該令牌會在redirect_uri的後邊使用#連線。例如:

// 請求url
GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=http://localhost:9001/callback HTTP/1.1
    Host: server.example.com
 
// 認證之後,重定向結果:
HTTP/1.1 302 Found
Location: http://localhost:9001/callback#access_token=2YotnFZFEjr1zCsicMWpAA
          &state=xyz&token_type=example&expires_in=3600
複製程式碼

  從上邊的結果可以看出,簡化模式最後的令牌資訊,直接在url中展示,其中url的Hash部分包含了令牌資訊,因此一般客戶端可以從Hash中獲取令牌資訊,用於下一次訪問。
  Spring Cloud微服務系統中,多個服務基本都需要進行身份認證,因此,可以使用OAuth2的認證授權方式,單獨建立一個使用者認證的微服務,解決整個系統的認證問題。如果只是微服務系統,我們可以使用密碼模式,來做一個登陸頁面,輸入使用者名稱和密碼,可以看起來像一個完成的系統。
  但是,考慮到可能會和其他系統配合使用的情況,決定做成一個單點登陸的模式,所有的系統認證時,都去認證伺服器登陸,認證伺服器自己提供一個登陸介面。

2. Vue使用簡化模式

  根據OAuth2的簡化模式的相關特性,如果Vue使用該模式,可以將請求的redirect_uri設定為Vue的根目錄,然後,根據當前請求路徑的Hash獲取令牌資訊。
  實現方法,定義全域性路由守衛:使用Vue的全域性路由守衛,如果沒有token的話,從請求中獲取Hash,如果Hash中的token資訊為空,則去認證伺服器認證:

import router from './router'
import store from './store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css'// progress bar style
import { getToken,setToken } from '@/utils/auth' // getToken from cookie
import {hasPermission} from '@/utils/hasPerm'
import {validateIsNull} from "./utils/validate";

NProgress.configure({showSpinner: false})

router.beforeEach((to, from, next) => {
  NProgress.start()
  // 獲取儲存的 token資訊
  let token = getToken()
  let hasToken = token !== 'undefined' && token !== undefined && token !== null && token !== ''

  if (hasToken) {
    // 如果有token,再做許可權判斷等操作,這裡暫時忽略
    ......
  } else {
  	// <1> 獲取 location 中的hash資訊,也就是 #後邊的內容
    let tokenStr = location.hash
	// <2> 如果,hash中不包含令牌,去認證伺服器認證
    if (tokenStr.indexOf('access_token') === -1) {
      window.location.href = process.env.AUTH_URI+ '/oauth/authorize?client_id=mobile-client&redirect_uri='+
        process.env.REDIRECT_URI+'&response_type=token'
    } else {
    // <3> 如果有令牌資訊,處理 hash,獲取其中的token,並儲存
      let tokenMap = new Map()
      let tokenArray = tokenStr.substr(2).split('&')
      tokenArray.forEach(str => {
        let strArr = str.split('=')
        tokenMap.set(strArr[0], strArr[1])
      })
      setToken(tokenMap.get('access_token'))
      store.dispatch('SET_TOKEN', tokenMap.get('access_token'))
      // <4> 將路由再次跳轉到主介面,主要是去掉hash中的內容
      router.push({path: '/'})

      NProgress.done()
    }

  }
})

router.afterEach(() => {
  NProgress.done()
})
複製程式碼

假如Vue設定埠是9527,那麼,認證之後請求的路徑會是http://localhost:9527/#/access_token=......; 而我們知道vue-router預設使用 hash 模式,也會有一個#,因此

  • <1> 處,獲取locationhash 內容。
  • <2> 處,需要使用window.location.href,讓瀏覽器訪問該路徑,而認證伺服器地址和前端專案地址可能生產和開發環境不同,為了避免釋出修改程式碼,因此,我將其配置在了config目錄下的檔案中,暫時想到該方法。
  • <3> 處,根據獲取的hash資訊,擷取其中的令牌資訊,也就是access_token對應的值,並儲存該值。
  • <4> 處,使用router.push({path: '/'})的主要原因在於,認證之後返回形如http://localhost:9527/#/access_token=......的路徑,該路徑會和vue的路由進行匹配,很明顯不會配置改形式的路由,因此,使用此方式,將後邊的資訊給清除。

相關文章