【Tilas|第八篇】万字讲解登录认证知识点
本文通俗讲解了登录认证中的核心概念:Cookie是浏览器存储的小数据,Session是服务器保存的会话信息,Token是身份令牌,JWT则是标准化的Token格式。文章通过代码示例展示了Cookie和Session的使用方式,并详细介绍了JWT的结构、特点及实现方法,包括JWT工具类、登录接口等具体代码实现。最后强调JWT使用时的注意事项,如不要存储敏感信息、设置过期时间等。全文以通俗易懂的方式帮
一、前言
登录认证是后端开发里的基础内容,也是面试高频点。
刚学的时候最容易乱的就是这几个词:
CookieSessionTokenJWT
它们都和“登录状态”有关,但不是一回事。
这篇文章我就用最通俗的方式,把它们讲清楚。
二、先记住一句话
Cookie 是浏览器保存的小纸条,Session 是服务器保存的会话,Token 是身份令牌,JWT 是一种标准化的 Token。
三、Cookie 是什么
Cookie 是浏览器端保存的一小段数据,服务器可以把它发给浏览器,浏览器后续请求时会自动带上。
它的特点是:
- 存在客户端
- 数据量小
- 会自动随请求携带
- 适合保存少量状态,不适合放敏感信息
1. Cookie 示例
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
response.addCookie(new Cookie("login_username","itheima"));
return Result.success();
}
@GetMapping("/c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(cookie.getName().equals("login_username")){
System.out.println("login_username: " + cookie.getValue());
}
}
return Result.success();
}
这段代码说明了两件事:
cookie1往浏览器里写 Cookiecookie2从请求里把 Cookie 取出来
一句话理解:
Cookie 更像浏览器替你保存的一张小卡片。
四、Session 是什么
Session 是服务器端保存的会话数据,通常用来记录当前用户是否登录、是谁登录了、权限是什么。
它的特点是:
- 存在服务器端
- 更适合保存登录状态
- 服务器能主动管理失效
- 用户多时会增加服务器压力
1. Session 示例
项目里的代码也很直观:
@GetMapping("/s1")
public Result session1(HttpSession session){
log.info("HttpSession-s1: {}", session.hashCode());
session.setAttribute("loginUser", "tom");
return Result.success();
}
@GetMapping("/s2")
public Result session2(HttpSession session){
log.info("HttpSession-s2: {}", session.hashCode());
Object loginUser = session.getAttribute("loginUser");
log.info("loginUser: {}", loginUser);
return Result.success(loginUser);
}
这里做的事情就是:
/s1往 Session 里存数据(set)/s2再从 Session 里取数据(get)
2. Cookie 和 Session 的关系
很多人会误以为它们是对立的,其实不是。
通常是这样配合的:
- Session 存在服务器
- 浏览器用 Cookie 带着
sessionId - 服务器根据
sessionId找到对应 Session
一句话理解:
Cookie 负责“带编号”,Session 负责“存内容”。
五、Token 是什么
Token 就是“令牌”。这里不是我们平常所说的ai算力
它是一种认证思路,不是某一种固定格式。
登录成功后,服务器给前端一个 Token,之后前端每次请求都带着它,服务器通过校验 Token 判断用户身份。
一句话理解:
Token 就像你的身份证
六、JWT 是什么
JWT 全称是 JSON Web Token,它是 Token 的一种标准格式。
JWT 一般长这样:
header.payload.signature
它由三部分组成:
Header:说明算法和类型Payload:保存用户信息和过期时间Signature:签名,用来防篡改
1. JWT 的特点
- 结构清晰
- 适合前后端分离
- 服务器可以少存状态
- 适合分布式和微服务
2. JWT 的注意点
- 不要放密码
- 不要放特别敏感的信息
- 一定要设置过期时间
- 密钥不能随便泄露
3.Maven依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
七、JWT 示例代码
1. 登录请求对象
public class LoginRequest {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2. 登录返回对象
public class LoginResponse {
private Integer userId;
private String username;
private String token;
public LoginResponse(Integer userId, String username, String token) {
this.userId = userId;
this.username = username;
this.token = token;
}
public Integer getUserId() {
return userId;
}
public String getUsername() {
return username;
}
public String getToken() {
return token;
}
}
3. JWT 工具类
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtils {
private static final String SECRET_KEY = "demo-secret-key";
private static final long EXPIRE_TIME = 2 * 60 * 60 * 1000;
public static String createToken(Integer userId, String username) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("username", username);
return Jwts.builder()
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.addClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE_TIME))
.compact();
}
public static Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
}
4. 登录接口
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/login")
public LoginResponse login(@RequestBody LoginRequest request) {
if ("admin".equals(request.getUsername()) && "123456".equals(request.getPassword())) {
String token = JwtUtils.createToken(1, request.getUsername());
return new LoginResponse(1, request.getUsername(), token);
}
throw new RuntimeException("用户名或密码错误");
}
@GetMapping("/profile")
public String profile() {
return "这是一个需要登录后才能访问的接口";
}
}
5. JWT 拦截器
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String path = request.getRequestURI();
if ("/auth/login".equals(path)) {
return true;
}
String token = request.getHeader("token");
if (token == null || token.isEmpty()) {
response.setStatus(401);
response.getWriter().write("token 不能为空");
return false;
}
try {
Claims claims = JwtUtils.parseToken(token);
System.out.println("当前用户: " + claims.get("username"));
return true;
} catch (Exception e) {
response.setStatus(401);
response.getWriter().write("token 无效或已过期");
return false;
}
}
}
6. JWT 过滤器
除了拦截器,其实过滤器也可以做 JWT 登录校验。
很多同学刚学到这里时容易分不清:
Filter是Servlet规范里的组件Interceptor是Spring MVC里的组件
它们都能做登录认证,但触发位置不一样。
简单理解一下:
- 过滤器更靠前,请求刚进来时就能处理
- 拦截器更靠近
Controller - 过滤器拦截范围更广
- 拦截器更适合结合 Spring MVC 的处理流程做控制
如果只是做一个简单的 JWT 校验,过滤器也完全能胜任。下面我单独写一个更容易理解的示例。
import io.jsonwebtoken.Claims;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class JwtFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String uri = httpRequest.getRequestURI();
// 登录接口直接放行
if ("/auth/login".equals(uri)) {
chain.doFilter(request, response);
return;
}
// 从请求头中获取 token
String token = httpRequest.getHeader("token");
// 如果没带 token,直接拦截
if (token == null || token.isEmpty()) {
httpResponse.setStatus(401);
httpResponse.getWriter().write("未登录,请先携带 token");
return;
}
try {
Claims claims = JwtUtils.parseToken(token);
System.out.println("过滤器校验通过,当前用户:" + claims.get("username"));
chain.doFilter(request, response);
} catch (Exception e) {
httpResponse.setStatus(401);
httpResponse.getWriter().write("token 无效或已过期");
}
}
}
这段代码的逻辑也不复杂:
- 先拿到当前请求路径
- 如果是登录接口,直接放行
- 如果不是登录接口,就从请求头里取 token
- token 不存在,直接返回
401 - token 存在,就解析它
- 解析成功,继续访问后面的资源
- 解析失败,返回
401
这里有一个很重要的点要记住:
过滤器和拦截器都能做登录校验,但实际项目里通常不会把同一套 JWT 校验逻辑同时在两边都写一遍,否则很容易重复校验。
一般来说:
- 想在更前面统一处理请求,可以用过滤器
- 想结合 Spring MVC 处理器链来控制,可以用拦截器
7. 注册认证组件
前面我们已经写了拦截器,现在又补了一版过滤器。
那这两个东西怎么注册呢?
7.1 注册拦截器
拦截器还是按 Spring MVC 的方式注册:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JwtInterceptor())
.addPathPatterns("/**");
}
}
这表示所有请求都会先经过 JwtInterceptor。
7.2 注册过滤器
如果过滤器上用了 @WebFilter,那么 Spring Boot 启动类还要加上 @ServletComponentScan,这样容器才能扫描到过滤器。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
7.3 实际开发怎么选?
如果项目里只是做普通的接口登录校验,常见做法有两种:
- 用过滤器统一校验 token
- 用拦截器统一校验 token
更推荐的思路是:
同一层认证逻辑尽量只放一个入口,别过滤器和拦截器同时做同样的事。
否则会出现这些问题:
- 重复解析 token
- 代码职责不清
- 出错时排查麻烦
8. 整个 JWT 示例的执行流程
把前面的代码串起来,完整流程就是这样:
- 用户输入用户名和密码
- 前端调用
/auth/login - 后端校验用户名和密码
- 校验成功后生成 JWT
- 后端把 token 返回给前端
- 前端保存 token
- 前端访问受保护接口时,在请求头中携带 token
- 过滤器或者拦截器统一校验 token
- 校验通过,放行请求
- 校验失败,返回
401
到这里,其实 JWT 登录认证的核心链路就已经完整了。
八、登录流程图
九、JWT 时序图
十、过滤器和拦截器怎么区分
这一块也是很多同学容易混的点,这里顺手总结一下。
| 对比项 | 过滤器 Filter | 拦截器 Interceptor |
|---|---|---|
| 所属规范 | Servlet 规范 | Spring MVC |
| 执行时机 | 更早,进入 Servlet 之前 | 更靠后,进入 Controller 之前 |
| 依赖环境 | 依赖 Web 容器 | 依赖 Spring MVC |
| 拦截范围 | 更广 | 主要拦截 Controller 请求 |
| 适合场景 | 编码处理、统一日志、权限前置控制 | 登录校验、权限判断、业务控制 |
如果只用一句话来区分,可以记成:
过滤器更像“保安”,拦截器更像“单元楼的门禁”。
保安是请求一进楼就先检查,门禁则是准备进入具体业务区域前再核验一次。
十一、Cookie、Session、Token、JWT 区别总结
| 技术 | 存储位置 | 作用 | 优点 | 缺点 |
|---|---|---|---|---|
| Cookie | 浏览器 | 保存少量状态 | 自动携带 | 不适合敏感数据 |
| Session | 服务器 | 保存登录会话 | 服务端可控 | 分布式麻烦 |
| Token | 客户端 | 身份凭证 | 灵活 | 需要自己设计校验 |
| JWT | Token 的一种格式 | 标准化令牌 | 无状态,适合集群 | 不容易立刻失效 |
十二、什么时候用谁?
1. 用 Session 的场景
- 传统 Web 项目
- 单体系统
- 后台管理系统
- 用户量不大
2. 用 JWT 的场景
- 前后端分离
- Vue / React 项目
- App / 小程序接口
- 微服务 / 分布式系统
3. 一句话建议
- 单体系统,Session 很方便
- 分离式系统,JWT 更常见
- 安全要求高时,要配合黑名单、刷新令牌等机制
十三、面试题总结版
1. 过滤器和拦截器的区别是什么?
过滤器属于 Servlet 规范,拦截器属于 Spring MVC。
过滤器执行更早,范围更广;拦截器更贴近 Controller,适合业务层面的统一控制。
2. Cookie 和 Session 有什么区别?
Cookie 存在浏览器端,Session 存在服务器端。
Cookie 常常负责带 sessionId,Session 负责保存登录状态。
3. Token 和 JWT 有什么区别?
Token 是令牌的统称,JWT 是 Token 的一种标准格式。
4. 为什么前后端分离常用 JWT?
因为它更适合接口化调用,也更适合集群部署,不需要像 Session 那样强依赖服务端保存会话。
5. JWT 为什么不能存密码?
因为 JWT 的载荷部分通常可以被解码看到,所以不能直接放敏感信息。
6. JWT 的三部分是什么?
- Header
- Payload
- Signature
7. 过滤器和拦截器都能做登录认证吗?
都可以。
但实际项目里一般二选一,或者各司其职,不建议把同一套 token 校验逻辑重复写两遍。
十四、总结
学登录认证时,最重要的不是死记硬背概念,而是先把它们之间的关系理顺。
可以这样记:
Cookie是浏览器保存的小纸条Session是服务器保存的会话Token是身份令牌JWT是一种标准化的 TokenFilter和Interceptor都能做认证校验,只是位置和职责不同
如果是传统项目,Session 很常见。
如果是前后端分离项目,JWT 很常见。
如果再往下细分到认证入口,过滤器和拦截器都能做,但最好职责清晰,不要重复校验。
我是新手程序猿乐锅。本次分享到此结束,感谢大家的观看与支持!如果本期内容对您有帮助,欢迎点赞、收藏,您的支持将是我持续创作的最大动力,谢谢!
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)