【JavaWeb】token
前言
前面我么们学习了cookie、session今天我们来理解token,其实token很像session那其实都是衍生过来的,所以简单理解下吧,如果现在理解不了,慢慢往后就会理解了,那看前点个赞,养成好习惯👍
书接上回:
【JavaWeb】-Tomcat、Eclipse使用项目搭建(一)
【JavaWeb】-mysql、jdbc、dbcp使用(二)
【JavaWeb】-MVC、Servlet学习(三)
【JavaWeb】-Cookie、Session学习(四)
介绍
目前已经有很多都用到了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数据无疑全部暴露;
添加依赖
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<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"));
//可以获取id
System.out.println(claims.getId());
//获取subject
System.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}
root
root
ef13da19-c35e-423b-91e1-df11192281b4
user
Tue 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"));
//可以获取id
System.out.println(claims.getId());
//获取subject
System.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放入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 stub
doGet(request, response);
}
}
index.jsp页面
<%@page import="Dao.BaseDao"%>
<%@page import="com.sun.javafx.collections.MappingChange.Map"%>
<%@page import="util.TokenUtil"%>
<%@page import="io.jsonwebtoken.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>主界面</title>
</head>
<body>
<%
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")+"<br/>");
out.print(user.get("pass")+"<br/>");
}
}
}
%>
这里是主界面
</body>
</html>
效果


我们发现,这里我们并没有存储用户信息而是将信息放到了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 stub
doGet(request, response);
}
}
index.jsp页面
<%@page import="Dao.BaseDao"%>
<%@page import="com.sun.javafx.collections.MappingChange.Map"%>
<%@page import="util.TokenUtil"%>
<%@page import="io.jsonwebtoken.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>主界面</title>
</head>
<body>
<%
/* 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")+"<br/>");
out.print(user.get("pass")+"<br/>");
}
}
} */
if (request.getHeader("token") != null) {
Claims user = TokenUtil.parseToken(request.getHeader("token"));
out.print(user.get("name") + "<br/>");
out.print(user.get("pass") + "<br/>");
}
%>
这里是主界面
</body>
</html>
我们再次运行执行登录请求,打开检查,我们发现已经从请求头中响应了过来:

这里我们用postman测试下,通过请求头测试,测试成功:

而用户只要是携带了token,我们后端有这个token只需要判断有没有就知道这个用户是否登录过;这里我们放在了cookie中,token我们还可以放在请求头中,可以防止很多攻击,对于前后端项目来说,很好,但是这里尽量不要存放用户敏感信息,因为token也不是安全的;
包括token如果存放在请求头中,这里我们没有办法刷新用户的token只能是到时间之后自动过期;
具体使用场景看具体业务,这里只是简单的了解下,并没有什么业务代码,也是对于自己的理解,好就这样吧,那占位符 {jsp}
小结
通过cookie、session、以及token的学习相信已经了解了,那这只是很简单的了解,之后可以更加深入的学习,进行业务操作,所以到这里还不三连点个关注吗

本文介绍了JavaWeb中的Token,将其与Session进行了对比,并详细讲解了JWT(JSON Web Token)的工作原理和使用方法,包括JWT的构成、生成与解析。通过示例展示了如何在登录时使用JWT生成和验证Token,以及如何通过请求头传递Token。
997

被折叠的 条评论
为什么被折叠?



