Vue使用OAuth2简化模式登录
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的路由进行匹配,很明显不会配置改形式的路由,因此,使用此方式,将后边的信息给清除。