web会话跟踪(JWT是什么):
一.使用会话跟踪的原因:
因为http请求是无状态,一次请求响应结束后,就结束了,下一次再向服务器端发送请求,服务器并不知道是谁向他发送的
所以我们需要对整个会话过程进行跟踪
二.token:
● token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后, 服务器生成一个token便将此token返回给客户端,以后客户端只需带上这个token前来 请求数据即可。token保存在客户端,并且进行了加密,保证了数据的安全性.
● 目的:token的目的是为了减轻服务器的压力,使服务器更加健壮。
三.过程:
1.当登录时,后端验证账号密码是否正确,如果账号正确,就需要在后端为当前登录的用户生成一个令牌(token),将令牌信息响应给前端。
2.前端存储token
3.后面每次从前端向后端发送请求,都要携带token
4.后端验证令牌,如果令牌有效,继续向后执行。如果令牌无效,向前端响应认证失败,让重新登录。
JWT:
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON
的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON
**对象的形式安全的传递信息。**因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC
算法或者是RSA
的公私秘钥对进行签名。
Id–>token 秘钥-签名 abc
JWT就是用来生成token的一种方式,一种可以携带用户信息,并且可以设置秘钥加密的字符串,是安全的.**
流程:
1. 用户使用账号和密码发出post请求;
2. 服务器使用私钥创建一个jwt;
3. 服务器返回这个jwt给浏览器;
4. 浏览器将该jwt串在请求头中像服务器发送请求;
5. 服务器验证该jwt;
6. 返回响应的资源给浏览器。
JWT的主要应用场景
身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。
优点
1.简洁(Compact): 可以通过**URL
,POST
参数或者在HTTP header
发送,因为数据量小,传输速度也很快
2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
3.因为Token
是以JSON
加密的形式保存在客户端的,所以JWT
是跨语言的,原则上任何web形式都支持。
4.不需要在服务端保存会话信息,特别适用于分布式微服务。
JWT的构成:
JWT是由三段信息构成的,将这三段信息文本用.
链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 用户的信息),第三部分是签证(signature).
第一部分
header
jwt的头部承载两部分信息:
- 声明类型,这里是jwt
- 声明加密的算法 通常直接使用 HMAC HS256
完整的头部就像下面这样的JSON:
{‘typ’: ‘JWT’,‘alg’: ‘HS256’}–>重新进行编码(不是加密)
然后将头部进行base64转码,构成了第一部分.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
第二部分
playload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
· 标准中注册的声明
· 公共的声明
· 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务 需要的必要信息.但不建议添加敏感信息(例如密码),因为该部分在客户端可解密. id,用户名,头像名
· 私有的声明
定义一个payload
{ “sub”: “1234567890”, “name”: “John Doe”,“admin”: true}
然后将其进行base64转码,得到Jwt的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
第三部分
signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
· header (base64后的)
· payload (base64后的)
· secret
这个部分需要base64转码后的header和base64转码后的payload使用.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt的第三部分。
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
JWT搭建
引入JWT依赖,由于是基于Java,所以需要的是java-jwt
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.8.2</version></dependency>
创建生成token的方法:
* jwt生成token* @param id* @param account* @return*/public static String token (Integer id, String account){String token = "";try {//过期时间 为1970.1.1 0:0:0 至 过期时间 当前的毫秒值 + 有效时间Date expireDate = new Date(new Date().getTime() + 10*1000);//秘钥及加密算法Algorithm algorithm = Algorithm.HMAC256("xxxxxxxxxxxxxxxx");//设置头部信息Map<String,Object> header = new HashMap<>();header.put("typ","JWT");header.put("alg","HS256");//携带id,账号信息,生成签名token = JWT.create().withHeader(header).withClaim("id",id).withClaim("account",account).withExpiresAt(expireDate).sign(algorithm);}catch (Exception e){e.printStackTrace();return null;}return token;}
验证token是否有效的方法:
public static boolean verify(String token){try {//验签Algorithm algorithm = Algorithm.HMAC256("xxxxxxxxxxxxxxxxxx");JWTVerifier verifier = JWT.require(algorithm).build();DecodedJWT jwt = verifier.verify(token);return true;} catch (Exception e) {//当传过来的token如果有问题,抛出异常return false;}}
获得token 中playload部分数据:
/*** 获得token 中playload部分数据,按需使用* @param token* @return
*/
public static DecodedJWT getTokenInfo(String token){
return JWT.require(Algorithm.HMAC256("xxxxxxxxxxxxxxxx")).build().verify(token);
}
用户登录成功后将用户id和账号存储到token中返回给客户端,之后客户端每次请求将token发送到服务器端验证, 在服务器中进行验证.
前端此时发送请求:
var token=sessionstorage.getItem('token');
this.$http.get("/test?token="+token).then((resp)=>{
})
后端此时接收请求:
@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("test doget");String token=req.getParameter("token");System.out.println(token);boolean verify= JWTUtil.verify(token);System.out.println(verify);if(verify){}else {}}
优化:
但如果我们每次在前端发送一次请求,后端总是使用verify来验证的话过于麻烦,所以我们需要在前端的过滤器中把token在每次请求后直接默认给请求头,然后在后端的过滤器中验证一次请求头中的token。
后端过滤器:
经过过滤器验证完token后直接跳转相应的servlet,没有这个过滤器的话就需要在每个servlet中添加验证
// 登录login的servlrt中不加api,需要验证登录没的servlet都加api前缀
// 这样子token之类的过滤器不会在login时加载
@WebFilter(urlPatterns="/api/*")
public class TokenFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// getHeader是HttpServletRequst中的HttpServletRequest request=(HttpServletRequest)servletRequest;//向下转型String token=request.getHeader("token"); //接收请求头中的tokenSystem.out.println("token验证过滤器");//验证tokenboolean res= JWTUtil.verify(token);if(res){//token验证成功,继续向后执行,到达目标servlet程序filterChain.doFilter(servletRequest,servletResponse);}else {//token验证失败,向前端响应401Result result=new Result(401,"token认证失败",null); //权限认证失败servletResponse.getWriter().print(new ObjectMapper().writeValueAsString(result));}}
}
前端拦截器:
接下来我们需要在main.js里添加请求拦截,每当我们使用axios把框架向后端发送请求时,都会经过拦截器。把浏览器的token给请求头进行验证是否相同,相同则请求通过。
//axios 请求拦截,每当我们使用axios框架向后端发送请求时,都会经过拦截器
axios.interceptors.request.use(config=>{//为请求头对象,添加token验证的token字段//c.h.token的token是请求头中设置的token名
config.headers.token=window.sessionStorage.getItem('token');return config;
})
前端添加响应拦截器:
当后端响应回来的状态为401和500的时候需要拦截器拦截同意进行操作,不然就要在每次可能会出现401和500的情况下做出相应的操作,这样很麻烦。
//添加响应拦截器
axios.interceptors.response.use((resp)=>{ //正常响应拦截if(resp.data.code==401){ElementUI.Message({message:resp.data.desc,type:"error"})router.replace("/login");//用replace跳转返回不回去}if(resp.data.code==500){ElementUI.Message({message:resp.data.desc,type:"error"})}return resp;
});