用自定义注解 + 拦截器实现登录鉴权

简介: 通过自定义注解 `@Login` 结合 Spring 拦截器,实现声明式登录校验。无需重复编码,自动拦截未登录请求,提升代码可维护性与安全性,适用于前后端分离架构的权限控制实践。

在 Web 应用中,我们经常需要对某些接口进行登录状态校验:  

  • 公共接口(如登录、注册)→ 无需登录;  
  • 业务接口(如查询订单、修改资料)→ 必须登录

传统做法是在每个 Controller 方法里手动判断 session 或 token,但这样代码重复、难以维护。

更好的方式是:用自定义注解 + Spring 拦截器,实现声明式鉴权。


一、定义自定义注解 @Login

import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Login {
    // 默认值为 YES,表示需要登录;设为 NO 则跳过校验
    YesOrNo value() default YesOrNo.YES;
}

配套枚举:

public enum YesOrNo {
    YES, NO
}

✅ 这样设计的好处:  

  • 大多数接口默认需登录(安全优先);  
  • 仅对少数公开接口显式标注 @Login(YesOrNo.NO)

二、实现 Spring MVC 拦截器

注意:这里使用的是 HandlerInterceptor(Spring MVC 拦截器),而非 Servlet Filter

它能直接获取到 Controller 方法的元信息(如注解),更适合做方法级鉴权。

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception {
        
        // 只处理 Controller 方法(排除静态资源等)
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Login login = handlerMethod.getMethodAnnotation(Login.class);
        // 如果方法上有 @Login(NO),放行
        if (login != null && YesOrNo.NO.equals(login.value())) {
            return true;
        }
        // 否则:检查是否已登录
        if (!UserAuthHelper.isAuthenticated(request)) {
            // 未登录,返回 JSON 错误
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setContentType("application/json;charset=UTF-8");
            PrintWriter writer = response.getWriter();
            writer.write(JSON.toJSONString(ResultMessage.error("请先登录")));
            writer.flush();
            return false; // 中断请求
        }
        return true; // 放行
    }
}

关键点说明:

  1. HandlerMethod:可获取被调用的 Controller 方法,进而读取其上的注解;
  2. UserAuthHelper.isAuthenticated():封装了登录状态判断逻辑(如从 Session、Token、Redis 中验证用户);
  3. 返回 false:中断请求链,不再执行 Controller 方法;
  4. 统一错误格式:返回 JSON 而非跳转页面,适配前后端分离架构。

三、注册拦截器

在 Spring Boot 中配置:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")          // 拦截所有请求
                .excludePathPatterns("/login", "/static/**", "/error"); // 排除公开路径
    }
}

💡 即使有 @Login(NO),也可以通过 excludePathPatterns 提前放行高频公开接口,提升性能。


四、在 Controller 中使用

@RestController
@RequestMapping("/api/user")
public class UserController {
    // 公开接口:无需登录
    @Login(YesOrNo.NO)
    @PostMapping("/filter")
    public ResultMessage filter(String companyId, String code) {
        // 业务逻辑
        return ResultMessage.ok("查询成功", data);
    }
    // 默认需登录(可省略 @Login)
    @GetMapping("/profile")
    public ResultMessage profile() {
        // 自动校验登录状态
        return ResultMessage.ok("获取成功", userInfo);
    }
}

✅ 优势:  

  • 无需在每个方法写 if (!login) return error;;  
  • 权限规则集中管理,修改只需调整拦截器;  
  • 语义清晰,一眼看出接口是否需要登录。

五、对比:拦截器 vs 过滤器(Filter)

特性 Filter(Servlet) HandlerInterceptor(Spring MVC)
执行时机 更早(在 DispatcherServlet 之前) 在 Spring MVC 流程中
能否获取 Controller 方法信息 ❌ 不能 ✅ 能(通过 HandlerMethod
是否支持 Spring 注入 ❌ 需手动获取 Bean ✅ 可直接注入 Service/Bean
适用场景 编码、日志、全局异常 方法级权限、参数预处理

📌 结论:做方法级注解鉴权,优先选 拦截器


六、扩展方向

  1. 角色鉴权:扩展 @Login(role = "ADMIN")
  2. 多端支持:区分 Web / App / 小程序 的 token 校验逻辑;
  3. 自动续期:在拦截器中刷新 token 有效期;
  4. 结合 Spring Security:作为轻量级替代方案,或与之互补。

总结

通过 自定义注解 + Spring 拦截器,我们实现了:

  • 声明式登录控制
  • 零侵入业务代码
  • 高可维护性与可读性

这种模式不仅适用于登录鉴权,还可用于权限、限流、审计等横切关注点,是构建整洁、健壮 Web 系统的重要实践。


相关文章
|
2月前
|
JSON 安全 fastjson
使用阿里巴巴 Fastjson 替代 Spring Boot 默认的 Jackson
本文介绍在 Spring Boot 项目中如何替换默认的 Jackson,集成阿里巴巴 Fastjson 作为 JSON 处理框架。内容涵盖 Fastjson 与 Jackson 的核心对比、依赖配置、自定义消息转换器、null 值统一处理及循环引用控制,并提供安全建议与最佳实践,助你高效、安全地使用 Fastjson。
|
安全 C++ Windows
好工具推荐系列:VC++开发必备神器 -- Dependencies,查看依赖库DLL,支持win10,比depends更好用
好工具推荐系列:VC++开发必备神器 -- Dependencies,查看依赖库DLL,支持win10,比depends更好用
4599 0
好工具推荐系列:VC++开发必备神器 -- Dependencies,查看依赖库DLL,支持win10,比depends更好用
|
运维 新制造 数据可视化
带你读《智能制造之卓越设备管理与运维实践》之一:设备运维管理的新挑战
本书从工业企业实际需求出发,结合智能制造环境下的紧迫需求,融合作者信息化咨询与项目工作实践,以理论联系实际,将设备的全生命周期管理、精益管理、全员维护、先进的维护策略(预测性维护、智能维护)等管理理与信息化技术进行融合设计,以“IE+IT”的思想实现管理平台与信息平台的平衡发展。
|
Java Maven
Idea配置项目的热启动
Idea配置项目的热启动
2071 5
Idea配置项目的热启动
|
Java 应用服务中间件
SpringBoot工程打包部署
SpringBoot工程打包部署简介:SpringBoot项目可通过三种方式运行:可执行Jar包、可执行War包和标准War包。其中,可执行Jar/War包可独立运行,标准War包需部署在Tomcat中。具体步骤包括:1. 修改pom.xml添加构建依赖;2. 执行`mvn clean package`命令打包;3. 运行生成的Jar/War包(如`java -jar xxx.jar`)。对于标准War包,还需修改启动类并配置Tomcat依赖。
1032 7
|
IDE 开发工具
【开发IDE升级】如何对IDEA版本进行升级
本文介绍了如何将 IntelliJ IDEA Ultimate 从 2020.2.2 版本升级到 2022.3.2 版本。主要内容包括准备工作、卸载旧版本和安装新版本的步骤。首先,从官网下载所需版本并备份旧版配置;接着,通过 Uninstall.exe 卸载旧版,保留配置和插件;最后,安装新版并完成激活。详细的操作步骤和截图帮助用户顺利完成升级过程。
14207 1
【开发IDE升级】如何对IDEA版本进行升级
|
存储 安全 算法
Java面试题之Java集合面试题 50道(带答案)
这篇文章提供了50道Java集合框架的面试题及其答案,涵盖了集合的基础知识、底层数据结构、不同集合类的特点和用法,以及一些高级主题如并发集合的使用。
1371 1
Java面试题之Java集合面试题 50道(带答案)
|
存储 Cloud Native Java
Windows下Minio的安装以及基本使用
MinIO 是一个开源的云原生分布式对象存储系统,兼容亚马逊S3接口,适合存储大容量非结构化数据。本文介绍Windows下MinIO的安装与基本使用:通过以上步骤,您可以在Windows环境中成功安装并使用MinIO。
9205 19
|
消息中间件 存储 Java
MQ核心作用、解耦、削峰使用场景详解
【11月更文挑战第21天】在如今的高并发互联网应用中,如何确保系统在巨大的流量冲击下还能稳定运行,是每个技术团队都会遇到的挑战。说到这,消息队列(MQ)就是背后的“大功臣”了。无论是异步处理请求、平滑应对流量高峰,还是让各个系统模块相互独立不“拖后腿”,MQ都是不可或缺的帮手。那么,MQ是如何削峰的?或者它是如何让复杂系统解耦的?今天,我们就来聊聊MQ的三大核心功能,看它是如何助力系统高效、稳定运转的。
971 1