会话:当打开浏览器并访问Web服务器上的资源时,就会建立一个会话。一个会话包括多次请求和响应。例如,登录教学系统会发起一次请求并得到响应,查询课程也会发起一次请求并得到响应。
会话跟踪:服务器识别多个请求是否来自同一浏览器的过程,目的是在同一会话中的多次请求之间共享数据,例如获取验证码并校对获取过的验证码(两个请求)。
HTTP无状态:每个请求都是独立的,没有相互关联,无法共享数据。解决方法是使用会话跟踪。
跟踪方案:
Cookie, 不能进行跨域请求,可以被用户禁用,不够安全。
Session,在服务器集群中无法直接共享。
JWT 令牌,需要自行实现,无法伪造
本质上是一个字符串,它定义了一种简洁且自包含的格式,用于在通信双方之间以JSON数据格式安全传输信息。令牌由以下三个部分组成:
eyJhbGciOiJIUzI1NiJ9.
eyJuYW1lIjoiam9pbiIsImlkIjoxLCJleHAiOjE3MjUwNjc5NjJ9.
3GoCTzD_Hd0hp5E1snA8Dta2UTytTNyH7GrP78Z_5kU
Header(头部):记录签名类型及所用的签名算法,例如:{"alg":"HS256","typ":"JWT"}。
Payload(有效载荷):包含自定义信息以及默认信息,如:{"id":1,"name":"张三"}。
Signature(签名):为了防止令牌被篡改,结合header和payload以及一个指定的密钥,通过特定算法生成。
用户成功登录后,系统将生成一个令牌。此后,每个请求都需要附带该jwt令牌,并且系统会在处理每个请求之前进行拦截验证。
在pom文件中引入jwt依赖:
io.jsonwebtoken
jjwt
0.9.1
使用链式调用的方式来一步生成令牌
public void enJwt(){
HashMap claims= new HashMap<>();
claims.put("id",1);
claims.put("name","join");
String jjweb = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, "jjweb")//指定数字签名的算法以及自定义的密钥类型,就是一个字符串
.setClaims(claims)//第二部分,设置自定义数据,封装在map中
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置令牌有效期,一个小时内
.compact();//调用后得到一个字符串返回值,即令牌
System.out.println(jjweb);
}
解释
创建 claims
对象:
claims
中:
"id"
:1"name"
:"join"生成令牌
使用 Jwts.builder()
来创建一个 JWT 构建器实例,生成一个新的 JWT 令牌。
设置自定义声明
signWith(SignatureAlgorithm.HS256, "jjweb")
:使用 SignatureAlgorithm.HS256
指定哈希算法为 HMAC SHA-256,这是 JWT 常用的对称加密算法。"jjweb"
是一个自定义字符串密钥,用于签名令牌。任何尝试验证此令牌的系统都需要知道这个密钥。签名用于验证数据完整性,确保数据在传输过程中没有被篡改。作为头部。添加claims声明对象
addClaims
是一个用于向现有的 JWT Builder 中添加多个声明(claims)的方法。这通常用于在生成 JWT 令牌时向其中添加自定义数据。
设置令牌的有效期:
setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))
:System.currentTimeMillis()
获取当前时间(以毫秒为单位),并加上 3600 * 1000
(3600秒,即1小时)来指定令牌的有效期为1小时。一旦超过这个时间,JWT 就会被认为无效。生成并返回 JWT 字符串:
compact()
:调用 compact()
方法生成完整的 JWT 字符串。生成的 JWT 包含了以下三个部分,每部分用点(.
)分隔:
claims
)。提前封装好jwt工具类
此方法已自定义密钥和过期时间,并最终返回一个令牌。
private static String signKey = "jjweb";
private static Long expire = 52000000L;
public static String generateJwt(Map claims){
String jwt = Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, signKey)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
Controller接收请求,调用service
@RequestMapping("/login")
public class LoginController {
@Autowired
private EmpService empService;
@PostMapping
public Result login(@RequestBody Emp emp){
Emp emp1 = empService.login(emp);
Result.success(emp1);
}
Service处理层,调用数据访问层mapper接口
@Override
public Emp login(Emp emp) {
Emp login = empMapper.login(emp);
return login;
}
Mapper数据访问层获取数据库中用户名和密码信息并映射到Emp对象中进行逐层返回
@Select("select * from emp where username=#{username} and password = #{password}")
public Emp login(Emp emp);
Controller中对返回的Emp信息进行处理,并响应结果
如果登录成功,那么我们需要返回这个令牌
如果在数据库中查询到相关信息,那么emp对象将不为null。在这里,载荷部分使用了自定义信息,比如员工的id和姓名。最终,我们会对结果进行格式化处理后返回。如果没有找到信息,则返回错误信息。(不包含拦截验证,主要是生成并下发)
public Result login(@RequestBody Emp emp){
Emp emp1 = empService.login(emp);
//如果登录成功,就生成令牌,并发送给前端
if(emp1!=null){
HashMap claims = new HashMap<>();
claims.put("id",emp1.getId());
claims.put("username",emp1.getUsername());
String jwt = JwtUtils.generateJwt(claims);
return Result.success(jwt);
}
return Result.error("错误");
}
使用postman进行本地测试
失败
成功
请求拦截并验证JWT(下)https://mp.csdn.net/mp_blog/creation/editor/141750536
C你太美,一种多看一眼就会爆炸的代码艺术https://blog.csdn.net/2301_81243054/article/details/141663587