大家好,小编今天分享一下Spring拦截器鉴权,在讲解鉴权拦截之前,我们先来了解一下cookie、session、token(JWT)相关知识,帮助我们对接口鉴权型形成一个整体的知识框架。文章中存在错误之处,可以进行留言,博主会及时纠正。如果文章对您的学习有所帮助,不要忘记点赞哟!🤞🤞🤞🤞🤞🤞

目录

 一、Cookie

1、了解Cookie

2、特点 

3、适用场景

二、Session 

1、了解Session

2、Session认证流程

3、适用场景

三、Token(令牌)

1、了解Token

2、特点及结构

3、Token认证流程(例如登陆过程)

4、适用场景

四、Cookie、Session和Token区别总结

五、Spring拦截器实现鉴权

1、创建JWT工具类,生成Token令牌

2、拦截器鉴权实现

(1)拦截器介绍

(2)构造拦截器实现鉴权(示例代码


 一、Cookie

1、了解Cookie

HTTP协议本身不具备记录用户状态的能力,每次请求与响应都独立存在(无状态特性)。为了解决服务器无法识别连续请求来源的问题,Cookie技术应运而生,成为维持会话状态的桥梁。

其核心机制表现为:

(1)客户端存储机制
服务器通过响应头向浏览器下发微型数据片段(即Cookie),浏览器会将其持久化存储在本地。当用户后续访问同一服务器时,浏览器会自动在请求头中附加该域名下的所有有效Cookie,使服务器能通过读取这些数据识别用户身份或历史行为。

(2)域名隔离原则
Cookie通过域名绑定机制实现安全隔离,例如为"example.com"设置的Cookie默认不会被发送至"anothersite.com"。该限制通过两个维度实现:

  • 作用域控制:可显式定义生效域名(如允许子域共享时设置为".example.com")

  • 访问限制:浏览器严格遵循同源策略,禁止跨域名访问Cookie

注:同一域名下的不同端口默认共享Cookie,但现代浏览器逐渐加强端口隔离的安全性限制

2、特点 

  • 存储于客户端,默认通过 HTTP 请求头自动传递。
  • 支持设置过期时间(持久化 Cookie)或会话级失效(临时 Cookie)。
  • 安全性依赖 HTTPS 和属性设置(如 HttpOnlySecure) 

3、适用场景

  • 存储非敏感信息(如用户偏好、语言设置)。
  • 结合 Session 传递 Session ID(传统认证方案) 。
     

二、Session 

1、了解Session

  • session 是另一种记录服务器和客户端会话状态的机制

  • session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中

2、Session认证流程

(1) 会话初始化阶段
当客户端首次发起请求时,服务端通过以下步骤建立有状态会话:

  • 会话存储创建:验证用户凭证后,在服务端内存或分布式存储中生成会话数据容器(Session),内含用户身份、权限状态等敏感信息

  • 会话密钥同步:将全局唯一的会话标识符(SessionID)通过HTTP响应头Set-Cookie传递至客户端,例如:

Set-Cookie: session_id=5f4dcc3b5aa765d61d8327deb882cf99; Domain=.example.com; Path=/; HttpOnly 

(2)客户端持久化存储
浏览器接收到SessionID后执行:

  • Cookie容器写入:按照RFC 6265规范,将SessionID持久化存储至与域名绑定的Cookie存储区

  • 安全策略附加:根据服务端设置的Cookie属性(Secure/HttpOnly/SameSite等),限制JavaScript访问与传输条件

(3)会话维持阶段
客户端发起后续请求时触发自动凭证传递: 

  • 请求头自动装配:浏览器依据同源策略,自动在HTTP请求头中附加匹配当前域名的Cookie数据:

Cookie: session_id=5f4dcc3b5aa765d61d8327deb882cf99

  • 服务端会话验证:服务端执行链式验证:
    ① 从Cookie提取SessionID → ② 查询会话存储 → ③ 会话有效性判定(超时/主动销毁等)

  • 状态感知响应:根据验证结果执行分支逻辑:

    • 有效会话:读取会话数据,执行授权操作

    • 无效会话:返回401 Unauthorized或重定向至认证入口

3、适用场景

  • 需要服务端控制会话状态的场景(如电商购物车、权限管理)。

  • 传统单体架构的登录认证 。

三、Token(令牌)

1、了解Token

为了解决 Session 的扩展性问题,Token(如 JWT)采用无状态设计,将用户数据编码到 Token 中并签名,客户端自行存储 Token,服务端仅需验证签名。

2、特点及结构

(1)特点

  • 无状态:服务端无需存储 Token,通过签名验证合法性。

  • 自包含:Token 中直接编码用户信息(Payload)。

  • 可扩展性:支持分布式系统、跨域认证(如 OAuth、单点登录) 。

(2)结构 

  • Header:算法类型(如 HS256)。

  • Payload:承载用户数据(如用户 ID、角色)。

  • Signature:基于密钥对前两部分的签名,防篡改。

3、Token认证流程(例如登陆过程)

  • 用户在浏览器使用账号和密码进行登录,发送post请求

  • 服务端收到请求,去验证用户名与密码

  • 验证成功后,服务端会生成一个 token 并把这个 token 发送给客户端

  • 客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里

  • 客户端每次向服务端请求资源的时候需要带着服务端发送的token

  • 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据。

4、适用场景

  • 移动端 App 认证、微服务间鉴权、第三方登录(如微信登录)

四、Cookie、Session和Token区别总结

特性 Cookie Session Token(JWT)
存储位置 客户端(浏览器) 服务端(内存、数据库) 客户端(LocalStorage、Cookie)
安全性 较低(易被 XSS/CSRF 攻击) 较高(仅暴露 Session ID) 高(签名防篡改,需配合 HTTPS)
扩展性 受限于跨域策略 需解决分布式 Session 共享 天然支持分布式系统
典型场景 用户偏好设置、临时会话标识 传统 Web 应用的登录状态管理 移动端、API 鉴权、单点登录

五、Spring拦截器实现鉴权

1、创建JWT工具类,生成Token令牌

package com.springboot.until;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Date;

public class GenerateTokenUtil {

    /**
     * 生成token
     * @param username 用户名
     * @param password 密码
     * @return
     */
    public static String generateToken(String username, String password) {

        //token有效期12小时,12小时后过期
        Long expireTime = System.currentTimeMillis() + 1000 * 60 * 60 * 12;

        String token = JWT.create()
                .withAudience(username) //设置token的签发者
                .withSubject("token") //设置token的主题
                .withExpiresAt(new Date(expireTime)) //设置token的过期时间
                .sign(Algorithm.HMAC256(password)); //设置token的加密算法和密码

        return token;
    }

    /**
     * 解析token获取用户名
     * @param token
     * @return
     */
    public static String parseToken(String token) {
        return JWT.decode(token).getAudience().get(0);
    }

    /**
     * 如果token过期,则返回false,否则返回true
     * @param token
     * @param password
     * @return
     */
    public static boolean verifyToken(String token,String password) {
        try {
            Date expireTime =  JWT.decode(token).getExpiresAt();
            if (expireTime.before(new Date())) {//token已过期
                return false;
            }else {
                return true;
            }

        }catch (Exception e){
            return false;
        }
    }

}

2、拦截器鉴权实现

(1)拦截器介绍

 拦截器的主要是基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在Service或者一个方法前调用一个方法,或者在方法后调用一个方法,甚至在抛出异常的时候做业务逻辑的操作。它的作用就是拦截请求,在指定方法调用前后,根据业务需要执行预先设定的代码。

(2)构造拦截器实现鉴权(示例代码)

定义拦截器:

package com.springboot.config;

import com.alibaba.druid.util.StringUtils;
import com.springboot.pojo.Staff;
import com.springboot.service.Crud;
import com.springboot.service.CrudStaff;
import com.springboot.until.GenerateTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

@Component
@Slf4j
public class MyInterceptor  implements HandlerInterceptor {

    @Autowired
    private CrudStaff crudStaffService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求处理之前进行调用(Controller方法调用之前)
        // 返回true才会继续向下执行,返回false取消当前请求
        // 这个方法也是需要当前用户登录以后,才能进行操作。

        try {


            String token = request.getHeader("token");

            //设置白名单(不需要拦截的请求)
            if (request.getRequestURI().contains("/user/login") ||
                    request.getRequestURI().contains("/user/register")
            ) {
                return true;
            }

            if (!StringUtils.isEmpty(token)) {
                //校验用户信息是否存在
                String userName = GenerateTokenUtil.parseToken(token);
                Staff staff = crudStaffService.getByUserName(userName);
                if (Objects.isNull(staff)) {
                    log.info("用户信息不存在");
                    return false;
                }

                //校验token是否过期
                if (GenerateTokenUtil.verifyToken(token)) {
                    log.info("token未过期");
                }else {
                    log.info("token过期");
                    return false;
                }


            } else {
                log.info("token为空");
                return false;
            }

            return HandlerInterceptor.super.preHandle(request, response, handler);
        }catch (Exception e){
            log.info("拦截器异常");
            return false;
        }
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在整个请求结束之后被调用,也就是在DispatcherServlet渲染了对应的视图之后执行(主要是用于进行资源清理工作)
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

注意:

  1. preHandle方法:在请求处理之前进行调用(Controller方法调用之前),返回true才会继续向下执行,返回false取消当前请求
  2. postHandle方法:请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
  3. afterCompletion方法:在整个请求结束之后被调用,也就是在DispatcherServlet渲染了对应的视图之后执行(主要是用于进行资源清理工作)

注册配置拦截器

package com.springboot.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

public class MyWebConfig implements WebMvcConfigurer {

    @Autowired
    private MyInterceptor myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //定义拦截对象
        registry.addInterceptor(myInterceptor)
                .addPathPatterns("/**")  //拦截所有请求
                .excludePathPatterns("/user/login","/user/register"); //排除白名单(不拦截该请求路径)
    }
}

 至此,我们完成了Spring的拦截器的配置,之后遇到不需要拦截的请求路径,可以直接加入到白名单中。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐