简介
此项目用于演示popularmvc如何提供统一全自动化的API隐私数据保护,并且可以做到业务无感和灵活指定数据加解密算法。
请求数据加密使用
@Decrypt
注解,响应信息加密使用@Encrypt
注解,使用自定义算法进行加解密请实现DataEncryptHandler
接口,注解中指定即可。
- 请求数据解密
- 响应信息加密
- 指定自定义解密算法
- 指定自定义加密算法
项目示例
1 项目结构
-
项目结构
│ pom.xml │ README.md │ ├─src │ ├─main │ │ ├─java │ │ │ └─com │ │ │ └─danyuanblog │ │ │ └─framework │ │ │ └─demo │ │ │ └─popularmvc │ │ │ │ StartDemoApplication.java │ │ │ │ │ │ │ ├─component │ │ │ │ RSADataEncryptHandler.java │ │ │ │ │ │ │ ├─controller │ │ │ │ │ TestCustomEncryptAndDecryptController.java │ │ │ │ │ TestEncryptAndDecryptController.java │ │ │ │ │ │ │ │ │ └─dto │ │ │ │ UserInfoDto.java │ │ │ │ │ │ │ └─utils │ │ │ RSAEncryptUtil.java │ │ │ │ │ └─resources │ │ application.yml │ │ │ └─test │ └─java │ └─com │ └─danyuanblog │ └─framework │ └─popular │ └─mvc └─target ├─classes │ │ application.yml │ │ │ └─com │ └─danyuanblog │ └─framework │ └─demo │ └─popularmvc │ │ StartDemoApplication.class │ │ │ ├─component │ │ RSADataEncryptHandler.class │ │ │ ├─controller │ │ │ TestCustomEncryptAndDecryptController.class │ │ │ TestEncryptAndDecryptController.class │ │ │ │ │ └─dto │ │ UserInfoDto.class │ │ │ └─utils │ RSAEncryptUtil.class │ └─test-classes └─com └─danyuanblog └─framework └─popular └─mvc
-
引入模块依赖,在
pom.xml
添加
<dependency>
<groupId>com.danyuanblog.framework</groupId>
<artifactId>popular-web-mvc</artifactId>
<version>${popular-web-mvc.version}</version>
</dependency>
2 启用PopularMvc框架
/**
* Title StartDemoApplication.java
* Description
* @author danyuan
* @date Oct 31, 2020
* @version 1.0.0
* site: www.danyuanblog.com
*/
package com.danyuanblog.framework.demo.popularmvc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.danyuanblog.framework.popularmvc.annotation.EnablePopularMvc;
@SpringBootApplication
@EnablePopularMvc
public class StartDemoApplication {
public static void main(String[] args) {
SpringApplication.run(StartDemoApplication.class, args);
}
}
3 配置信息
秘钥信息配置如下
application.yml
popularmvc:
api:
channels:
default: #用于不区分渠道信息的应用
default: #默认的秘钥信息
enable: true
#用于对称加解密、生成数字签名、验证数字签名的秘钥
secret: "123456"
keyPair: #用于非对称加解密的秘钥对
privateSecret: "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIZjqviU3YAn3yOftco++Ya+KcuE6hC3scAvh7CoC4bgYTAqWZhNmdXOu4I5jzd2rhz2r6E6CInRhhY+m9kRIKN8GKP7hsG2/GibY/iK304zxqetzhvX9jd8D/f9riTqd6N09g3zBkmh6XUBvbha52Xksi4UzXJhV3ZVgfWv1t4nAgMBAAECgYByVrQtQQLfuYypE4Yo58GHOZ33sUMwLAoImKfazm7YN2mZAD8wTL3Y4kY4ut418zyaGew9wVFoaKKrpGMWoPLhvZiMZGb92r5SIb4C9gI3S6XHKYoOaVXi8oSTkCF0duoSQdCy3w3CGZbdfTEO/qtT25CePyGB2c1bYj0VULUm2QJBAPTzhh5ndTyuGiV+wbnWk6x6gcONNUGpTuvdGkcNBw/fn6QAA44CA+LKdFOMDB2QQmO3vF/JhQEjG6L4mbq8A6UCQQCMc3l40RwRypJ3A9RbwrYtO1c0X2VHdGCpi1L4FMxLWdTB0c7y9HbbXcJdL0L3Bl48Go0bwzzNSkfr2vqR9MDbAkBcZqjHO1u8QijW0BQgGFOokqX0sIXQeR+uVb+d4coyMLc11FOC9DunB5wOEBxZ4ptIpnzG3Wvw29+HAKRtDpOJAkBwoBSTTlPM7H0glOCQKIY/pSsbozeq4ea1bjS9HKhp8AIM3jquVlyNMhUu9jwjrGaamcv3rEqwcFVWC0YNDpArAkBYjalgYWGq6hRTjYSYJ82mdHy2EGxGhBkq51U871uaReP8c/a5wGjxPEyZMclF5+fpfFTwD+F8JZReVqa70eh6df723820"
publicSecret: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCGY6r4lN2AJ98jn7XKPvmGvinLhOoQt7HAL4ewqAuG4GEwKlmYTZnVzruCOY83dq4c9q+hOgiJ0YYWPpvZESCjfBij+4bBtvxom2P4it9OM8anrc4b1/Y3fA/3/a4k6nejdPYN8wZJoel1Ab24Wudl5LIuFM1yYVd2VYH1r9beJwIDAQAB"
appId1: #某个应用下的秘钥信息
enable: true
#用于对称加解密、生成数字签名、验证数字签名的秘钥
secret: "123456"
keyPair: #用于非对称加解密的秘钥对
privateSecret: ""
publicSecret: ""
android:
default:
enable: true
#用于对称加解密、生成数字签名、验证数字签名的秘钥
secret: "123456"
keyPair: #用于非对称加解密的秘钥对
privateSecret: ""
publicSecret: ""
ios:
default:
enable: true
secret: "12345678"
keyPair:
privateSecret: ""
publicSecret: ""
4 演示代码解析
4.1 使用默认的AES进行加解密
API数据的加解密的使用主要分两种应用场景:
对api调用者传递过来的接口请求内的隐私信息进行解密,然后交由业务去处理,popularmvc对业务屏蔽了加解密的过程,业务可以直接使用明文进行处理
将API返回隐私信息加密后再返回给接口调用者,接口调用者拿到后再进行解密后处理
示例代码如下所示。
-
接口源码
TestEncryptAndDecryptController.java
/** * Title TestEncryptAndDecryptController.java * Description * @author danyuan * @date Jan 4, 2021 * @version 1.0.0 * site: www.danyuanblog.com */ package com.danyuanblog.framework.demo.popularmvc.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import java.util.ArrayList; import java.util.List; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.danyuanblog.framework.demo.popularmvc.controller.dto.UserInfoDto; import com.danyuanblog.framework.popularmvc.annotation.Decrypt; import com.danyuanblog.framework.popularmvc.annotation.Encrypt; @Api(tags = "测试接口数据安全传输功能") @RestController public class TestEncryptAndDecryptController { @GetMapping(value="testDecryptParamAndResponse", name="测试加解密普通参数和响应信息") @ApiOperation(value="测试加解密普通参数和响应信息", notes="测试加解密普通参数和响应信息") @Encrypt public UserInfoDto testDecryptParamAndResponse(@RequestParam("desc") @Decrypt String desc) { UserInfoDto user = new UserInfoDto(); user.setUsername("danyuan"); user.setAge(18); user.setDesc("this is a encrypt test ."); return user; } @GetMapping(value="testEncryptListResponseData", name="测试加密列表响应信息") @ApiOperation(value="测试加密列表响应信息", notes="测试加密列表响应信息") public List<UserInfoDto> testEncryptListResponseData(){ List<UserInfoDto> list = new ArrayList<>(); UserInfoDto user1 = new UserInfoDto(); user1.setUsername("danyuan"); user1.setAge(18); user1.setDesc("this is a encrypt test 1 ."); UserInfoDto user2 = new UserInfoDto(); user2.setUsername("小明"); user2.setAge(22); user2.setDesc("this is a encrypt test 2 ."); list.add(user1); list.add(user2); return list; } @GetMapping(value="testEncryptStringResponse") @ApiOperation(value="测试加密字符串响应信息", notes="测试加密字符串响应信息") @Encrypt public String testEncryptStringResponse(){ return "This just a string response encrypt test !"; } }
-
用到的DTO信息
UserInfoDto.java
/** * Title UserInfoDto.java * Description * @author danyuan * @date Nov 29, 2020 * @version 1.0.0 * site: www.danyuanblog.com */ package com.danyuanblog.framework.demo.popularmvc.controller.dto; import java.io.Serializable; import java.util.List; import com.danyuanblog.framework.popularmvc.annotation.Encrypt; import lombok.Data; @Data public class UserInfoDto implements Serializable{/** *serialVersionUID */ private static final long serialVersionUID = 1L; private String username; private Integer age; @Encrypt private String desc; private List<UserInfoDto> friends; }
4.2 使用自定义的算法进行加解密
-
新增自定义加解密算法处理器
RSADataEncryptHandler.java
/** * Title RSADataEncryptHandler.java * Description * @author danyuan * @date Jan 4, 2021 * @version 1.0.0 * site: www.danyuanblog.com */ package com.danyuanblog.framework.demo.popularmvc.component; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.danyuanblog.framework.demo.popularmvc.utils.RSAEncryptUtil; import com.danyuanblog.framework.popularmvc.SecretManager; import com.danyuanblog.framework.popularmvc.consts.ErrorCodes; import com.danyuanblog.framework.popularmvc.context.RequestContext; import com.danyuanblog.framework.popularmvc.dto.SecretInfo; import com.danyuanblog.framework.popularmvc.encrypt.DataEncryptHandler; import com.danyuanblog.framework.popularmvc.exception.BusinessException; @Component public class RSADataEncryptHandler implements DataEncryptHandler { @Autowired private SecretManager secretManager; /** * @author danyuan */ @Override public String encrypt(String appId, String channelId, String userId, String content) throws Throwable { SecretInfo secret = secretManager.load(RequestContext.getContext().getChannelId(), RequestContext.getContext().getAppId(), RequestContext.getContext().getSecretId(), RequestContext.getContext().getUserId(), RequestContext.getContext().getSessionId()); if(secret != null){ return RSAEncryptUtil.encrypt(content, secret.getKeyPair().getPublicSecret()); } throw new BusinessException(ErrorCodes.NOT_FOUND_CONFIG).setParam("secret_key"); } /** * @author danyuan */ @Override public String decrypt(String appId, String channelId, String userId, String content) throws Throwable { SecretInfo secret = secretManager.load(RequestContext.getContext().getChannelId(), RequestContext.getContext().getAppId(), RequestContext.getContext().getSecretId(), RequestContext.getContext().getUserId(), RequestContext.getContext().getSessionId()); if(secret != null){ return RSAEncryptUtil.decrypt(content, secret.getKeyPair().getPrivateSecret()); } throw new BusinessException(ErrorCodes.NOT_FOUND_CONFIG).setParam("secret_key"); } }
测试接口源码
TestCustomEncryptAndDecryptController.java
/**
* Title TestCustomEncryptAndDecryptController.java
* Description
* @author danyuan
* @date Jan 4, 2021
* @version 1.0.0
* site: www.danyuanblog.com
*/
package com.danyuanblog.framework.demo.popularmvc.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.danyuanblog.framework.demo.popularmvc.component.RSADataEncryptHandler;
import com.danyuanblog.framework.demo.popularmvc.controller.dto.UserInfoDto;
import com.danyuanblog.framework.popularmvc.annotation.Decrypt;
import com.danyuanblog.framework.popularmvc.annotation.Encrypt;
@Api(tags = "测试自定义加解密算法的接口数据安全传输功能")
@RestController
@RequestMapping("custom")
public class TestCustomEncryptAndDecryptController {
@GetMapping(value="testDecryptParamAndResponse",
name="测试加解密普通参数和响应信息")
@ApiOperation(value="测试加解密普通参数和响应信息", notes="测试加解密普通参数和响应信息")
@Encrypt(type = RSADataEncryptHandler.class)
public UserInfoDto testDecryptParamAndResponse(@RequestParam("desc") @Decrypt(type = RSADataEncryptHandler.class) String desc) {
UserInfoDto user = new UserInfoDto();
user.setUsername("danyuan");
user.setAge(18);
user.setDesc("this is a encrypt test .");
return user;
}
@GetMapping(value="testEncryptStringResponse")
@ApiOperation(value="测试加密字符串响应信息", notes="测试加密字符串响应信息")
@Encrypt(type = RSADataEncryptHandler.class)
public String testEncryptStringResponse(){
return "This just a string response encrypt test !";
}
}