提示:koa + vue + axios
前言
不理解具体的token保持登录的原理,所以研究了一下,做下总结
一、jwt
什么是jwt:
写了半天官方解释,感觉不太好理解,表述下自己的理解,如果想看官方下方有地址。
JWT是一个标准,把json格式的数据进行加密进行传输;流程大概如下:
1、客户端登录,发送用户信息给服务器,服务器验证用户信息成功后,生成一个token,返回给客户端
2、客户端接收token,存储在本地,之后所有的请求都带上token
3、客户端发送其他(非注册登录)的请求携带token
4、服务器接收到请求后,验证token,这里是用jwt的规则对token进行解析,解析token是否过期,解析token中用户的信息
5、如果token仍在有效期内,并且通过服务器自定义的secret key与加密方式,可以解析出用户信息,服务器执行接收到的请求
优点:不用每次都操作数据库验证用户信息
缺点:token是存储在客户端的,在传输过程中,可能被获取,存再风险,传输中用https,服务器自定义的secret key不能泄露,
token示例展示:
//token结构组成
header.payload.signature
//示例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 //header
.eyJ1c2VyRGF0YSI6eyJ1c2VyTmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiIxMjM0NTYifSwiaWF0IjoxNjY3OTg0NzI0LCJleHAiOjE2Njc5ODgzMjR9 //Payload
.LAPHZoqFDfVUijJRv7CZVrEF32LVOktUzPqFoOWfPa8 //Signature
二、token的生成和验证
一、 前端代码
1、新建项目test
//进入test项目后执行
vue init webpack test
npm install js-cookie
npm install axios
//启动项目
npm run dev
2、HellowWorld.vue
<template>
<div class="hello">
<div v-if="noLogin" class="loginBox">
<p class="ipt"><input v-model="userName" type="text"></p>
<p class=""><input v-model="password" type="password"></p>
<button @click="goLogin">登录</button>
</div>
<div v-else class="testBox">
<button @click="goTest">发送带token的请求</button>
</div>
</div>
</template>
<script>
import requestApi from "@/api/request";
import Cookies from 'js-cookie'
export default {
name: "HelloWorld",
data() {
return {
noLogin: true,
userName: "",
password: "",
};
},
methods: {
goLogin() {
requestApi.login({userName:this.userName,password:this.password}).then((res) => {
Cookies.set('token',res.data.token);
this.noLogin = false;
});
},
goTest() {
requestApi.test({test:'执行test'}).then((res) => {
console.log(res)
});
},
},
};
</script>
3、api/request.js
import axios from 'axios';
import Cookies from 'js-cookie'
const token = Cookies.get('token');
const request = axios.create({
baseURL: 'http://localhost:3000',
withCredentials: true,
headers: {'Content-Type': 'application/json'},
});
if(token){
request.defaults.headers.common['Authorization'] = 'Bearer '+ token;
}
request.interceptors.request.use(
config => {
return config;
}, err => {
return Promise.reject(err);
});
request.interceptors.response.use((data) => {
return data.data
}, (err) => {
return err
});
const resquestApi = {
login(params) {
return request.post('/api/login/login', params);
},
test(params){
return request.post('/api/login/test', params);
}
}
export default resquestApi;
二、 后端代码
1、新建项目testkoa
//新建文件夹testkoa
//进入testkoa项目后执行
npm init -y
npm install koa koa-bodyparser koa-static koa-session-minimal koa-cors jsonwebtoken koa-router
node main.js
1、main.js
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const staticServe = require('koa-static');
const session = require('koa-session-minimal');
const cors = require('koa-cors');
//token
const jwt = require('jsonwebtoken');
const TOKEN_SECRET_KEY = '加密key';
//router
const router = require('koa-router')();
const app = new Koa();
app.use(cors({
origin: 'http://localhost:8080',
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization', 'Accept'],
credentials: true
}));
app.use(session({
key: 'sesson_id',
cookie: {
maxAge: 1000 * 60 * 1, // cookie有效时长
path: '/', // 写cookie所在的路径
domain: 'localhost', // 写cookie所在的域名-------这里不加也可以
httpOnly: true, // 是否只用于http请求中获取
overwrite: false, // 是否允许重写
}
}))
const sign_token = (userData) => {
//web传入参数,加密key,过期时间
return jwt.sign(userData, TOKEN_SECRET_KEY, {
expiresIn: '1h'
});
}
const check_token = async (ctx) => {
if (!ctx.header.authorization || ctx.header.authorization.length < 1) {
return
}
const token = ctx.header.authorization.split('Bearer ')[1];
//"未登录,请前往登录!"
if (!token || token.length < 1) return false;
try {
// 或者用 //let userData = await jwt.decode(token);
let userData = await jwt.verify(token, TOKEN_SECRET_KEY);
return userData;
} catch (err) {
//登录过期,请前往登录!
return false
}
}
const test = async (ctx) => {
const result = await check_token(ctx);
let body = {
success: false,
msg: '登录信息验证失败',
};
if (result) {
//执行实际当前接口请求的操作就可以,不需要去数据库验证用户信息
body = {
success: false,
msg: 'test请求成功',
data:result
};
}
ctx.body = body;
}
const login = (ctx) => {
let userData = ctx.request.body;
let reObj = {
userName: userData.userName
}
reObj.token = sign_token({
userName: userData.userName
});
ctx.body = {
success: true,
msg: '登录成功!',
data: reObj
}
}
//登录
router.post(`/api/login/login`, login);
//测试请求验证token
router.post(`/api/login/test`, test);
app.use(bodyParser());
app.use(staticServe(__dirname + '/public'));
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000, () => {
console.log('localhost:3000');
});
三、页面效果
1、初始化页面,cookie没有任何值
2、执行login请求,headers无token信息
执行login请求,payload传参
执行login请求,返回token信息,存储到cookie
cookie此时有了token信息
执行test请求,headers中包含了token的信息
执行test请求,payload传参
执行test请求,返回token解析后的json数据
四、官方解释
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。此信息可以被验证和信任,因为它是经过数字签名的。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
JSON Web Token由3部分组成:
1、Header
2、Payload
3、Signature
类似于下列结构
xxxxx.yyyyy.zzzzz
header.payload.signature
1、header
头部通常由两部分组成:令牌类型(JWT)和使用的签名算法(如HMAC SHA256或RSA)
{
"alg": "HS256",
"typ": "JWT"
}
2.Payload
令牌的第二部分是有效载荷,其中包含声明。声明是关于实体(通常是用户)和其他数据的声明。索赔有三种类型:注册索赔、公共索赔和私人索赔。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
3.Signature
要创建签名部分,您必须获取编码的头、编码的有效载荷、秘密、头中指定的算法,并对其进行签名。
例如,如果您想使用HMAC SHA256算法,签名将按以下方式创建:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
总结
踩坑路漫漫长@~@