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