【JavaWeb】cookie、session、token(jwt)全部完成,简单理解下吧
【JavaWeb】token
前言
前面我么们学习了cookie、session今天我们来理解token,其实token很像session那其实都是衍生过来的,所以简单理解下吧,如果现在理解不了,慢慢往后就会理解了,那看前点个赞,养成好习惯👍
书接上回:
【JavaWeb】-Tomcat、Eclipse使用项目搭建(一)
【JavaWeb】-mysql、jdbc、dbcp使用(二)
【JavaWeb】-MVC、Servlet学习(三)
【JavaWeb】-Cookie、Session学习(四)
- Cookie的使用
- Session的使用
-
Token的使用
- 【JavaWeb】token
-
- 前言
- 介绍
- jwt
-
- 添加依赖
- 编写测试代码
- 输出内容
- token尝试
- 小结
介绍
目前已经有很多都用到了token,那如果我们说了解cookie和session那token绝对是个新概念,其实我也是后面才了解,而且也不是很了解,这里既然都说到了,就拿目前自己了解的说下吧;
我们首先看看token是什么(来自百度百科):
就是信息加密技术,我们这里叫做令牌,作为交互对话的唯一身份标识,当用户第一次过来登录过后,我们验证身份会给用户发一个令牌,也就是token;
当用户再来访问的时候,只需要携带令牌,可以存放在cookie或请求头header中,这里注意我们session是通过session跟踪只能在cookie而令牌可以放到请求头中,这样可以防止一些攻击,大家可以自行去百度更多的文章去理解,这里我也不会这些攻击,等哪天会了再来跟大家说吧,这样我们与我们这里已经存在的令牌做对比,就可以得知用户是不是合法用户;
我们发现其实token跟session差不多,那其实token也可以理解成session,但是我们可以通过加密把信息全部存储在token中,服务端只需要留下token令牌只需要进行比对,需要的时候拿出token中的信息,这样我们就可以解放了服务端,同时信息的存储又交给了客户端;哈哈,是不是来来回回,还是来来回回,大家说会不会下一个技术出现,就又还给服务端了呢;
这样token虽然省出了空间,但是又要把用户的信息解密出来又是利用了cpu,那我们知道没办法,想要时间就浪费空间,想要空间就得浪费时间,就是这样
那token怎么实现呢,其实就是把信息加密成令牌就可以了,如果自己愿意做,自己可以做,那这里我们不做,就了解下jwt;
jwt
那什么又是jwt呢?
-
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。
-
一个 JWT 实际上就是一个字符串,它由三部分组成,头部、载荷与签名。前两部分需要经过 Base64 编码,后一部分通过前两部分 Base64 编码后再加密而成。
三部分组成:
header(头部)
{ 'typ':'jwt', #类型是jwt 'alg':'HS256' #加密算法,这里还有很多加密算法,大家自行了解}
payload(载荷)
{ 'sub':'42342342342', 'name':'john', 'admin':ture}iss:发行人exp:到期时间sub:主题aud:用户nbf:在此之前不可用iat:发布时间jti:JWT ID用于标识该JWT
signature(签名)
这里是我们做一个签名数据,放在我们后端,对数据进行加密,如果别别人知道了,那这些token数据无疑全部暴露;
添加依赖
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version></dependency>
编写测试代码
package Test;import java.util.Date;import java.util.UUID;import io.jsonwebtoken.*;public class TestJwt {// 这是我们后端对数据的加密签名,密钥private String signature = "xuexiriji";public String encryption() {// 通过建造者模式创建一个jwt构造器JwtBuilder jwtBuilder = Jwts.builder();// 过期时间24小时long time = 1000 * 60 * 60 * 24;// 设置头部String token = jwtBuilder// 头部header.setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256")// 载荷payload,也就是数据.claim("name", "root").claim("pass", "root")// 主题.setSubject("user")// 有效时间,重当前算起,到什么时候.setExpiration(new Date(System.currentTimeMillis() + time))// 再设置一个id.setId(UUID.randomUUID().toString())// signature,加一个签名.signWith(SignatureAlgorithm.HS256, this.signature)// 拼接.compact();System.out.println(token);return token;}public void parse(String token){JwtParser jwtParser = Jwts.parser();Jws<Claims> claimsJws = jwtParser.setSigningKey(this.signature).parseClaimsJws(token);Claims claims = claimsJws.getBody();System.out.println(claims);//获取载荷信息System.out.println(claims.get("name"));System.out.println(claims.get("pass"));//可以获取idSystem.out.println(claims.getId());//获取subjectSystem.out.println(claims.getSubject());//有效期System.out.println(claims.getExpiration());}public static void main(String[] args) {TestJwt testJwt = new TestJwt();testJwt.parse(testJwt.encryption());}}
输出内容
我们通过生成可以看到生成的由三部分组成,通过.分隔,前两个分别对应header和payload的base64加密,最后一个是通过前两个内容加上密钥也就是signature再一次加密:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoicm9vdCIsInBhc3MiOiJyb290Iiwic3ViIjoidXNlciIsImV4cCI6MTY1MTU0ODI0MSwianRpIjoiZWYxM2RhMTktYzM1ZS00MjNiLTkxZTEtZGYxMTE5MjI4MWI0In0.Kf7pwcDhRRnhZrZRJYodDsB3DJmh05ege-Qri7iCNdc这是我们输出的数据{name=root, pass=root, sub=user, exp=1651548241, jti=ef13da19-c35e-423b-91e1-df11192281b4}rootrootef13da19-c35e-423b-91e1-df11192281b4userTue May 03 11:24:01 CST 2022
我们不妨做给简单的tokenUtil:
package util;import java.util.Date;import java.util.Map;import java.util.UUID;import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jws;import io.jsonwebtoken.JwtBuilder;import io.jsonwebtoken.JwtParser;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;public class TokenUtil {// 这是我们后端对数据的加密签名,密钥private static String signature = "xuexiriji";// 过期时间24小时private static long time = 1000 * 60 * 60 * 24;public static String getToken(Map<String, Object> map) {// 通过建造者模式创建一个jwt构造器JwtBuilder jwtBuilder = Jwts.builder();// 设置头部String token = jwtBuilder// 头部header.setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256")// 载荷payload,也就是数据.setClaims(map)// 主题.setSubject("user")// 有效时间,重当前算起,到什么时候.setExpiration(new Date(System.currentTimeMillis() + time))// 再设置一个id.setId(UUID.randomUUID().toString())// signature,加一个签名.signWith(SignatureAlgorithm.HS256, signature)// 拼接.compact();return token;}public static Claims parseToken(String token) {JwtParser jwtParser = Jwts.parser();Jws<Claims> claimsJws = jwtParser.setSigningKey(signature).parseClaimsJws(token);Claims claims = claimsJws.getBody();/*System.out.println(claims);//获取载荷信息System.out.println(claims.get("name"));System.out.println(claims.get("pass"));//可以获取idSystem.out.println(claims.getId());//获取subjectSystem.out.println(claims.getSubject());//有效期System.out.println(claims.getExpiration());*/return claims;}}
token尝试
我们了解了jwt的加密这里我们尝试下jwt对登录信息的保存,做个小例子:
改变我们的MyLoginServlet:
package servlet;import java.io.IOException;import java.net.URLEncoder;import java.util.HashMap;import java.util.Map;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.apache.commons.collections.map.HashedMap;import com.mysql.cj.Session;import com.sun.org.apache.bcel.internal.generic.NEW;import Dao.BaseDao;import util.TokenUtil;/ * Servlet implementation class MyLoginServlet */@WebServlet("/MyLoginServlet")public class MyLoginServlet extends HttpServlet {private static final long serialVersionUID = 1L;/ * @see HttpServlet#HttpServlet() */public MyLoginServlet() {super();// TODO Auto-generated constructor stub}/ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String name = request.getParameter("username");String pass = request.getParameter("password");Map<String, Object> map = new HashMap<String, Object>();map.put("name", name);map.put("pass", pass);//生成token,使用token放入请求头中,那就是前后端分离的项目了,或者前端使用异步方式请求,带上请求头String token = TokenUtil.getToken(map);//将token放入cookieresponse.addCookie(new Cookie("token", token));//将token放入请求头//response.addHeader("token", token);response.sendRedirect("index.jsp");}/ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// TODO Auto-generated method stubdoGet(request, response);}}
index.jsp页面
主界面 <% Cookie[] cookies = request.getCookies();if (cookies != null) {// 我们可以遍历下我们的cooike看看for (Cookie cookie : cookies) {if(cookie.getName().equals("token")){Claims user = TokenUtil.parseToken(cookie.getValue());out.print(user.get("name")+"
");out.print(user.get("pass")+"
");}}} %>这里是主界面
效果
我们发现,这里我们并没有存储用户信息而是将信息放到了token中进行存储,我们只需要后面拿过来token然后通过密钥解析,就可以拿到用户的信息;
我们这里还可以用请求头测试下:
改变下代码:
MyLoginServlet.java
@WebServlet("/MyLoginServlet")public class MyLoginServlet extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String name = request.getParameter("username");String pass = request.getParameter("password");Map<String, Object> map = new HashMap<String, Object>();map.put("name", name);map.put("pass", pass);//生成token,使用token放入请求头中,那就是前后端分离的项目了,或者前端使用异步方式请求,带上请求头String token = TokenUtil.getToken(map);//将token放入cookie//response.addCookie(new Cookie("token", token));//将token放入请求头response.addHeader("token", token);//response.sendRedirect("index.jsp");}/ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// TODO Auto-generated method stubdoGet(request, response);}}
index.jsp页面
主界面 <%/* Cookie[] cookies = request.getCookies();if (cookies != null) {// 我们可以遍历下我们的cooike看看for (Cookie cookie : cookies) {if(cookie.getName().equals("token")){Claims user = TokenUtil.parseToken(cookie.getValue());out.print(user.get("name")+"
");out.print(user.get("pass")+"
");}}} */if (request.getHeader("token") != null) {Claims user = TokenUtil.parseToken(request.getHeader("token"));out.print(user.get("name") + "
");out.print(user.get("pass") + "
");}%>这里是主界面
我们再次运行执行登录请求,打开检查,我们发现已经从请求头中响应了过来:
这里我们用postman测试下,通过请求头测试,测试成功:
而用户只要是携带了token,我们后端有这个token只需要判断有没有就知道这个用户是否登录过;这里我们放在了cookie中,token我们还可以放在请求头中,可以防止很多攻击,对于前后端项目来说,很好,但是这里尽量不要存放用户敏感信息,因为token也不是安全的;
包括token如果存放在请求头中,这里我们没有办法刷新用户的token只能是到时间之后自动过期;
具体使用场景看具体业务,这里只是简单的了解下,并没有什么业务代码,也是对于自己的理解,好就这样吧,那占位符 {jsp}
小结
通过cookie、session、以及token的学习相信已经了解了,那这只是很简单的了解,之后可以更加深入的学习,进行业务操作,所以到这里还不三连点个关注吗