一 Cookie
1.什么是cookie?
HTTP cookie也叫cookie,最初用于在客户端存储会话信息。这个规范要求: 服务器通过发送Set-Cookie HTTP头部包含会话信息(服务器创建),例如下面是包含头部的一个HTTP响应:
yaml
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value //HTTP响应会设置一个名为"name",值为"value"的cookie
Other-header: other-header-value
浏览器会保存这些会话信息(浏览器保存),并在之后每个请求中都会通过HTTP头部再将cookie发给服务器,比如:
GET /index.jsl HTTP/1.1
Cookie: name=value
Other-header: other-header-value
cookie是与特定域绑定的。设置cookie后,他会与请求一起发送到创建他的域。这个限制能保证**cookie中存储的信息只对被认可的接收者开放,不能被其他域访问**。
2.cookie使用注意事项
因为所有cookie都会作为请求头部发送给服务器,所以cookie太大可能会影响客户端对特定域的请求性能。保存的cookie越大,请求完成的时间就越长。对于大量的数据cookie并非最佳存储方式,于是出现了Web Storage。
二 Session
1.广义上的Session技术
HTTP是无状态的,为了能够在HTTP协议上保持住状态,比如用户是否登陆接需要一种方案来把用户的一个个无状态HTTP请求关联起来。这种技术就叫Session。Session的功能就是个一个个分离的HTTP请求关联起来,只要实现这个功能,基本上本能叫Session的一种实现。
- 在Cookie里放个JSESSIONID,在服务器上保持状态,用户请求来了,根据这个JSESESSIONID去服务器里查状态。这是Tomcat的实现方法。
- 把所有状态都存在Cookie里,服务器给个签名防止伪造,每次请求来了,直接充Cookie里面获取状态,这是JWT的实现方法。
- 在Cookie里放个token,状态不存在中间件里,而是存在Redis里,这也是一种Session实现方法。
把Sessin存储在Web中间件中(比如Tomcat),这种做法正在淘汰,因为这种方案对负载均衡不友好,也不利于快速伸缩。 把Session存在Redis和前端的才是最佳方案,尤其在微服务架构大行其道的情况下。 只要HTTP还是无状态的,只要保存状态的是刚需,Session就不会消失,变化的只是它的实现方式。
2.传统的Session的代码和原理分析
java
package com.pug.zixun.controller;
@RestController
@Slf4j
@Api(tags = "用户管理")
public class PassportLoginController extends BaseController {
@PostMapping("/login")
@ApiOperation("登录信息")
public String saveUser(@RequestBody UserVo userVo, HttpSession session) {
// 账号和密码正确
//将用户信息存入session中
session.setAttribute("loginuser", userVo.getUsername());
return "登录成功";
}
@GetMapping("/info")
@ApiOperation("登录信息")
public String getuserinfo(HttpSession session) {
// 账号和密码正确
//从session中取出当前登录的用户信息
return "当前登录是是: " + session.getAttribute("loginuser");
}
}
3.传统session存在的问题
- session存储在内存中。是由tomcat来管理和运行的。
- 内存的问题:重启就会丢失。tomcat重启以后session就丢失
- session的存储数据在tomcat内存,如果你网站用户很大,很容易把jvm的内存撑满。当用户量很大的,很容易内存溢出
- 前后端分离时由于前端与后端往往不在同一个域中,而cookie跟后端的域绑定才生效因此产生跨域问题;
- 部署多个tomcat服务时由于session只存在某个tomcat中,其余的tomcat则无法获取session。
4.spring-session实现session共享[将session统一存储在redis中]
具体实现 依赖
pom.xml
//引入redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
//引入连接池
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
//引入session-redis
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
配置
application.yml
//spring-session的配置
spring:
session:
store-type: redis
timeout: 3600
redis:
namespace: login_user
//redis的配置
redis:
host: 127.0.0.1
port: 6379
database: 7
password: xxxxxxx
lettuce:
pool:
max-active: 20
max-wait: -1
max-idle: 5
min-idle: 0
注解
java
package com.pug.zixun.controller;
@RestController
@Slf4j
@Api(tags = "用户管理")
public class PassportLoginController extends BaseController {
@PostMapping("/login")
@ApiOperation("登录信息")
public String saveUser(@RequestBody UserVo userVo, HttpSession session) {
// 账号和密码正确
session.setAttribute("loginuser", userVo.getUsername());
return "登录成功";
}
@GetMapping("/info")
@ApiOperation("登录信息")
public String getuserinfo(HttpSession session) {
// 账号和密码正确
return "当前登录是是: " + session.getAttribute("loginuser");
}
}
数据存入redis
三 Token
1.为什么要使用token
你可能会说spring-session不香吗。为什么还要去自定义呢?原因很多时候我们的项目是前后端分离的项目开发方式。很多的端比如APP、小程序根本就没有cookie的概念。所以我们必须自己去实现。这样自由度也非常的高。 代码
java
package com.zl.springbootssm.controller.session;
import com.zl.springbootssm.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* Description:
* Author: Mr.Zhao
* Create Date Time: 2023/11/22 21:29.
* Update Date Time:
*/
@RestController
@Slf4j
@RequestMapping("/token")
public class LoginTokenController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@PostMapping ("/login")
public String saveUser(@RequestBody User user) {
log.info(String.valueOf(user));
String key = "token" + UUID.randomUUID().toString();
// 假设账号和密码正确
stringRedisTemplate.opsForValue().set(key, user.getUserName(), 3600, TimeUnit.SECONDS);
return key;
}
@GetMapping("/info")
public String getinfo(String token) {
return "当前登录时" + stringRedisTemplate.opsForValue().get(token);
}
}
2.存在问题
使用token虽然解决了自定义cookie的问题但仍然需要使用redis对session进行存储。
四 Jwt
1.jwt与token的区别
token是一串毫无意义的随机数,而jwt可以存储用户信息。 JWT是json web token缩写。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证JWTToken的正确性,只要正确即通过验证。
2.整合jwt的具体步骤
1.依赖
pom.xml
<!-- 引入jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.2</version>
</dependency>
2.配置 无 3.注解
LoginJwtController.java
package com.zl.springbootssm.controller.session;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.zl.springbootssm.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.AlgorithmConstraints;
import java.util.Date;
@RestController
@Slf4j
@RequestMapping("/jwt")
public class LoginJwtController {
// 秘钥
public final static String key = "admin123";
@GetMapping("login")
public String login(User user) {
Algorithm algorithm = Algorithm.HMAC256(key);
String jwt = JWT.create()
.withIssuer("zhaoliang")
.withClaim("loginuser", user.getUserName())
.withExpiresAt(new Date(System.currentTimeMillis() + 20000))
.sign(algorithm);
return jwt;
}
@GetMapping("/info")
public DecodedJWT getinfo(String token) {
Algorithm algorithm = Algorithm.HMAC256(key);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("zhaoliang")
.build();
DecodedJWT jwt = verifier.verify(token);
return jwt;
}
}