简介

此项目用于演示popularmvc如何提供统一全自动化的API隐私数据保护,并且可以做到业务无感和灵活指定数据加解密算法。

请求数据加密使用@Decrypt注解,响应信息加密使用@Encrypt注解,使用自定义算法进行加解密请实现DataEncryptHandler接口,注解中指定即可。

  1. 请求数据解密
  2. 响应信息加密
  3. 指定自定义解密算法
  4. 指定自定义加密算法

项目示例

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数据的加解密的使用主要分两种应用场景:

  1. 对api调用者传递过来的接口请求内的隐私信息进行解密,然后交由业务去处理,popularmvc对业务屏蔽了加解密的过程,业务可以直接使用明文进行处理
  2. 将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 !";		
	}
}