Token身份验证完整流程

前后端分离项目中JWT Token身份验证全流程详解

在前后端分离的Web开发架构中,身份验证是保障系统安全的核心环节,JWT(JSON Web Token)凭借其无状态、易扩展的特性成为主流方案。本文将结合实际代码,详细拆解Token身份验证的完整实现思路、核心代码逻辑及设计原则。

一、Token身份验证的核心设计理念

传统的Session-Cookie验证依赖服务端存储会话信息,在分布式系统中需解决会话共享问题;而JWT Token采用无状态验证模式,将用户身份信息加密后存储在Token中,服务端只需验证Token的合法性即可完成身份校验,无需存储会话数据,大幅提升系统扩展性。

核心优势:

**1. 无状态:**服务端无需存储会话,便于集群部署;

**2. 跨域友好:**Token通过请求头传递,适配前后端分离的跨域场景;

**3. 过期可控:**可自定义Token有效期,降低被盗用风险;

**4. 轻量化:**基于JSON格式,传输和解析成本低。

二、Token身份验证完整流程(附核心代码)

阶段1:登录生成Token(客户端→服务端)

1. 前端登录请求代码

用户输入账号密码后,前端通过AJAX发起登录请求,将参数转为JSON格式传递:

javascript

function login() {

$.ajax({

type: "post",

url: "/user/login",

contentType: "application/json",

data: JSON.stringify({

userName: $("#username").val(),

password: $("#password").val()

}),

success: function(result) {

if (result.code == 200 && result.data != null) {

// 登录成功后存储Token和用户ID

localStorage.setItem("loginUserId", result.data.userId);

localStorage.setItem("token", result.data.token);

location.href = "blog_list.html"; // 跳转到业务页面

}

}

});

}

2. 后端验证并生成Token

后端接收登录请求后,校验账号密码合法性,验证通过则生成JWT Token:

java

// UserServiceImpl核心逻辑

public LoginResponse check(LoginRequest loginRequest) {

// 1. 从数据库查询用户信息

LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();

queryWrapper.eq(UserInfo::getUserName, loginRequest.getUserName());

UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);

// 2. 校验用户是否存在、密码是否正确

if (userInfo == null) {

throw new BlogException("用户名不正确");

}

if (!userInfo.getPassword().equals(loginRequest.getPassword())) {

throw new BlogException("密码不正确");

}

// 3. 生成JWT Token

String token = JwtUtils.createToken(userInfo.getId().toString());

// 4. 返回Token和用户ID

LoginResponse loginResponse = new LoginResponse();

loginResponse.setUserId(userInfo.getId());

loginResponse.setToken(token);

return loginResponse;

}

阶段2:Token的本地存储(客户端)

登录成功后,前端将后端返回的Token存储在浏览器的 localStorage 中:

javascript

localStorage.setItem("token", result.data.token);

localStorage 是浏览器的本地存储机制,Token会持久化保存(除非手动清除),保证用户在关闭浏览器重新打开后仍可保留登录状态。

阶段3:请求自动携带Token(客户端→服务端)

为避免在每个请求中手动添加Token,前端通过jQuery的全局AJAX拦截器,在所有请求发送前自动将Token放入请求头:

javascript

// 全局AJAX请求拦截器:添加Token到请求头

$(document).ajaxSend(function(e, xhr, opt){

let userToken = localStorage.getItem("token");

xhr.setRequestHeader("user_token", userToken);

});

核心作用:所有需要权限的请求都会自动携带 user_token 请求头,无需重复编写Token传递逻辑。

阶段4:服务端Token校验(拦截器实现)

1. 登录拦截器(LoginInterceptor):核心校验逻辑

java

@Component

public class LoginInterceptor implements HandlerInterceptor {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

// 1. 从请求头获取Token

String userToken = request.getHeader("user_token");

// 2. 校验Token合法性(是否过期、签名是否正确)

Boolean check = JwtUtils.check(userToken);

if (check) {

// Token有效,放行请求

return true;

} else {

// Token无效,返回401未授权状态码

response.setStatus(401);

return false;

}

}

}

2. 拦截器配置(WebConfig):规则管理

java

@Configuration

public class WebConfig implements WebMvcConfigurer {

// 无需拦截的路径:登录接口、静态资源等

List<String> excludePath = List.of(

"/user/login",

"/**/**.html",

"/pic/**",

"/js/**",

"/css/**"

);

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new LoginInterceptor())

.addPathPatterns("/**") // 拦截所有请求

.excludePathPatterns(excludePath); // 排除公开路径

}

}

  • LoginInterceptor :负责具体的Token校验逻辑(执行者);

  • WebConfig :负责拦截器的注册和规则配置(配置者);

  • 分工设计遵循"单一职责原则",便于后续扩展(如添加日志拦截器、权限拦截器)。

阶段5:Token失效处理(客户端)

当Token过期/无效时,服务端返回401状态码,前端通过全局AJAX错误拦截器统一处理,自动跳转到登录页:

javascript

// 全局AJAX错误拦截器:处理401未授权

$(document).ajaxError(function (event, xhr, options, exc) {

if (xhr.status == 401) {

location.href = "blog_login.html"; // 跳转到登录页

}

});

三、关键配套代码说明

1. 全局异常处理(保证错误信息统一返回)

@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
// 处理自定义业务异常
@ExceptionHandler(BlogException.class)
public Result error(BlogException e) {
log.error("业务异常:", e);
return Result.error(e.getMessage());
}
// 处理全局异常
@ExceptionHandler(Exception.class)
public Result error(Exception e) {
log.error("系统异常:", e);
return Result.error(e.getMessage());
}
}

2. 统一响应结果(Result类)

@Data
public class Result<T> {
private int code; // 响应码:200成功/其他失败
private String errMsg; // 错误信息
private T data; // 响应数据
// 成功响应
public static <T> Result<T> ok(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setData(data);
return result;
}
// 失败响应
public static <T> Result<T> error(String errMsg) {
Result<T> result = new Result<>();
result.setCode(-1);
result.setErrMsg(errMsg);
return result;
}
}

四、核心设计原则与最佳实践

1. 职责分离原则

  • 前端:Token的存储、自动携带、失效跳转分离实现;
  • 后端:Token生成、校验、拦截规则配置分离实现;
  • 优势:代码模块化,便于维护和扩展(如新增权限拦截器只需修改配置类)。

2. 安全性设计

  • 密码校验:将数据库密码放在equals方法前,避免空指针( userInfo.getPassword().equals(loginRequest.getPassword()) );
  • Token传输:通过HTTPS传输,防止Token被窃取;
  • 过期控制:JWT Token设置合理有效期(如2小时),降低被盗用风险。

3. 用户体验优化

  • 全局拦截:前端自动携带Token、自动处理401跳转,无需用户手动操作;
  • 统一错误信息:后端通过自定义异常返回明确的错误提示(如"用户名不正确"),前端无需解析复杂错误格式。

五、总结

JWT Token身份验证的核心是"生成-存储-携带-校验-失效处理"的闭环流程:
1. 登录时生成Token并返回前端;

2. 前端存储Token并在请求时自动携带;

3. 后端通过拦截器统一校验Token合法性;

4. Token失效时前端自动引导用户重新登录。
该方案既解决了传统Session的分布式共享问题,又通过前后端的全局拦截器实现了代码的统一管理,是前后端分离项目中身份验证的最优实践之一。

相关推荐
王九思2 小时前
Java 内存分析工具 MAT
java·开发语言·安全
浅水壁虎2 小时前
任务调度——XXLJOB2(调度中心)
java·spring boot·spring
利刃大大2 小时前
【Vue】指令修饰符 && 样式绑定 && 计算属性computed && 侦听器watch
前端·javascript·vue.js·前端框架
青火coding2 小时前
SOFAServerless架构的意义
java·运维·中间件·架构·serverless
夕除2 小时前
java--2
java·开发语言
ValidationExpression3 小时前
学习:词嵌入(Word Embedding / Text Embedding)技术
python·学习·ai
源码获取_wx:Fegn08953 小时前
计算机毕业设计|基于springboot + vue景区管理系统(源码+数据库+文档)
java·vue.js·spring boot·后端·课程设计
星辰徐哥3 小时前
Rust函数与流程控制——构建逻辑清晰的系统级程序
开发语言·后端·rust
Aliex_git3 小时前
Claude Code 使用笔记(一)- 配置和基础
人工智能·笔记·学习·ai编程