# encryption **Repository Path**: 44346460/encryption ## Basic Information - **Project Name**: encryption - **Description**: 一个基于 Spring MVC 的轻量级请求/响应加解密统一处理框架。它通过注解驱动的方式,在 Controller 层自动完成请求体的解密和响应体的加密,支持 RSA 和 AES 两种算法,并为每个用户维护独立的密钥体系,实现真正的"一人一钥"安全策略。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-08-05 - **Last Updated**: 2026-07-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Encryption Framework ## 简介 `Encryption` 是一个基于 Spring MVC 的轻量级请求/响应加解密统一处理框架。它通过注解驱动的方式,在 Controller 层自动完成请求体的解密和响应体的加密,支持 RSA 和 AES 两种算法,并为每个用户维护独立的密钥体系,实现真正的"一人一钥"安全策略。 ## 核心特性 - **零侵入设计**:通过 `@Encrypted` / `@Decrypted` 注解即可启用,无需修改业务逻辑 - **多算法支持**:内置 RSA(非对称)和 AES(对称)算法,支持扩展自定义算法 - **用户密钥隔离**:每个用户拥有独立的 RSA 密钥对和 AES 密钥,降低单点泄露风险 - **游客账户体系**:支持匿名用户,自动管理生命周期和过期清理 - **跳过机制**:支持通过特定请求头跳过加解密,便于内部系统对接和调试 - **ISP 扩展架构**:基于接口隔离原则,所有核心组件均可自定义替换 ## 快速开始 ### 1. 引入依赖 将框架代码集成到您的 Spring Boot 项目中,确保依赖包含: - `spring-boot-starter-web` - `spring-boot-starter-data-redis` ### 2. 启用框架 在 Spring Boot 启动类或配置类上添加 `@EnableEncryption` 注解: ```java @SpringBootApplication @EnableEncryption public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` ### 3. 配置密钥存储 框架默认使用 Redis 存储用户密钥,确保已配置 Redis 连接。 ### 4. 在 Controller 中使用 ```java @RestController @RequestMapping("/api/user") public class UserController { /** * 接收加密后密文字符串,并自动解密为 LoginDTO对象 * 请求头需携带: uid=用户ID */ @PostMapping("/login") @Decrypted("RSA") // 使用 RSA 解密请求体 public UserDTO login(@RequestBody LoginDTO loginDTO) { // loginDTO 已经是解密后的明文对象 return userService.login(loginDTO); } /** * 返回加密的响应体 * 请求头需携带: uid=用户ID */ @GetMapping("/profile") @Encrypted("RSA") // 使用 RSA 加密响应体 public UserProfileDTO getProfile() { return userService.getProfile(); } /** * 使用 AES 加密响应(性能更优,适合大数据量) */ @PostMapping("/data") @Encrypted("AES") public BigDataDTO getBigData() { return bigDataService.fetch(); } } ``` ## 核心注解说明 ### `@Encrypted` - 响应加密 标记在 Controller 方法上,表示该方法的返回值需要加密后返回客户端。 ```java @Encrypted( value = "RSA", // 加密算法:RSA 或 AES,默认 RSA key = "uid" // 从请求头中获取用户标识的字段名,默认 "uid" ) ``` **工作流程:** 1. 方法执行完成后,获取返回值对象 2. 将对象序列化为 JSON 字节数组 3. 根据注解指定的算法,从 Redis 加载该用户的密钥 4. 执行加密(RSA 使用用户公钥 `ck`,AES 使用用户对称密钥 `ak`) 5. 将加密后的字节数组进行 Base64 编码 6. 返回 Base64 字符串给客户端 ### `@Decrypted` - 请求解密 标记在 Controller 方法上,表示该方法的 `@RequestBody` 参数需要解密。 ```java @Decrypted( value = "RSA", // 解密算法:RSA 或 AES,默认 RSA key = "uid" // 从请求头中获取用户标识的字段名,默认 "uid" ) ``` **约束条件:** - 必须是 **POST** 请求 - 参数上必须有 `@RequestBody` 注解 - 请求体必须是有效的 Base64 编码字符串 **工作流程:** 1. 从请求体读取 Base64 字符串 2. Base64 解码为字节数组 3. 根据注解指定的算法,从 Redis 加载该用户的密钥 4. 执行解密(RSA 使用服务端私钥 `sk`,AES 使用用户对称密钥 `ak`) 5. 将解密后的 JSON 字节数组转换为目标对象 6. 注入到 Controller 方法参数中 ## 密钥体系设计 ### UserKeyStoreDTO - 用户密钥存储模型 每个用户在系统中拥有以下密钥: | 字段 | 名称 | 说明 | 使用场景 | |------|------|------|----------| | `userId` | 用户标识 | 唯一标识用户 | 密钥索引 | | `ck` | Client Public Key | 客户端生成的 RSA 公钥 | 服务端加密响应时使用 | | `sk` | Server Private Key | 服务端生成的 RSA 私钥 | 服务端解密请求时使用 | | `ak` | AES Key | 服务端生成的对称密钥 | AES 加解密使用 | | `timestamp` | 创建时间 | 毫秒级时间戳 | 生命周期追踪 | ### 密钥生成时机 **推荐实践:** 用户登录时重新生成一套完整的密钥对。 ```java @Service public class AuthService { @Autowired private IUserKeyStoreService keyStoreService; public void login(String userId) { // 1. 生成新的 RSA 密钥对 String[] rsaPair = keyStoreService.generateKeyPair(userId); String publicKey = rsaPair[0]; // 服务端保留私钥,公钥给客户端 String privateKey = rsaPair[1]; // 2. 生成新的 AES 密钥 String aesKey = keyStoreService.generateAESKey(userId); // 3. 构建密钥存储对象 UserKeyStoreDTO keystore = new UserKeyStoreDTO(); keystore.setUserId(userId); keystore.setCk(publicKey); // 客户端公钥(客户端生成后上传) keystore.setSk(privateKey); // 服务端私钥 keystore.setAk(aesKey); // 服务端 AES 密钥 // 4. 持久化到 Redis keyStoreService.save(keystore); // 5. 将服务端公钥和 AES 密钥安全下发给客户端(通过加密通道) // 客户端使用服务端公钥加密请求,使用 AES 密钥加密大数据 } } ``` ### 游客(匿名)用户 框架支持匿名用户访问,通过 `IUserKeyStoreService#isAnonymous` 判断: - 游客密钥存储在独立的 Redis Hash 中 - 使用 Redis ZSet 维护过期时间,便于批量清理 - 默认有效期 7 天(可通过 `hyw.encryption.anonymous.ttl` 配置,单位:秒) ## 配置项 ### application.yml / application.properties ```yaml hyw: encryption: # 总开关,默认 true。设为 false 则关闭所有加解密功能 enable: true # 游客账户密钥有效期,默认 604800 秒(7天) anonymous: ttl: 604800 # 跳过加解密配置(用于内部系统对接、ESB 等场景) skipper: enable: true key: "esb" # 请求头名称 value: "c4d97ad826974dfa8f0500307f560c97" # 请求头值,匹配则跳过 ``` ### 跳过机制 当请求头中包含 `esb: c4d97ad826974dfa8f0500307f560c97` 时,框架将跳过加解密处理,直接透传原始数据。 **适用场景:** - 内部微服务之间调用 - ESB(企业服务总线)对接 - 调试和测试环境 - 第三方系统回调(无法支持加解密) ## 扩展与自定义 ### 1. 自定义加解密算法 实现 `IEncryptionProvider` 和 `IDecryptionProvider` 接口: ```java @Component public class SM2EncryptionProvider implements IEncryptionProvider, IDecryptionProvider { @Override public byte[] encryptByKey(String key, byte[] content) { // 使用国密 SM2 算法加密 return SM2Util.encrypt(content, loadPublicKey(key)); } @Override public byte[] decryptByKey(String key, byte[] content) { // 使用国密 SM2 算法解密 return SM2Util.decrypt(content, loadPrivateKey(key)); } @Override public String getAlgorithm() { return "SM2"; // 注解中使用 @Encrypted("SM2") } } ``` ### 2. 自定义密钥提取方式 默认从 HTTP 请求头中提取 `uid`,您可以自定义提取逻辑: ```java @Component public class TokenKeyExtractor implements IEncryptKeyExtract, IDecryptKeyExtract { @Autowired private JwtTokenUtil jwtTokenUtil; @Override public String extract(HttpHeaders headers, String key) { // 从 JWT Token 中解析用户ID String token = headers.getFirst("Authorization"); return jwtTokenUtil.getUserIdFromToken(token); } } ``` ### 3. 自定义密钥存储服务 实现 `IUserKeyStoreService` 接口,例如使用数据库存储: ```java @Service public class DbUserKeyStoreService implements IUserKeyStoreService { @Autowired private UserKeyRepository repository; @Override public Optional load(String userId) { return repository.findByUserId(userId) .map(this::convertToDTO); } @Override public void save(UserKeyStoreDTO keystore) { repository.save(convertToEntity(keystore)); } // ... 其他方法实现 } ``` ### 4. 游客账户判定规则 默认以 `Anony` 前缀判断游客,可通过构造 `DefaultUserKeyStoreServiceImpl` 时传入自定义的 `Predicate`: ```java @Bean public IUserKeyStoreService userKeyStoreService(RedisTemplate redisTemplate) { // 以 "guest_" 或 "temp_" 开头的都视为游客 Predicate anonymousPredicate = uid -> uid.startsWith("guest_") || uid.startsWith("temp_"); return new DefaultUserKeyStoreServiceImpl(redisTemplate, 604800L, anonymousPredicate); } ``` ## 安全建议 1. **密钥轮换**:建议定期(如每次登录)重新生成用户密钥对,框架已提供 `generateKeyPair` 和 `generateAESKey` 方法 2. **HTTPS 必配**:本框架只解决应用层加解密,传输层必须使用 HTTPS 防止中间人攻击 3. **游客清理**:生产环境建议配置定时任务清理过期游客数据,虽然框架使用 ZSet 支持按时间清理,但仍需业务层触发 4. **Skipper 密钥保护**:`hyw.encryption.skipper.value` 相当于一个后门密钥,务必妥善保管,生产环境建议定期更换 5. **AES 密钥长度**:框架默认生成 16 位混合字符的 AES 密钥,确保符合 AES-128 要求 ## 技术架构 ``` +-------------------------------------------------------------+ | Client (前端/移动端) | | +-------------+ +-------------+ +-------------+ | | | RSA Public | | RSA Private| | AES Key | | | | (Server) | | (Client) | | (Server) | | | +------+------+ +------+------+ +------+------+ | +--------+----------+----------------+----------+-------------+ | | | | | | | | v | | v Encrypt Request | | Decrypt Response (Server Public) | | (Server AES Key) | | +-------------------+----------------+------------------------+ | v | | +-----------------------------------------------------+ | | | Spring MVC Controller | | | | @Decrypted("RSA") <- DecryptRequestBodyAdvice | | | | @Encrypted("RSA") -> EncryptResponseBodyAdvice | | | +-----------------------------------------------------+ | | | | | +----------------+----------------+--------------------+ | | | ISP Provider | Key Extractor | | | | +-----------------+ | +-----------------+ | | | | | RSA Provider |<---+ | HttpHeader | | | | | | AES Provider |<---+ | KeyExtractor | | | | | | Custom Provider | +-----------------+ | | | | +-----------------+ | | | +-------------------------------------------------------+ | | | | | +----------------+----------------+--------------------+ | | | IUserKeyStoreService | Redis | | | | +-----------------+ | +-----------------+ | | | | | load(userId) |<---+ | Hash: Reg Keys | | | | | | save(keystore) |----+---> | Hash: Anon Keys | | | | | | generateKeyPair | | | ZSet: Expiry | | | | | +-----------------+ | +-----------------+ | | | +-------------------------+ | | +-----------------------------------------------------------+ ``` ## 核心接口速查 | 接口 | 职责 | 默认实现 | |------|------|----------| | `IEncryptionProvider` | 加密算法实现 | `DefaultRSAEncryptionProvider`, `DefaultAESEncryptionProvider` | | `IDecryptionProvider` | 解密算法实现 | `DefaultRSADecryptionProvider`, `DefaultAESDecryptionProvider` | | `IUserKeyStoreService` | 用户密钥的 CRUD | `DefaultUserKeyStoreServiceImpl` (Redis) | | `IEncryptKeyExtract` | 从上下文提取加密用的用户标识 | `HttpHeaderKeyExtractor` | | `IDecryptKeyExtract` | 从上下文提取解密用的用户标识 | `HttpHeaderKeyExtractor` | ## 注意事项 1. **请求方法限制**:`@Decrypted` 只支持 POST 请求,且参数必须有 `@RequestBody` 2. **Base64 编码**:所有加密结果和待解密数据都必须是标准 Base64 编码 3. **请求头要求**:默认需要请求头携带 `uid` 字段用于定位用户密钥,可通过 `key()` 属性自定义 4. **空值处理**:加密时响应体不允许为 `null`,解密时支持空请求体透传 5. **算法匹配**:注解中的算法值必须与某个 `Provider#getAlgorithm()` 返回值匹配,否则抛出异常 ## 版本要求 - Java 8+ - Spring Boot 2.x / Spring Framework 5.x - Redis 3.0+ ## 作者 Hongyu