vue jwt 认证

JWT 认证 什么是jwt?

JSON Web Token (JWT) 是目前最流行的跨域身份验证解决方案
解决问题: session 不支持分布式架构,无法支持横向扩展,只能通过数据库来保存会话数据实现共享。如果持久层失败会出现认证失败。
优点: 服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。
JWT包含了使用 . 分割的三部分
  • Header 头部
    {"alg":"HS256","typ":"JWT"} //"alg":"HS256"=>algorithm:HS256 //"tpy":"JWT"=>type:JWT

  • Payload 载荷
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
  • Signature 签名
    对前两部分(Header,Payload)的签名,防止数据篡改
    HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

    JWT作为一个令牌(token),有些场合可能会放到URL(比如api.example.com/?token=xxx)。Base64有三个字符 +、/和=,在URL中有特殊的含义,所以要被替换掉:=被省略;+替换成-,/替换成 _ 这就是Base64URL算法。
使用方式
HTTP请求的头信息Authorization字段里面
Authorization: Bearer

通过URL传输
http://www.xxx.com/pwa?token=xxxxx

POST请求放在请求体总也可
使用 vue-cli3 创建项目
vue create

一、服务端返回签发的 token
这里服务端采用的是express,第三方模块有 body-parser, jsonwebtoken
在用户登录和客户端路由跳转时分别进行token的发送和权限的校验
//登录时根据用户名返回对应的token app.post('/login',(req,res)=>{ let {username} = req.body; if(username === 'admin'){ // 如果访问的是admin 种植cookie res.json({ code:0, username:'admin',//这里的secret是密钥,我这里是定义的是一个字符串: let secret = 'siran' token:jwt.sign({username:'admin'},secret,{ expiresIn:20 }) }) }else{ res.json({ code:1, data:'用户名不存在' }) } }); //路由跳转时根据token检查权限 app.get('/validate',(req,res)=>{ let token = req.headers.authorization; jwt.verify(token,secret,(err,decode)=>{ // 验证token的可靠性 if(err){ return res.json({ code:1, data:'token失效了' }) }else{ res.json({ username:decode.username, code:0, // 给token续命 token:jwt.sign({username:'admin'},secret,{ expiresIn:20 }) }) } }); });

二、路由配置
  • Home.vue 首页
  • Profile.vue 个人中心
  • Login.vue 登录页面
export default new Router({ mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home, }, { path: '/profile', name: 'profile', component: Profile, meta: { needLogin: true }, // 路由跳转时进行校验,必须登录了才能访问当前路由组件 }, { path: '/login', name: 'login', component: Login, }, ], });

三、axios封装
这里封装axios的目的主要有以下3点:
  • 为每一个请求都设置拦截器,从而让每一个请求都可以带上 token
  • 之后在每一次请求时不用写完整请求路径,设置baseURL
  • 使整个请求响应的流程更符合Vuex的设计
import axios from 'axios'; classAjaxRequest{ constructor() { this.baseURL = process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : '/'; // 请求路径 this.timeout = 3000; // 设置超时时间 }setInterceptor(instance) { // 设置拦截器 instance.interceptors.request.use(config => { config.headers.Authorization = `${localStorage.getItem('token')}`; return config; // 让请求带上token,前提是服务端允许请求头带上 Authorization字段 }, (err) => { Promise.reject(err); }); instance.interceptors.response.use(res => res.data, (err) => { Promise.reject(err); }); }request(request) { const instance = axios.create(); const config = { baseURL: this.baseURL, timeout: this.timeout, ...request, }; // 合并配置 this.setInterceptor(instance); return instance(config); } }export default new AjaxRequest();

四、测试接口
接口文件单独写在了 api 这个目录下
import fetchData from '刚刚封装的axios'export const getTest = () => fetchData.request({ url: '/test' }); export const login = username => fetchData.request({ url: '/login', method: 'POST', data: { username, }, }); export const validate = () => fetchData.request({ url: '/validate' });

五、在Vuex中发送请求
export default new Vuex.Store({ state: { username: '', }, mutations: { setUsername(state, username) { state.username = username; }, }, actions: { async login({ commit }, username) { const r = await login(username); // 登录成功后返回用户名信息 if (r.token) { // 如果有返回token说明成功 commit('setUsername', username); // 将用户存入state中 localStorage.setItem('token', r.token); // 将token存放起来 } else { // 否则返回失败的promise return Promise.reject(r); } }, }, });

六、权限认证
在登录成功之后给我们返回token后,用户在通过路由跳转不同的路由组件时需要对用户的权限进行验证,使用路由的全局守卫来针对每一个路由进行校验
router.beforeEach(async (to, from, next) => { const isLogin = await store.dispatch('validate'); if (isLogin) { // 如果已经登录过了 if (to.name === 'login') {//如果即将跳转的路由还是登录,则跳转到profile next('/profile'); } else { next(); } } else { //如果没有登录过,则检查要跳转的路由需不需要登录权限 const flag = to.matched.some(item => item.meta.needLogin); if (flag) { next('/login'); } else { next(); } } });

【vue jwt 认证】Vuex actions中验证的方法
async validate({ commit }) { const r = await validate(); //调用api中的接口 if (r.code === 1) { return false; } commit('setUsername', r.username); localStorage.setItem('token', r.token); // 将token存放起来 return true; }

    推荐阅读