简介
此项目用于演示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 !";
}
}