目录
浏览器保存用户信息
路由导航守卫
路由嵌套
web会话跟踪机制
JWT生成token(json wen token)
起源
传统的session认证
基于session认证所暴露的问题
基于token的鉴权机制
JWT的主要应用场景
优点:
JWT的构成
第一部分:头部(header)
base64
第二部分:载荷(payload,用户的信息)
第三部分:签证(signature)
JWT搭建
引入JWT依赖,由于是基于java,所以需要的是java-jwt
创建生成token的方法
JWT组件
验证token是否有效
axios请求拦截
token过滤器
响应拦截器
获得token中playload部分数据
浏览器保存用户信息
会话期间存储,关闭浏览器后数据就会销毁
sessionStorage.setItem();
//在前端浏览器中存储用户信息
//会话期间存储,关闭浏览器后数据就会销毁
sessionStorage.setItem("account",resp.data.result.account);
sessionStorage.setItem("gender",resp.data.result.gender);
sessionStorage.setItem("phone",resp.data.result.phone);
效果
localStorage.setItem("","");
本地存储,即使关闭浏览器,下次打开还是存在,可以长久保存
路由导航守卫
目前除了访问呢login.vue是不需要登录,除此之外的组件,都必须要登录后才能访问
使用vue-router中的路由导航守卫,在前端每次发生路由跳转时会触发拦截
判断访问哪些组件,哪些组件需要登录,哪些组件不需要登录
路由导航守卫,前端每发生一次路由跳转时,会自动触发beforeEach().
rout.beforeEach((to,from,next)=>{if(to.path=='/login'){//如果访问登录组件,不需要做任何判断,直接放行到目标组件去return next();//放行到目标文件}else{var token = window.sessionStorage.getItem("account");if(token==null){//用户信息为空,说明用户没有登录return next("/login");}else{//说明用户已经登录next();}}
})
目前只是在前端做了控制,后端同样需要进行控制
路由嵌套
在main路由下嵌套其他子路由
<template slot="title"><i class="el-icon-message"></i>操作菜单</template>
<el-menu-item-group>
<el-menu-item index="/majorlist">专业管理</el-menu-item>
<!--路由地址-->
<el-menu-item index="/studentlist">学生管理</el-menu-item>
</el-menu-item-group>
index.js导入组件
import MajorList from "../views/major/MajorList.vue";
import StudentList from "../views/student/StudentList.vue";
在el菜单上加一个router,这样我们的菜单是路由的
<el-menu :default-openeds="['1', '3']" router>
将major和student路由嵌套到main下
{path: '/main',component: Main,children:[{path: "/majorlist",component: MajorList},{path: "/studentlist",component: StudentList} ]}
mian.vue
<el-main><router-view></router-view>
</el-main>
web会话跟踪机制
因为http请求是无状态的,一次强求响应结束后,就结束了
下一次再向服务器端发送请求,服务器并不知道是谁向它发送的
我们需要对整个会话过程进行跟踪:
1、当登录时,后端验证密码是否正确,如果账号正确,就需要在后端为当前登录的用户生成一个令牌(token)将令牌信息响应给前端
2、前端存储token
3、后面每一次从前端向后端发送请求都要携带token
4、后端验证令牌,如果令牌有效,继续向后执行,如果令牌无效,向前端响应失败,重新登录
JWT生成token(json wen token)
基于json的开放标准定义的一种简洁的,自包含的方法,用于通信双方以json对象的形式安全的传递信息。JWT可以使用HMAC算法或者是RSA是公私秘匙进行签名。
id-->token 秘匙-签名
JWT就是用来生成token的方式,是一种可以携带用户信息,并且可以设置秘钥加密的字符串,是安全的。
起源
谈一谈基于token的认证和传统的session认证的区别
传统的session认证
将用户信息存储在服务器端,不好的地方:
1、占用服务器内存
2、分布式项目中,不灵活
基于session认证所暴露的问题
ssion: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
CSRF (跨站请求伪造):因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
基于token的鉴权机制
基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
流程上是这样的:
1. 用户使用账号和密码发出post请求;
2. 服务器使用私钥创建一个jwt;
3. 服务器返回这个jwt给浏览器;
4. 浏览器将该jwt串在请求头中像服务器发送请求;
5. 服务器验证该jwt;
6. 返回响应的资源给浏览器。
JWT的主要应用场景
可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。
由于信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。
优点:
1.简洁(Compact): 可以通过URL
,POST
参数或者在HTTP header
发送,因为数据量小,传输速度也很快
2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
3.因为Token
是以JSON
加密的形式保存在客户端的,所以JWT
是跨语言的,原则上任何web形式都支持。
4.不需要在服务端保存会话信息,特别适用于分布式微服务。
JWT的构成
由三段信息构成,将三段信息文本用链接一起就构成了jwt字符串:
v51vdsvv45KJVHJUSVYD5VK.q3yqfy5q4qebshwny64w6q43t34y54qrtmanr3OH.AKCTW64XBVCSTXIcsDSbwhbvvonwqncpoi7h9p4
第一部分:头部(header)
jwt的头部承载两部分信息:
1、声明类型,这里是jwt
2、声明加密的方法:通常直接使用HMAC HS256
完整的头部就像下面这样的JSON:
{'typ':'JWT';'alg':'HS256'}-->重新进行编码
然后将头部进行base64转码,构成第一部分
v51vdsvv45KJVHJUSVYD5VK.
base64
链接:base64-CSDN博客
第二部分:载荷(payload,用户的信息)
存放有效信息的地方,形象理解为飞机上承载的货品,这些有效信息包含三个部分
1、标准中注册的声明
2、公共的声明
可以添加任何的信息。一般添加用户的相关信息或其他业务需要的必要信息/但不建议添加敏感信息(例如密码),因为该部分在客户端可解密。id,用户名,头像名
3、私有的声明
定义一个playload
{"sub":"1234567890","name":"thesky","admin":true}
然后将头部进行base64转码,得到JWT第二部分
q3yqfy5q4qebshwny64w6q43t34y54qrtmanr3OH.
第三部分:签证(signature)
由三部分组成:
header(base64后的)
playload(base64后的)
secret
需要base64转码后的header和base64转码后的playload使用连接组成新的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,构成jwt的第三部分
AKCTW64XBVCSTXIcsDSbwhbvvonwqncpoi7h9p4
JWT搭建
引入JWT依赖,由于是基于java,所以需要的是java-jwt
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.2</version>
</dependency>
创建生成token的方法
JWT组件
package com.ffyc.dormserver.util;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.back.bean.User;
import com.ffyc.dormserver.model.Admin;import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** JWT工具类*/
public class JWTUtil {/*** 根据用户id,账号生成token* @param u* @return*/public static String getToken(Admin admin) {String token = "";try {//过期时间 为1970.1.1 0:0:0 至 过期时间 当前的毫秒值 + 有效时间Date expireDate = new Date(new Date().getTime() + 10*1000);//秘钥及加密算法Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");//设置头部信息Map<String,Object> header = new HashMap<>();header.put("typ","JWT");header.put("alg","HS256");//携带id,账号信息,生成签名token = JWT.create().withHeader(header).withClaim("id",admin.getId())//第二部分.withClaim("account",admin.getAccount())//第二部分.withExpiresAt(expireDate)//第二部分.sign(algorithm);//第三部分 秘钥 加盐}catch (Exception e){e.printStackTrace();return null;}return token;}
}
后端获取token,将token响应给前端
if(admin!=null){//登录成功后,为当前登录的用户生成tokenString token = JWTUtil.getToken(admin);admin.setToken(token);result = new Result<>(200,"登录成功",admin);}else{result = new Result<>(201,"账号或密码错误",null);}
sessionStorage.setItem("token",resp.data.result.token);
验证token是否有效
JWT组件
/*** 验证token是否有效* @param token* @return*/public static boolean verify(String token){try {//验签Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");JWTVerifier verifier = JWT.require(algorithm).build();DecodedJWT jwt = verifier.verify(token);return true;} catch (Exception e) {//当传过来的token如果有问题,抛出异常return false;}}
axios请求拦截
//axios 请求拦截,每当我们使用axios框架向后端发送请求时,都会经过拦截器
axios.interceptors.request.use(config =>{//为请求头对象,添加 Token 验证的 token 字段config.headers.token = sessionStorage.getItem('token');return config;
})
token过滤器
package com.ffyc.dormserver.filter;import com.fasterxml.jackson.databind.ObjectMapper;
import com.ffyc.dormserver.model.Result;
import com.ffyc.dormserver.util.JWTUtil;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;/*验证token是否有效
*/
@WebFilter(urlPatterns = "/api/*")
public class TokenFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//向下转型HttpServletRequest request = (HttpServletRequest)servletRequest;//从请求头中拿到tokenString token = request.getHeader("token");System.out.println("token验证过滤器");//验证tokenboolean verify = JWTUtil.verify(token);if(verify){//token验证成功继续向后执行,到达目标servlet程序filterChain.doFilter(servletRequest, servletResponse);}else{//token验证失败向前端响应401Result result = new Result(401,"token认证失败",null);servletResponse.getWriter().print(new ObjectMapper().writeValueAsString(result));}}
}
响应拦截器
// 添加响应拦截器
axios.interceptors.response.use((resp) =>{//正常响应拦截if(resp.data.code==500){ElementUI.Message({message:resp.data.desc,type:"error"})}if(resp.data.code==401){ElementUI.Message({message:resp.data.desc,type:"error"})router.replace("/login");}return resp;
});
获得token中playload部分数据
/*** 获得token 中playload部分数据,按需使用* @param token* @return*/public static DecodedJWT getTokenInfo(String token){return JWT.require(Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);}