如何手搓一个Spring Security

"手搓"一个 Security 框架,实际上是为了透彻理解 Spring Security 等成熟框架背后的底层原理(如认证、授权、拦截机制)。基于现有的技术分析和实战案例,我们可以将这个过程简化为一个"极简版权限控制框架"的开发。这通常被称为"造轮子"或"徒手写安全框架"。

以下是结合 Java Web 技术栈,教你如何一步步"手搓"一个简易 Security 框架的核心步骤:

🧱 第一阶段:核心组件设计 (搭架子)

在 Spring Security 中,有 UserDetailsUserDetailsService 等概念。我们要先定义自己的核心模型。

  1. 定义用户实体 (User)

模拟框架中的 UserDetails

包含属性:用户名、密码、权限列表(例如 ["admin", "user"])。

  1. 定义认证服务 (AuthenticationService)

模拟框架中的 UserDetailsService

逻辑: 接收前端传来的用户名密码,与数据库(初期可用 Map 模拟)中的数据比对。

功能: 如果匹配成功,生成一个"登录凭证"(可以是 SessionId 或者简单的 Token 对象)。

🚧 第二阶段:拦截机制 (立屏障)

这是安全框架的"大门",所有请求必须先经过这里。

  1. 使用过滤器 (Filter) 或 拦截器 (Interceptor)

在请求到达 Controller 之前进行拦截。

排除路径: 放行登录页(/login)、静态资源、注册接口。

拦截路径: 拦截所有需要权限的接口(如 /admin/**, `/user/**)。

  1. 实现"认证"逻辑 (Authentication)

检查请求中是否携带了凭证(Cookie 中的 SessionId 或 Header 中的 Token)。

如果没有凭证,返回 401 Unauthorized 或重定向到登录页。

如果有凭证,去 Redis 或内存中校验该凭证是否有效(是否过期、是否被注销)。

🔑 第三阶段:授权逻辑 (分权限)

认证通过后,还要看用户有没有资格执行这个操作。

  1. 权限匹配

从凭证中获取当前用户的权限列表。

获取当前请求的 URL 或 方法名。

逻辑判断: 比如,请求 /admin/delete 需要 ROLE_ADMIN 权限。如果用户权限列表中包含该角色,则放行;否则返回 403 Forbidden

  1. 模拟注解功能

你可以通过 Java 反射机制,读取 Controller 方法上的自定义注解(例如 @RequireRole("ADMIN")),然后进行逻辑判断。

💻 第四阶段:代码实战 (极简版伪代码)

这里参考了常见的"徒手写 Security"教学案例,给出一个简化的逻辑流程:

1. 模拟用户登录 (认证)
java 复制代码
// 简化的伪代码
@PostMapping("/login")
public String login(String username, String password, HttpSession session) {
    // 1. 校验用户名密码 (模拟数据库查询)
    if ("admin".equals(username) && "123456".equals(password)) {
        // 2. 登录成功,将用户信息存入 Session
        session.setAttribute("user", new User("admin", Arrays.asList("admin", "user")));
        return "login success";
    }
    throw new RuntimeException("用户名或密码错误");
}
2. 拦截器进行权限校验 (授权)
java 复制代码
// 实现 HandlerInterceptor 接口
public class AuthInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        
        // 1. 获取 Session
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("user");
        
        // 2. 如果用户未登录
        if (user == null) {
            response.sendRedirect("/login"); 
            return false;
        }
        
        // 3. 权限校验逻辑 (例如:如果是访问 /admin 路径,需要 admin 角色)
        String uri = request.getRequestURI();
        if (uri.startsWith("/admin") && !user.getRoles().contains("admin")) {
            response.sendError(403, "权限不足");
            return false;
        }
        
        // 4. 校验通过,放行
        return true;
    }
}

🚀 第五阶段:进阶功能 (对标 Spring Security)

当你完成了上述基础流程,可以尝试添加以下功能来提升"逼格":

集成 Redis: 将 Session 存储在 Redis 中,实现分布式环境下的统一登录状态管理。

实现 JWT: 不使用 Session,改为生成 JWT Token,实现无状态认证。

方法级安全: 使用 AOP 切面,拦截带有特定注解(如@PreAuthorize),实现细粒度的控制。

CSRF 防护: 增加 Token 校验,防止跨站请求伪造攻击。

📌 注意点

1、密码存储必须加盐哈希

2、必须合理配置空闲超时和绝对超时时间

3、必须添加默认拒绝规则,必须防止越权访问

4、使用AOP必须主要拦截器失效的情况

5、注意路径匹配漏洞

6、记录审计、防止SQL注入

📌 总结

"手搓" Security 框架的核心在于理解 "过滤器链" 和 "领域模型"。

Spring Security 很复杂,因为它考虑了各种企业级场景(LDAP、OAuth2、RememberMe )。

你自己写的很简单,但只要包含了 "拦截请求 -> 校验身份 -> 校验权限 -> 放行/拒绝" 这一流程,你就已经掌握了其最核心的原理。

相关推荐
码农小卡拉8 小时前
深度解析 Spring Boot 启动运行机制
java·spring boot·后端
weixin_448119948 小时前
如何装docker
java·云原生·eureka
love_summer8 小时前
优雅地控制Python循环:break与continue的最佳实践及底层逻辑
后端
yaoxin5211238 小时前
288. Java Stream API - 创建随机数的 Stream
java·开发语言
kylezhao20198 小时前
C#根据时间加密和防止反编译
java·前端·c#
Assby8 小时前
Java异常体系结构
java·后端
喵叔哟8 小时前
19.服务集成与通信
后端·docker·容器·服务发现
superman超哥8 小时前
Iterator Trait 的核心方法:深入理解与实践
开发语言·后端·rust·iterator trait·trait核心方法
喵叔哟8 小时前
18.核心服务实现(下)
数据库·后端·微服务·架构