Cookie
核心背景:HTTP 是无状态的
服务器默认不记得你是谁
- 请求1 ------ 完了就忘
- 请求2 ------ 当新请求
在客户端保存状态标识,解决 HTTP 无状态问题
java
@GetMapping("/hello")
public String hello(HttpServletResponse response) {
Cookie cookie = new Cookie("name", "value");
// 20秒后过期
cookie.setMaxAge(20);
response.addCookie(cookie);
return "OK";
}
@GetMapping("/user")
public void user(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals("name")) {
System.out.println(cookie.getName() + ":" + cookie.getValue());
}
}
}
后端设置 cookie 时

前端发起请求携带 cookie 时

cookie 管理界面

优缺点
优点:HTTP协议中支持的技术
缺点:
- 移动端APP无法使用Cookie
- 不安全,用户可以自己禁用Cookie
- Cookie不能跨域
Session
HttpSession 是服务器端保存用户会话状态的对象
核心特点:
- 存在服务器内存(或 Redis)
- 以 SessionId 为索引
- 解决 HTTP 无状态问题
Session 是怎么连续的?(核心原理)
第一次访问之后,会产生一个Cookie,往后访问都会带着它

java
// 存值
@GetMapping("/hello")
public String hello(HttpSession session) {
log.info("HttpSession-s1:{}", session.hashCode());
session.setAttribute("name", "hello");
return "OK";
}
// 获取值
@GetMapping("/user")
public void user(HttpSession session) {
log.info("HttpSession-s2:{}", session.hashCode());
Object name = session.getAttribute("name");
log.info("name: {}", name);
}
缺点,分布式集群下无法使用 Session
优点:数据存放到服务端
令牌JWT
令牌会话跟踪方案的优缺点 ?
优点:
- 支持PC端、移动端
- 解决集群环境下的认证问题
- 减轻服务器端存储压力
缺点:需要自己实现
全称:JSON Web Token (jwt.io/)
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。 组成:
- 第一部分:Header(头),记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}
- 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}
- 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload融入,并加入指定密钥,通过指定签名算法计算而来。

Base64:是一种基于64个可打印字符(A-Z a-z 0-9 + /)来表示二进制数据的编码方式。
引入 maven
xml
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
java
HashMap<String, Object> map = new HashMap<>();
map.put("id", 1);
map.put("name", "admin");
// 字符串中应该,base64机密的,加密内容必须足够长
String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256,
"abcdefghijklmn")
// 自定义数据
.addClaims(map)
// 过期时间, 单位毫秒 + 1小时
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))
.compact();
return jwt;
打印 JWT,用三个 . 分出来三个部分
eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYWRtaW4iLCJpZCI6MSwiZXhwIjoxNzY2MjI3Njk5fQ.ZBiknENQ1NoKxv4myhVF2uS-toijk90uXz9O70Mhg9s
- 第一个部分,base64 解密是:{"alg":"HS256"}
- 第二部分,就是我们存入的对象
- 第三部分,就是加密字符串


令牌生成
java
HashMap<String, Object> map = new HashMap<>();
map.put("id", 1);
map.put("name", "admin");
// 字符串中应该,base64加密的,加密内容必须足够长
String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256,
"abcdefghijklmn")
// 自定义数据
.addClaims(map)
// 过期时间, 单位毫秒 + 1小时
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))
.compact();
令牌解析
java
String token = "eyJhbGciOiJIUzI1NiJ9...";
// claims 实际就是 map
Claims claims = Jwts.parser().setSigningKey("abcdefghijklmn")
.parseClaimsJws(token).getBody();
过滤器
多个过滤器是按照姓名
java
// 开启SpringBoot 对 Servlet 组件的扫描
@ServletComponentScan
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
配置过滤器
java
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
// Web 服务器启动时调用
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
// 拦截请求
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 放行,不写就会被拦截
chain.doFilter(request, response);
}
// Web 服务器关闭时调用
@Override
public void destroy() {
Filter.super.destroy();
}
}
拦截器
配置
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new DemoInterceptor())
// 拦截所有请求
.addPathPatterns("/**")
// 不拦截的请求
.excludePathPatterns("/login");
}
}
拦截器
java
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("在请求之前到,true 返回,false 返回");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("目标运行之后运行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("请求彻底结束");
}
}
