vue+koa2實現(xiàn)session、token登陸狀態(tài)驗證的示例
Session 登陸與 Token 登陸的區(qū)別
1、Session 登陸是在服務(wù)器端生成用戶相關(guān) session 數(shù)據(jù),發(fā)給客戶端 session_id 存放到 cookie 中,這樣在客戶端請求時帶上 session_id 就可以驗證服務(wù)器端是否存在 session 數(shù)據(jù),以此完成用戶認(rèn)證。這種認(rèn)證方式,可以更好的在服務(wù)端對會話進(jìn)行控制,安全性比較高(session_id 隨機(jī)),但是服務(wù)端需要存儲 session 數(shù)據(jù)(如內(nèi)存或數(shù)據(jù)庫),這樣無疑增加維護(hù)成本和減弱可擴(kuò)展性(多臺服務(wù)器)。 CSRF 攻擊一般基于 cookie。另外,如果是原生 app 使用這種服務(wù)接口,因為沒有瀏覽器 cookie 功能,所以接入會相對麻煩。
2、基于 token 的用戶認(rèn)證是一種服務(wù)端無狀態(tài)的認(rèn)證方式,服務(wù)端不用存放 token 數(shù)據(jù)。用戶驗證后,服務(wù)端生成一個 token(hash 或 encrypt)發(fā)給客戶端,客戶端可以放到 cookie 或 localStorage 中,每次請求時在 Header 中帶上 token,服務(wù)端收到 token,通過驗證后即可確認(rèn)用戶身份。這種方式相對 cookie 的認(rèn)證方式就簡單一些,服務(wù)端不用存儲認(rèn)證數(shù)據(jù),易維護(hù)擴(kuò)展性強(qiáng),token 存在 localStorage 可避免 CSRF,web 和 app 應(yīng)用都比較簡單。不過這種方式在加密或解密的時候會有一些性能開銷(好像也不是很大),有些對稱加密存在安全隱患(aes cbc 字節(jié)翻轉(zhuǎn)攻擊)。
koa + session 登陸驗證
1、首先要安裝 koa-sisson 包
npm install koa-session -S
koa-session 實際上是通過 cookie 來保存信息的。koa-session 在服務(wù)器上生成一個信息,通過加密后,以 cookie 的形式發(fā)送到用戶的瀏覽器,在用戶瀏覽器上可以看到是一個加密的 cookie 字段,然后在服務(wù)端路由中通過 ctx.session.xx 來獲取 xx 這個信息。而當(dāng)每次當(dāng)用戶發(fā)送請求時候,就可以獲取到這個 cookie,koa-sesscion 會內(nèi)部會幫我們解密為最初的存儲的信息,于是我們可以通過判斷這個 cookie 是存在來校驗用戶是否已經(jīng)登錄了。
2、app.js 中初始化
const Koa = require('koa')
const app = new Koa()
const session = require('koa-session')
const bodyParser = require('koa-bodyparser')
const Router = require('koa-router')
const router = new Router()
const CONFIG = {
key: 'koa:sess', /** (string) cookie key (default is koa:sess) cookie 的Name */
/** (number || 'session') maxAge in ms (default is 1 days) */
/** 'session' will result in a cookie that expires when session/browser is closed */
/** Warning: If a session cookie is stolen, this cookie will never expire */
maxAge: 86400000, /** cookie 的過期時間 */
autoCommit: true, /** (boolean) automatically commit headers (default true) */
overwrite: true, /** (boolean) can overwrite or not (default true) */
httpOnly: true, /** (boolean) httpOnly or not (default true) */
signed: true, /** (boolean) signed or not (default true) */
rolling: false, /** (boolean) Force a session identifier cookie to be set on every response. The expiration is reset to the original maxAge, resetting the expiration countdown. (default is false) */
renew: false, /** (boolean) renew session when session is nearly expired, so we can always keep user logged in. (default is false)*/
}
app.keys = ['login secret'] // 加密密鑰
app.use(session(CONFIG, app));
app.use(bodyParser())
app.use(router.routes()).use(router.allowedMethods())
3、登陸路由
router.post('/login', async (ctx) => {
try {
const data = ctx.request.body.data
const { username, password } = data
if (true) {
// 保存登錄狀態(tài),這句代碼會在瀏覽器中生成一個以 "koa:sess" 為 Name 的 cookie
ctx.session.userInfo = {username: '', userID: ''}
ctx.body = {code: 1, message: '登陸成功'}
} else {
ctx.body = {code: 0, message: '賬號或密碼錯誤'}
}
} catch(err) {
throw new Error(err)
}
})
// 前端
axios.post('/login', {username: '', password: ''}).then(res => {})
4、校驗是否已登陸
router.get('/getSession', async (ctx) => {
try {
if (ctx.session.userInfo) {
ctx.body = {code: 1, message: '已登陸'}
} else {
ctx.body = {code: 0, message: '未登陸'}
// 跳轉(zhuǎn)到登錄頁
// ctx.response.redirect('/login')
}
} catch(err) {
throw new Error(err)
}
})
// 前端
axios.get('/getSession').then(res => {})
5、退出登陸
router.post('/logout', async (ctx) => {
try {
// 將登錄信息清空
ctx.session = null
// 跳轉(zhuǎn)到登錄頁或網(wǎng)站首頁
ctx.response.redirect('/')
} catch(err) {
throw new Error(err)
}
})
// 前端
axios.post('/logout').then(res => {})
koa + token 登陸驗證
1、安裝 jsonwebtoken 包
npm install jsonwebtoken -S
2、app.js 初始化
const Koa = require('koa')
const app = new Koa()
const jwt = require('jsonwebtoken')
const bodyParser = require('koa-bodyparser')
const Router = require('koa-router')
const router = new Router()
const tokenConfig = {privateKey: 'xxxxxxxxxxxx'} // 加密密鑰
app.use(bodyParser())
app.use(router.routes()).use(router.allowedMethods())
3、登陸路由
router.post('/login', async (ctx) => {
try {
const data = ctx.request.body.data
const { username, password } = data
if (true) {
const userInfo = {username: '', userID: ''}
const token = jwt.sign(userInfo, tokenConfig.privateKey, {expiresIn: '7d'}) // 簽發(fā) token, 7天有效期
ctx.body = {code: 1, message: '登陸成功', data: {token: 'Bearer ' + token}}
} else {
ctx.body = {code: 0, message: '賬號或密碼錯誤'}
}
} catch(err) {
throw new Error(err)
}
})
前端登陸
axios.post('/login', {username: '', password: ''}).then(res => {
if (res.data.code === 1) {
localStorage.setItem('token', res.data.data.token)
// vuex 存儲 userInfo 和登陸狀態(tài)
store.commit('SET_USERINFO', {userInfo: res.data.data.userInfo, status: true})
}
})
4、校驗是否已登陸
router.get('/getUserInfo', async (ctx) => {
try {
const token = ctx.get('Authorization') // 獲取請求 Header 中 Authorization 值
let userInfo = {}
if (token === '') {
ctx.body = {code: 0, message: '未登陸'}
} else {
try {
userInfo = jwt.verify(token.split(' ')[1], tokenConfig.privateKey) // 驗證 token
ctx.body = {code: 1, message: '已登陸', data: {userInfo: userInfo: loginStatus: true}}
} catch(err) {
// token 過期或無效
ctx.body = {code: 0, message: '未登陸', data: {userInfo: {}: loginStatus: false}}}
}
}
} catch(err) {
throw new Error(err)
}
})
要每次的請求中都帶上 token 信息,要給 axios 設(shè)置請求攔截
// 請求攔截,在每次請求中的 header 中帶上 token
axios.interceptors.request.use(config => {
let token = localStorage.getItem('token')
if (token) {
config.headers.common.Authorization = token
}
return config
}, error => {
return Promise.reject(error);
})
每次進(jìn)入頁面之前要判斷下是否已登陸,是否有權(quán)限進(jìn)入該頁面,之前我是在每個頁面的 created 鉤子函數(shù)中去請求 '/getUserInfo' 判斷是否以登陸,這樣做繁瑣,并且頁面會先呈現(xiàn)一下,然后一閃而過(驗證不過的情況下),在路由鉤子函數(shù)中可全局配置
// 路由守衛(wèi), 在跳轉(zhuǎn)之前執(zhí)行
router.beforeEach((to, from, next) => {
let token = localStorage.getItem('token')
let requireAuth = to.meta.requireAuth // VueRouter 里配置頁面是否需要登陸進(jìn)入
let root = to.meta.root // VueRouter 里配置頁面是否需要登陸且管理員權(quán)限進(jìn)入
if (!token) {
// vuex 清除 userInfo 和登陸狀態(tài)
store.commit('SET_USERINFO', {userInfo: {}, status: false})
requireAuth ? next({path: '/'}) : next()
} else {
axios.get(API.getUserInfo).then(res => {
// vuex 存儲 userInfo 和登陸狀態(tài)
store.commit('SET_USERINFO', {userInfo: res.data.userInfo, status: res.data.loginStatus})
if (requireAuth) {
if (!res.data.loginStatus || (root && !res.data.userInfo.root)) {
next({path: '/'})
} else {
next()
}
} else {
next()
}
})
}
})
/** VueRouter
{
path: '/admin',
name: 'admin',
meta: {
requireAuth: true,
root: true
},
}
*/
5、退出登陸
因為服務(wù)器端并沒有存儲用戶登陸相關(guān)信息,只與前端是否存在 token 或是否能驗證通過有關(guān),所以退出登陸就將 token 清除即可
methods: {
logout() {
localStorage.removeItem('token')
// vuex 清除登陸信息
store.commit('SET_USERINFO', {userInfo: {}, status: false})
if (this.$route.path !== '/') {
this.$router.push({path: '/'})
}
}
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- vue實現(xiàn)登陸登出的實現(xiàn)示例
- Vue2.0 axios前后端登陸攔截器(實例講解)
- vue+axios新手實踐實現(xiàn)登陸的示例代碼
- 詳解用vue.js和laravel實現(xiàn)微信授權(quán)登陸
- 基于vue-cli3和element實現(xiàn)登陸頁面
- Vue 前端實現(xiàn)登陸攔截及axios 攔截器的使用
- 詳解vue2.0+axios+mock+axios-mock+adapter實現(xiàn)登陸
- 詳解springboot和vue前后端分離開發(fā)跨域登陸問題
- Vue 頁面權(quán)限控制和登陸驗證功能的實例代碼
- vue實現(xiàn)登陸功能
相關(guān)文章
vue3.0使用vue-pdf-embed在線預(yù)覽pdf 控制頁碼顯示范圍不生效問題解決
這篇文章主要介紹了vue3.0使用vue-pdf-embed在線預(yù)覽pdf 控制頁碼顯示范圍不生效問題的問題及解決方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-01-01
關(guān)于vue的element-ui web端引入高德地圖并獲取經(jīng)緯度
這篇文章主要介紹了關(guān)于vue的element-ui web端引入高德地圖并獲取經(jīng)緯度,高德地圖首先要去申請key和密鑰,文中提供了部分實現(xiàn)代碼和解決思路,感興趣的朋友可以學(xué)習(xí)一下2023-04-04
詳解vue數(shù)組遍歷方法forEach和map的原理解析和實際應(yīng)用
這篇文章主要介紹了詳解vue數(shù)組遍歷方法forEach和map的原理解析和實際應(yīng)用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11
vue3數(shù)據(jù)可視化實現(xiàn)數(shù)字滾動特效代碼
這篇文章主要介紹了vue3數(shù)據(jù)可視化實現(xiàn)數(shù)字滾動特效,實現(xiàn)思路是使用Vue.component定義公共組件,使用window.requestAnimationFrame(首選,次選setTimeout)來循環(huán)數(shù)字動畫,詳細(xì)代碼跟隨小編一起看看吧2022-09-09
Vue2.0實現(xiàn)組件數(shù)據(jù)的雙向綁定問題
這篇文章主要介紹了Vue2.0實現(xiàn)組件數(shù)據(jù)的雙向綁定問題,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2018-03-03

