token流程;
- 前端使用用戶名、密碼來請求服務器
- 服務器通過數據庫查詢等操作驗證用戶的信息
- 服務器通過驗證發送給用戶一個token
- 前端存儲token(cookie、sessionStorage、loaclStorage),並在每次請求時附送上這個token值
- 後臺驗證token值,並返回數據
- token會有過期,前端登出的時候直接清空本地token,服務端不需要任何操作
session VS token:
- session要求服務端存儲信息,並且根據id能夠檢索,而token不需要。在大規模系統中,對每個請求都檢索會話信息可能是一個複雜和耗時的過程。但另外一方面服務端要通過token來解析用戶身份也需要定義好相應的協議。
- session一般通過cookie來交互,而token方式更加靈活,可以是cookie,也可以是其他header,也可以放在請求的內容中。不使用cookie可以帶來跨域上的便利性。
- token的生成方式更加多樣化,可以由第三方服務來提供
詳細session:開始一個koa2 -(4)-session
那麼開始
本文前後端完全分離。前端通過ajax請求後端數據;不再使用模板引擎;
假設你已經用vue-cli3 製作了一個前端項目E:\myKun\yuntumap-web;這個項目的後臺語言可以是php、java、.net、還有c#等等;
在原有的前端項目中我們來嘗試寫一個koa2語言的後臺與之匹配;
上圖;
而後端koa2的目錄;結合前幾節課的知識點;
好現在開始。
1運行你的前端項目;運行後瀏覽器自動打開 http://localhost:8080
2運行後臺koa2-test ;因爲都在本地運行的;所以 確保後端和前端的端口不一致;比如3000;
3安裝 koa-cors 他可以解決跨域問題;
npm install koa-cors --save
在app.js 中
// CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。
// 下面以koa2-cors爲例,
const cors = require('koa-cors');
// 具體參數我們在後面進行解釋
app.use(cors({
origin: function (ctx) {
if (ctx.url === '/test') {
return "*"; // 允許來自所有域名請求
}
return 'http://localhost:8080'; // 這樣就能只允許 http://localhost:8080 這個域名的請求了
},
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
maxAge: 5,
credentials: true,
allowMethods: ['GET', 'POST', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization', 'Accept'],
}))
這裏插個題外話,koa2每次修改代碼都重啓一次太麻煩;我們安裝nodemon ,熱啓動;
npm install --save-dev nodemon
在package.json 中修改
"scripts": {
"start": "nodemon ./bin/www"
},
現在我們回到前端;
將請求的根路徑改成
let rootUrl = 'http://localhost:3000'
ajax每次請求;
這個ajax我自己按照喜好封裝起來了。具體vue插件/公共方法屬性
所以登錄的ajax使用;
login(){
let data = {
account: this.account,
password: md5(this.password),
};
this.myAjax('/api/AlarmHandle/GetLogin', data).then(res=>{
this.toast(res.ReturnMsg);
this.config.setItem('token', res.Token)
this.config.setItem('account',this.account);
this.mytime= setTimeout(()=>{
this.goHome();
},1000)
})
}
好,回到後端;
1安裝token相關中間件
jsonwebtoken,服務認證生成一個json對象;
koa-jwt 主要提供路由權限控制的功能,它會對需要限制的資源請求進行檢查;
在app.js中
const koajwt = require('koa-jwt');
//通過 app.use 來調用該中間件,並傳入密鑰 {secret: 'my_token'},unless 可以指定哪些 URL 不需要進行 token 驗證。
app.use(koajwt({
secret: config.secret
}).unless({
path: [/\api\/AlarmHandle\/GetLogin/]
}));
新建目錄token/createToken.js
/**
* Created by Administrator on 2018/12/4 0004.
*/
const jwt = require('jsonwebtoken');
const config=require('../config')
module.exports = function(name){
//生成token
//第一個參數是載荷,用於編碼後存儲在 token 中的數據
//第二個是密鑰,自己定義的,驗證的時候也是要相同的密鑰才能解碼
//第三個是options,可以設置 token 的過期時間
const token = jwt.sign({name: name}, config.secret, {expiresIn: '10s'});
console.log(token)
return token;
};
新建目錄token/checkToken.js
const jwt = require('jsonwebtoken');
const util = require('util')
const verify = util.promisify(jwt.verify) // 解密
const config=require('../config')
//檢查token是否過期
module.exports = async (token) =>{
if (token) {
let payload
try{
payload = await verify(token, config.secret) // // 解密,獲取payload
console.log(payload,"payload")
}catch (err){
return false
}
return true
} else {
return false;
}
};
在 myrouter/login.js
const router = require('koa-router')();
const sql = require('../async-db/sql.js')
let createToken = require('../token/createToken.js')
let checkToken = require('../token/checkToken.js')
router.get('/api/AlarmHandle/GetLogin', async (ctx, next) => {
let name = ctx.query.account;
let pass = ctx.query.password;
await sql.findUserData(name)
.then(res=> {
if (res.length != 0) {
if (pass === res[0]['pass']) {
console.log('登錄成功')
ctx.response.body = {
ReturnCode: "SUCCESS",
ReturnMsg: '登錄成功',
Token: createToken(name)
}
} else {
ctx.response.body = {
ReturnCode: "FAIL",
ReturnMsg: '密碼錯誤',
}
}
} else {
ctx.response.body = {
ReturnCode: "FAIL",
ReturnMsg: '賬號不存在',
}
console.log('登錄失敗')
}
next();
})
})
假設MySQL中已經存在;
登錄成功後,前端再次請求服務器,需要把token返回給後臺;
所以。在myrouter/login.js中再加入前端請求的新路由/api/AlarmHandle/GetAlarmOrder
router.get('/api/AlarmHandle/GetAlarmOrder', async (ctx, next) => {
let token = ctx.query.Token
let istoken = await checkToken(token);
ctx.response.body = {
ReturnCode: istoken ? "SUCCESS" : 'FAIL',
ReturnMsg: istoken ? '操作成功' : 'token失效',
TokenInvalid: istoken
}
})
前端響應;
用戶登出;在本地清空儲存的token即可;
loginOut(){
localStorage.clear();
this.$router.push('/login')
}