做后端开发这几年,权限认证几乎是每个项目都绕不开的坎。登录、鉴权、Token 过期、接口防刷、角色权限……这些事看着简单,真做起来全是细节。Spring Security 功能全但配置复杂,Shiro 轻一些但文档老旧,很多中小型项目其实只需要把登录和权限控制做好,完全用不上那么重的体系。所以我整理了一套轻量级 Java 权限认证框架的完整源码,核心就三件事:登录发令牌、接口验令牌、注解控权限。这篇文章不是框架说明书,而是把我从零设计、手写、测试、踩坑的全过程都拆开讲一遍,想把权限这块搞清楚的朋友,不管你是刚转 Java 的新人,还是做了两三年的后端,大概率都能从中拿走一些能直接用的东西。
1. 为什么做这个轻量级 Java 权限认证框架
1.1 中小企业项目的真实痛点
先说需求来源。我接触过的很多项目,尤其是企业内部管理系统、外包交付项目、创业公司 MVP,它们对权限认证的要求其实非常朴素:用户登录后能拿到身份凭证,访问接口时能校验身份,部分敏感接口需要管理员或其他指定角色才能调。就这三板斧,撑起了 90% 的业务场景。
但真去落地的时候问题就来了。Spring Security 作为行业标配,功能确实强大,可它的过滤器链机制、SecurityContext 传递、多种认证提供者的抽象层次,对普通团队来说学习成本相当高。我见过一个团队引入 Spring Security 后,光是配置 SecurityFilterChain 就花了两天,最后因为没法从数据库动态加载权限,还是改回了代码里写死配置。项目周期短、人员流动快、业务变化频繁,这是中小企业项目的常态,重框架带来的复杂度往往比它解决的问题还多。
另一个选择是 Apache Shiro,它的侵入性比 Spring Security 小很多,Session 机制也直观,但 Shiro 主动维护的频率一般,跟 Spring Boot 新版本的集成时不常会遇到兼容问题。而且 Shiro 的注解权限校验默认走的是 AOP 代理,在自调用场景下容易失效,这个坑我后面会细讲。
我并不是说这些框架不好,而是想说明一个道理:技术选型要匹配业务规模和团队能力。当一个轻量级自研方案能把登录、鉴权、授权三件事控制在几百行代码内,而且源码完全掌握在自己手里时,对于大量 CRUD 型业务系统来说,这是比引入全家桶更务实的解法。
1.2 自研不是重复造轮子:轻量级方案的核心优势
有人可能会质疑,权限认证这种安全敏感的功能,自己写是不是容易写出漏洞?这个担心是合理的,但要看你怎么定义“自己写”。一个合格的轻量级方案不是把 AES、RSA、数字签名全玩一遍,那确实容易出事;而是站在成熟的加密库、成熟的协议规范之上,只做业务层的编排。
我这套框架依赖的核心组件就三个:JWT 做令牌载体,Redis 做会话存储,BCrypt 做密码散列。JWT 是一种开放标准,BCrypt 是公认安全的哈希算法,它们本身都是工业级产物,我要写的只是怎么把这三个东西串起来满足业务需求。这样一来,安全底子是由成熟方案保证的,框架层只处理“谁登录、持什么证、有什么权”这三个业务问题,复杂度自然降下来了。
轻量级的优势在真实项目里非常明显。首先是排障快,代码就那么多,出问题打开源码直接定位,不用在框架源码里翻来翻去;其次是改造灵活,比如要实现多端登录互踢、单设备登录、临时权限提升,在自定义框架里都是加几段逻辑的事,而在重框架里可能要研究半天扩展点;最后是启动优越,没有大量自动配置,Spring Boot 应用启动速度明显提升,这在微服务拆分的场景下很受用。
做一个轻量级框架最大的价值还不是省事,而是让你真正理解权限认证的底层原理。自己写过一遍拦截器、解析过一次 Token、设计过一回 RBAC 表结构,再去面试聊权限设计,“Java 八股文”里常考的单点登录、会话管理、无状态认证这些概念才能真正落地成自己的知识。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 框架的核心设计思路
2.1 模块划分:认证、会话、授权三件事分开
我设计这个框架时定了一个基本原则:认证、会话、授权三个职责必须拆开,不能揉成一坨。认证负责“你是谁”,会话负责“你这次登录持续多久”,授权负责“你能干什么”。这三个环节各自独立,才能保证框架的扩展性。
模块结构上分了四层。最底层是用户模型层,包含用户实体、角色、权限的数据结构,这一层对应数据库表设计;往上是认证服务层,负责处理登录请求、校验账号密码、生成令牌,这是登录入口的核心逻辑;再往上是访问控制层,由拦截器结合自定义注解完成接口的鉴权和授权;最外层是上下文层,通过 ThreadLocal 保存当前登录用户信息,方便业务代码随时取用。
这里有一个关键设计决定:授权信息不足时,框架是直接拒绝,还是交给业务方自己判断?我的方案是框架只做粗粒度的接口权限控制,也就是判断当前用户是否拥有访问某个接口所需的角色或权限码,而细粒度的数据权限(比如“只能查看自己创建的订单”)由业务代码通过 UserContext 获取当前用户 ID 后自行判断。这样既保证了通用性,又不会把框架做得太复杂。
2.2 Token 会话机制:JWT + Redis 双通道
会话管理是权限认证框架的核心,也是方案取舍最多的地方。常见的做法有两种:一种是无状态纯 JWT,服务端不保存会话,令牌自包含用户信息,校验时验签即可;另一种是全有状态 Session + Redis,用户信息存在服务端,客户端只拿一个随机 ID。
这两种方案各有硬伤。纯 JWT 的最大问题是无法主动失效。用户退出登录、修改密码、管理员封禁账号后,只要 JWT 没过期,理论上依然可以调用接口,这在安全要求稍微高一点的系统里是不能接受的。而纯 Session + Redis 的问题在于每次请求都要查一次 Redis,虽然加了连接池后性能损耗不大,但每次都把完整用户信息查出来放进内存,在权限列表很长的场景下是额外开销。
我的设计是两者结合:JWT 负责携带用户唯一标识和基础身份信息,Redis 负责存储会话动态状态。具体流程是这样:登录成功后生成两个东西,一个是 JWT 令牌(有效期设置为相对较短,比如 2 小时),一个是 Redis 中的会话记录(key 为 userId,value 为会话状态,比如是否被踢下线、退出标记)。每次请求校验时,拦截器先验 JWT 签名和有效期,然后再查 Redis 确认会话状态是否有效。
这个方案的好处在于,JWT 让鉴权过程无需访问数据库,性能损耗接近零;Redis 让会话不过期、强制下线、多端控制这类强需求变成可能;两者互补,各取所长。实际项目中我把 Redis 过期时间设置成比 JWT 有效期略长,比如 JWT 2 小时、Redis 7 天,配合双 Token 刷新机制就能实现长期免登录。
2.3 RBAC 权限模型与注解驱动机制
权限模型我直接用经典的 RBAC(基于角色的访问控制),用户关联角色、角色关联权限,三层结构。为什么不用更简单的用户直接关联权限?因为现实业务里权限的分配通常是按角色批量操作的,新来一个运营专员,应该一键继承“运营”角色下的全部权限,而不是一个个勾选接口权限。RBAC 真正解决的是一对多的授权管理效率问题。
权限的最小粒度我定义为权限码(Permission Code),比如“user:create”“order:delete”,一个接口对应一个权限码。角色持有权限码集合,用户通过角色间接获得权限。在数据库层面就是五张表:用户表、角色表、权限表、用户角色关联表、角色权限关联表。
框架对外暴露的编程接口是三个自定义注解。第一个是 @RequireLogin,标注这个接口需要登录才能访问;第二个是 @RequirePermission,标注这个接口需要指定权限码;第三个是 @IgnoreAuth,标注这个接口完全公开,比如登录接口、验证码接口。三个注
