起因
http是无状态协定,须要晓得是哪一个用户,因而须要一种验证机制。随着技术的倒退,分布式web利用的遍及,通过session治理用户登录状态老本越来越高,因而缓缓倒退成为token的形式做登录身份校验,而后通过token去取redis中的缓存的用户信息,随着之后jwt的呈现,校验形式更加简略便捷化,无需通过redis缓存,而是间接依据token取出保留的用户信息,以及对token可用性校验,单点登录更为简略。具体认证的实现过程如下:
什么是jwt
JSON Web Token (JWT)是一个凋谢规范(RFC 7519),它定义了一种紧凑的、自蕴含的形式,用于作为JSON对象在各方之间平安地传输信息。该信息能够被验证和信赖,因为它是数字签名的。
jwt的组成
JWT是由三段信息形成的,将这三段信息文本用.链接一起就形成了Jwt字符串。就像这样:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一局部咱们称它为头部(header),第二局部咱们称其为载荷(payload, 相似于飞机上承载的物品),第三局部是签证(signature).
jwt加密流程
1.前端发动登录申请
login({ commit }, userInfo) { const { username, password } = userInfo return new Promise((resolve, reject) => { login({ username: username.trim(), password: password }).then(response => { commit('SET_TOKEN', response.token) setToken(response.token) resolve() }).catch(error => { reject(error) }) }) }
2.后端承受参数进行登录校验
this.app.jwt.sign({ username: username }, this.app.config.jwt.web.secret, { expiresIn: expiresIn })
如上后端代码应用了nodejs的egg框架,应用的是egg-jwt插件进行加密和解密;其中代码中的username为用户账号(id),secret则为加密的盐值(解密须要用到),expiresIn则为生成的token的无效工夫。账号密码验证通过后,你能够自定你的加密内容(payload),其中示例代码退出了username。当然你还能够退出你的userInfo等,并且将生成的toekn返回到前端(思考:很多编程老手喜爱在登录胜利之后间接返回了用户的信息,作者是不举荐这样做的;这里咱们只返回token,前端拿到返回的token存储起来)
3.登陆胜利后前端贮存token
login({ username: username.trim(), password: password }).then(response => { commit('SET_TOKEN', response.token)//登录胜利之后存储token,你能够存在vuex或者local中 setToken(response.token) resolve() }).catch(error => { reject(error) })
jwt解密流程
1.前端发动申请,axios申请拦截器设置headers
service.interceptors.request.use( config => { // do something before request is sent if (store.getters.token) { // let each request carry token // ['X-Token'] is a custom headers key // please modify it according to the actual situation config.headers['Authorization'] = getToken() } return config }, error => { // do something with request error console.log(error) // for debug return Promise.reject(error) })
作者这里应用的是axios库配合拦截器,具体应用参考:https://www.jianshu.com/p/86122178002a
2.服务端解析headers中的token
const { Status, Code } = ctx.app.config if (ctx.get('Authorization')) { //拿出token let token = ctx.get('Authorization'); try { //解密token ctx.app.jwt.verify(token, secret); } catch (error) { if (error.name == 'TokenExpiredError') { //token过期啦 ctx.responseMsg(Status.FAIL.UNAUTHORIZED, { msg: Code.FAIL.TOKEN_EXPIRED.MSG }); return; } else { ctx.responseMsg(Status.FAIL.UNAUTHORIZED, { msg: Code.FAIL.TOKEN_EXPIRED.MSG }); return; } } await next(); } else { //不非法的申请 ctx.responseMsg(Status.FAIL.UNAUTHORIZED, { msg: Code.FAIL.ILLEGAL_TOKEN.MSG }); return; }
token:申请头中获取到的token
secret:盐值,和加密的保持一致
其中对于jwt的解密工作应用了中间件,对于egg中间件参考:https://eggjs.org/zh-cn/basics/middleware.html
3.服务端依据返回的约定code做出相应的响应
service.interceptors.response.use( response => { // if the custom code is not 20000, it is judged as an error. const res = response.data; if (res.code !== 20000) { Message({ message: res.msg || 'Error', type: 'error', duration: 5 * 1000 }) } else { return res } }, error => { //console.log(error) // for debug Message({ message: error.message, type: 'error', duration: 5 * 1000 }) if (error.response.status == 401) { MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { confirmButtonText: 'Re-Login', cancelButtonText: 'Cancel', type: 'warning' }).then(() => { store.dispatch('user/resetToken').then(() => { location.reload() }) }) } else if (error.response.status == 422) { } return Promise.reject(error) })
其中的code是依据和后端人员约定标准的,能够参考restful格调:https://zhuanlan.zhihu.com/p/66148320?utm_source=wechat_session&utm_medium=social&utm_oi=986957734650208256
如上即可实现基于jwt认证(第一次写,写得不好大佬们下手轻点)