yashashvini15/Secure-Login-JWT-Authentication-Office-Task

GitHub: yashashvini15/Secure-Login-JWT-Authentication-Office-Task

基于 Spring Boot 的安全登录模块,实现 JWT 认证、Refresh Token 轮换、RBAC 权限控制及 OWASP 安全加固,并附带 16 项常见认证漏洞的分析与修复文档。

Stars: 0 | Forks: 0

# 安全登录模块 — JWT, Refresh Token, RBAC 一个基于 Spring Boot 的认证与授权模块,实现了基于 JWT 的无状态认证、Refresh Token 轮换、基于角色的访问控制(RBAC)、输入验证,以及符合 OWASP 标准的安全实践。 ## 📌 作业目标 设计并实现一个安全的登录模块,并识别典型认证实现中的安全漏洞,涵盖: - 基于 JWT (JSON Web Token) 的认证 - Refresh Token 机制 - 基于角色的访问控制 (RBAC) - 密码哈希与输入验证 - OWASP 安全基础 ## 🛠️ 技术栈 | 组件 | 技术 | |---|---| | 语言 | Java 17 | | 框架 | Spring Boot | | 安全 | Spring Security | | 数据库 | MySQL (通过 Spring Data JPA / Hibernate) | | JWT 库 | JWT (io.jsonwebtoken) 0.12.6 | | 密码哈希 | BCrypt | | 构建工具 | Maven | ## 🏗️ 架构概述 ``` Client (Postman / Frontend) │ ▼ [POST /api/auth/signup] → Validate input → Hash password → Save user │ ▼ [POST /api/auth/login] → Verify password → Issue Access Token (JWT) + Refresh Token (Cookie) │ ▼ [Protected APIs] → JwtAuthFilter verifies token → Role checked (RBAC) → Response │ ▼ [POST /api/auth/refresh-token] → Rotate Refresh Token → New Access Token │ ▼ [POST /api/auth/logout] → Revoke Refresh Token ``` ### 请求流程 (受保护的路由) ``` Request → JwtAuthFilter (validates JWT, sets SecurityContext) → SecurityConfig (checks role/authorization rules) → Controller (business logic) → Response ``` ## 🗄️ 数据库设计 **`users` 表** | 列名 | 类型 | 备注 | |---|---|---| | id | BIGINT (PK) | 自增 | | name | VARCHAR(50) | 非空 | | email | VARCHAR | 唯一,非空 | | password_hash | VARCHAR | BCrypt 哈希 — 绝不使用明文 | | role | VARCHAR | `USER` 或 `ADMIN` | | created_at | TIMESTAMP | | **`refresh_tokens` 表** | 列名 | 类型 | 备注 | |---|---|---| | id | BIGINT (PK) | 自增 | | user_id | BIGINT | 外键引用 | | token | VARCHAR(500) | 唯一 | | expiry_date | TIMESTAMP | | | revoked | BOOLEAN | 用于轮换/重用检测 | | device_info | VARCHAR | 可选 | ## 🔌 API Endpoints | Endpoint | 方法 | 访问权限 | 描述 | |---|---|---|---| | `/api/auth/signup` | POST | 公开 | 注册新用户(默认角色:`USER`) | | `/api/auth/login` | POST | 公开 | 认证用户,签发 Access + Refresh Token | | `/api/auth/refresh-token` | POST | 公开 (基于 Cookie) | 轮换 token,签发新的 Access Token | | `/api/auth/logout` | POST | 已认证 | 吊销 Refresh Token | | `/api/user/profile` | GET | 任何已认证用户 | 获取个人资料(安全的 DTO,无密码) | | `/api/admin/users` | GET | 仅限 `ADMIN` | 列出所有用户 — 强制执行 RBAC | ## 🔐 已实现的安全特性 ### 1. 密码哈希 - 使用 BCrypt (`BCryptPasswordEncoder`) — 密码绝不以明文形式存储。 - Salting 由 BCrypt 自动处理。 ### 2. JWT 认证 - 通过签名的 JWT (HMAC SHA-512) 进行无状态认证。 - Claims 包含: `sub` (email), `role`, `iat`, `exp`。 - 较短的有效期(15分钟),以在 token 泄露时限制暴露风险。 - Payload **不包含敏感数据**(无密码,无 PII)。 ### 3. 带轮换的 Refresh Token - Refresh Token 存储在服务器端(数据库),有效期较长(7天)。 - 存储在 `httpOnly`, `Secure`, `SameSite=Strict` cookie 中 — JavaScript 无法读取(防范 XSS)。 - **轮换**:每次刷新都会签发新 token 并使旧 token 失效。 - **重用检测**:如果重放已吊销/已使用的 token,该用户的所有会话都将被吊销(表明 token 被盗用)。 ### 4. RBAC (基于角色的访问控制) - 角色(`USER`, `ADMIN`)作为 JWT claim 嵌入。 - 在两个层面上强制执行: - URL 层面: `SecurityConfig` → `.requestMatchers("/api/admin/**").hasRole("ADMIN")` - 方法层面: 在 controller 方法上使用 `@PreAuthorize("hasRole('ADMIN')")`。 - 角色**始终在服务器端设置**(注册时硬编码为 `"USER"`) — 绝不接受来自客户端的输入,防止权限提升。 ### 5. 输入验证 - Bean Validation (JSR-380) 注解: `@NotBlank`, `@Email`, `@Size`, `@Pattern`。 - 强制要求密码复杂度(大写字母、小写字母、数字、特殊字符,至少 8 个字符)。 - 验证在服务器端进行 — 绝不仅依赖客户端。 ### 6. 通用错误消息 - 登录失败返回通用的 `"Invalid credentials"` 消息(而不是“邮箱不存在”或“密码错误”),以防止用户枚举攻击。 ### 7. 集中式异常处理 - `GlobalExceptionHandler` (`@RestControllerAdvice`) 确保不会向客户端暴露任何堆栈跟踪或内部细节。 ### 8. 使用 DTO 代替实体 - API 响应使用 DTO(`UserProfileResponse`, `LoginResponse`) — `User` 实体(包含 `passwordHash`)绝不会在响应中被直接序列化。 ## 🚨 识别出的安全漏洞(常见错误)及已应用的修复 | # | 安全漏洞 | 风险 | 应用的修复 | |---|---|---|---| | 1 | 以明文或使用 MD5/SHA1 存储密码 | 发生泄露时所有凭证外泄 | BCrypt 哈希(缓慢、加盐) | | 2 | 明确的登录错误消息(区分邮箱或密码错误) | 用户/邮箱枚举攻击 | 通用的 `"Invalid credentials"` 消息 | | 3 | JWT payload 中包含敏感数据 | Payload 是编码而非加密的 — 任何人都可以读取 | 仅存储非敏感 claims (email, role) | | 4 | 长期有效的 Access Token | 如果 token 被盗,攻击窗口极大 | 15 分钟有效期 + Refresh Token 模式 | | 5 | Refresh Token 存储在 `localStorage` 中 | 容易受到 XSS 攻击而被盗 | `httpOnly` + `Secure` + `SameSite=Strict` cookie | | 6 | `/login` 没有速率限制 | 暴力破解密码 | *(记录为推荐的增强功能 — 见未来改进)* | | 7 | 原始 SQL 字符串拼接 | SQL 注入 | Spring Data JPA (自动使用预编译语句) | | 8 | 在 API 响应中返回完整的 `User` 实体 | 在响应体中泄露 `password_hash` | 所有响应均使用专用的 DTO | | 9 | 仅在前端进行角色检查(隐藏 UI 按钮) | 可通过 Postman/curl 直接访问后端绕过 | 通过 `@PreAuthorize` + URL 匹配器进行后端强制验证 | | 10 | 资源访问时没有归属权检查 (IDOR) | 用户 A 可以通过更改 ID 访问用户 B 的数据 | 在返回数据前在服务器端验证归属权 | | 11 | 错误响应中返回详细的堆栈跟踪 | 向攻击者暴露内部结构 | `GlobalExceptionHandler` 仅返回通用消息 | | 12 | 没有 HTTPS | 中间人攻击,token/密码被拦截 | 强制执行 `Secure` cookie 标志(生产环境中必须使用 HTTPS) | | 13 | 没有 Refresh Token 重用检测 | 被盗 token 可在有效期内被静默使用 | Token 轮换 + 重用检测(自动吊销所有会话) | | 14 | 在 JWT 验证期间盲目信任 `alg` 头 | `alg:none` 攻击 — 伪造的 token 被接受 | 通过签名密钥类型显式强制指定算法 | | 15 | 在注册期间接受来自客户端的 `role` | 任何人都可以自行注册为 `ADMIN` | 角色在服务器端硬编码为 `"USER"`;绝不从请求体中读取 | | 16 | 自助“更改我的角色” endpoint | 权限提升 — 任何用户都可以提升自己的权限 | 不存在这样的 endpoint;角色更改需要现有的 `ADMIN`(或数据库层面的引导) | ## 🧪 测试证据(手动,通过 Postman) | 测试用例 | 预期结果 | 状态 | |---|---|---| | 使用有效数据注册 | `200 OK` | ✅ 通过 | | 使用正确的凭证登录 | `200 OK` + Access Token + Refresh Token cookie | ✅ 通过 | | 使用有效 token 访问 `/api/user/profile` | `200 OK` + 个人资料数据 | ✅ 通过 | | 以 `USER` 角色访问 `/api/admin/users` | `403 Forbidden` | ✅ 通过 | | 以 `ADMIN` 角色访问 `/api/admin/users`(更新角色并重新登录后) | `200 OK` + 用户列表 | ✅ 通过 | | 使用过期的 token 访问 `/api/user/profile` | `403 Forbidden`(平滑拒绝,无崩溃) | ✅ 通过 | | Refresh Token endpoint | `200 OK` + 新的 Access Token + 轮换后的 cookie | ✅ 通过 | | 登出 | Refresh Token 从数据库中吊销 | ✅ 通过 | **关键观察:** 在角色更改(`USER`)之前签发的旧 JWT 将以*旧*角色保持有效,直到它过期 — 用户必须重新登录才能获得反映更新后 `ADMIN` 角色的 token。这演示了无状态 JWT 著名的**陈旧 claims 权衡 (stale-claims tradeoff)**。 ## 🔮 未来改进(未实现 — 为完整性而记录) - 在 `/login` 和 `/signup` 上进行**速率限制**,以防止暴力破解攻击。 - 多次登录失败后的**账户锁定**。 - 如果系统演变为微服务架构,使用 **RS256 (非对称签名)** 代替 HS256。 - **Token 黑名单 (Redis)**,用于在登出/安全事件发生时立即撤销 Access Token。 - 针对管理员操作和失败登录尝试的**审计日志**。 - 在注册/登录时使用 **CAPTCHA** 来防范机器人。 - 将 JWT 密钥移至环境变量或 secrets manager 中,而不是 `application.properties`。 ## ⚙️ 设置说明 1. 克隆代码库。 2. 创建一个 MySQL 数据库: CREATE DATABASE secure_login_db; 3. 使用您的 MySQL 凭据更新 `src/main/resources/application.properties`。 4. 运行应用程序: mvn spring-boot:run 5. 使用 Postman 测试 endpoints(上面列出的集合 endpoints)。 ## 📚 展示的关键概念 - 认证与授权 - 密码哈希 (BCrypt) 与加密 - JWT 结构(Header, Payload, Signature)及其无状态特性 - Access Token 与 Refresh Token 的权衡 - Refresh Token 轮换与重用检测 - 多层 RBAC 强制执行 - OWASP Top 10 相关风险:SQL 注入、XSS、CSRF、失效的认证、失效的访问控制、安全配置错误、敏感数据泄露
标签:JWT, RBAC, Spring Boot, Syscall, Web开发, 域名枚举