package com.alex.examples.annotation;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD) // 说明该注解只能放在方法上面
@Retention(RetentionPolicy.RUNTIME)
public @interface RsaAnnotation {
String className() default ""; //含包路径的实体名称
String fileName() default ""; //要参与验证的字段名
}
package com.alex.examples.aspect;
import cn.hutool.json.JSONUtil;
import com.alex.examples.annotation.RsaAnnotation;
import com.alex.examples.rsa.TestRSA;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.List;
/**
* 定义一个切面类
* 作用:需要清除对应的redis keys
*/
@Order(2) //根据这个切面的设定顺序,这个设定的顺序越小则越先执行
@Slf4j
@Aspect
@Component
public class RsaAspect {
private static final String byte2Base64 = "byte2Base64";
//定义切点
//让所有有@DelRedisKeysAnnotation注解的方法都执行切面方法
@Pointcut("@annotation(rsaAnnotation)")
public void excudeService(RsaAnnotation rsaAnnotation) {
}
//@Around注解可以用来在调用一个具体方法前和调用后来完成一些具体的任务。
@Around("excudeService(rsaAnnotation)")
public Object doAround(ProceedingJoinPoint pjp, RsaAnnotation rsaAnnotation) {
log.info("进入rsaAnnotation切面类,参数值:{},{}", rsaAnnotation.className(), rsaAnnotation.fileName());
if (null != pjp.getArgs() && StringUtils.isNotBlank(rsaAnnotation.fileName())) {
String str = JSONUtil.toJsonStr(pjp.getArgs());
try {
Class clazz = Class.forName(rsaAnnotation.className());
List<Object> list1 = JSONUtil.toList(str, clazz);
Object o = list1.get(0);
//提取父类中定义的byte2Base64参数值--开始
Class dto = null;
Object object = null;
List<Object> superList = JSONUtil.toList(str, (Class<Object>) o.getClass().getSuperclass());
Object superObj = superList.get(0);
for (Field field : superObj.getClass().getDeclaredFields()) {
field.setAccessible(true);
String parameterValue = field.get(superObj).toString();// 参数值
if (field.getName().equals(byte2Base64)) {
object = TestRSA.decipherToJson(parameterValue, clazz); //解析加密串,获取明文
dto = object.getClass();
}
}
//提取父类中定义的byte2Base64参数值--结束
//提取子类【className】值,进行篡改判断--开始
Boolean flag = false;
String[] filesName = rsaAnnotation.fileName().split(",");
for (String fileName : filesName) { //循环接口定义的要验证的字段
for (Field field : o.getClass().getDeclaredFields()) { //循环前端传入的参数组
field.setAccessible(true);
String parameterValue = field.get(o).toString(); //参数值
if (field.getName().equals(fileName)) {
for (Field dtoField : dto.getDeclaredFields()) { //循环解密后的明文参数组
dtoField.setAccessible(true);
if (field.getName().equals(fileName) && field.getName().equals(dtoField.getName())) {
if (null != dtoField.get(object)) {
String objParameterValue = dtoField.get(object).toString();// 参数值
if (!dto.getName().equals(field.getName()) && !objParameterValue.equals(parameterValue)) {
flag = true;
break;
}
} else { //当接口定义的需要验证的参数不存在时,则说明密文加密时,没有对其进行加密,所以判断为数据已被篡改
flag = true;
break;
}
}
}
if (flag) {
return "数据已被篡改,操作失败!";
}
}
}
}
//提取子类【className】值,进行篡改判断--结束
} catch (Exception e) {
e.printStackTrace();
}
}
//result的值就是被拦截方法的返回值(这里一定要return,不然进不来业务方法中)
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return result;
}
}
package com.alex.examples.rsa;
import cn.hutool.json.JSONUtil;
import com.alex.examples.vo.UserInfoDTO;
import org.apache.commons.lang.StringUtils;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* 备注:公钥和私钥必须是同时生成的那一对,不然则会解密失败
*/
public class TestRSA {
private static String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuo2EucVhaEMK/K4swqru7hWTBioO0c2a\n" +
"X44EbaBKloWWH5Yku+9sxz7364Y34cSNcEO/wE7AwImDdtWqXHTSHNqlK7eTjvWO0XEgkLyPiaX1\n" +
"iUA7PaCi0fw4FvZqjZfOt341asjQe43BDYPf6IHzrybOACaRcgM2JkWphhva5kNP6t7oENvRmVpR\n" +
"5xnoyo32aw+hrGUewBytA6vpcro498jqAGxCsWbmhlhM9LWFdI2aiHwV/uCwFVNrcL5bi5cNHxrO\n" +
"HAdlNJbpXz2orH4/GzdTF8XED9ygQRTYNmFVSMyMX7QcWP4AfmRsg4UyoL/IPBWKySYAPdjmlNML\n" +
"nsT50wIDAQAB";
private static String privateKeyStr = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC6jYS5xWFoQwr8rizCqu7uFZMG\n" +
"Kg7RzZpfjgRtoEqWhZYfliS772zHPvfrhjfhxI1wQ7/ATsDAiYN21apcdNIc2qUrt5OO9Y7RcSCQ\n" +
"vI+JpfWJQDs9oKLR/DgW9mqNl863fjVqyNB7jcENg9/ogfOvJs4AJpFyAzYmRamGG9rmQ0/q3ugQ\n" +
"29GZWlHnGejKjfZrD6GsZR7AHK0Dq+lyujj3yOoAbEKxZuaGWEz0tYV0jZqIfBX+4LAVU2twvluL\n" +
"lw0fGs4cB2U0lulfPaisfj8bN1MXxcQP3KBBFNg2YVVIzIxftBxY/gB+ZGyDhTKgv8g8FYrJJgA9\n" +
"2OaU0wuexPnTAgMBAAECggEBAJaB2pUuY5oEjPazZzVfRwkaqtWMVQx7fpKdN4FikwrBmA6/yb8+\n" +
"OVq+uLJyRN+G/j2WyztRzj0INOJJG/sf3OyekielIg1ym9jqFkXvezaoiGJFSgY8dWBIxs/v2qOF\n" +
"2Kc90cbnYRxtj1ViEeG6BmpHOmfwteNwJ93U0iJ6kq62Ox/Qn7Uk+JCGzUnb77IG2wxZJtzidKiQ\n" +
"VP9GoWAaypzpPlOUqqeck9LfwZ3vOXXf0l1S1cQMQMe1fuLIqiBhrOpW8oIZk1LbSFm2mrUtsPmf\n" +
"Q8n66cvq9jBurV6eqU5Oj0N/O2V8ppRw50vABd3tu5cBWj61h1VDQy224LpUKQECgYEA4J5TD2gg\n" +
"vtnpt0uPHiGh+0N1p8Hw/tNmxWvNjL+8cuq0iiDe6FKWQ01z6+bPBNU7EnKkBgbV5Sygskx+B2Yl\n" +
"XsRK6P5pdKnlPJBa3vMX0bnLqiaZYXHwSm/NOMcOgyt+tb6DzowaAmwG8Ro5f/jfifSO/dxQ1y57\n" +
"Y1b0lgF7y0ECgYEA1J2+J2VcLWYiCKMH+xO3oxkEbf2U5UH31NQmSYg9ALRyuTpH4hduxSzFQRm/\n" +
"sb2sc1GOQoqA4ygJMW1r1PCpK5wC1dIOluGPlWiCJUgkIkx+WgugAY5grkTCgY4nwlDvxCnCzLoT\n" +
"VZNIYhr9Rh1izBRanGEpERvw7jqTi+jP5BMCgYEA4BgU9gIE6WOu8NfJaV5UpMGg2EV9kj6HAd+T\n" +
"qyB/eob+p/Azc8UxdOQJCwmy8DdsP/NwATNAouvrRvdZpTgbgHiF4/4uZ944fjvm7RfKY8rhjlgE\n" +
"Hlv2R81TCZpOuD7ko533ElJkgJYWNuTbCPnay1/aaRss7AHLyEgwrJe+UgECgYEAu1WBhhS48wda\n" +
"VWHQVlk5JGuWi5My7g0B4HRMh92SYlyb+LhxPbSH1RNteb7GOEV2/OSPA/IlPgC4mORgbm7dn58G\n" +
"oH9Ee4yihnJeuZqmu9HKQmybvsNETEgpWR13yq6lk6F4EgqhnnT3AfnlzzF1K3uDmuXA2WJVFpJm\n" +
"DpBDHCECgYBOYmXrdMUBjA7YFfMx7N+400ddpLQMnj+lMT64Xe2IJDtRk2mKlBEXa3BjpM3LXjYs\n" +
"ZB+QqGEqe5A4OHUSB7s3+5sgFekQqRy3xLSnFbiiuM6oBjD9P1VnxXKijcQFX+ak2MWKksDLCyE+\n" +
"eTlNcgTG4pmcpstBp8Lmsz8c5LoDwA==";
public static void main(String[] args) {
try {
//===============生成公钥和私钥,公钥传给客户端,私钥服务端保留==================
//生成RSA公钥和私钥,并Base64编码
if (StringUtils.isBlank(publicKeyStr) || StringUtils.isBlank(privateKeyStr)) {
KeyPair keyPair = RSAUtil.getKeyPair();
publicKeyStr = RSAUtil.getPublicKey(keyPair); //RSA公钥Base64编码
privateKeyStr = RSAUtil.getPrivateKey(keyPair); //RSA私钥Base64编码
}
//=================客户端=================
UserInfoDTO dto = new UserInfoDTO();
dto.setName("哈哈");
dto.setAmount(10000L);
//将Base64编码后的公钥转换成PublicKey对象
PublicKey publicKey = RSAUtil.string2PublicKey(publicKeyStr);
//用公钥加密
byte[] publicEncrypt = RSAUtil.publicEncrypt(JSONUtil.toJsonStr(dto).getBytes(), publicKey);
//加密后的内容Base64编码(公钥加密并Base64编码的结果)
String byte2Base64 = RSAUtil.byte2Base64(publicEncrypt);
//############## 网络上传输的内容有Base64编码后的公钥 和 Base64编码后的公钥加密的内容 #################
//===================服务端================
//将Base64编码后的私钥转换成PrivateKey对象
PrivateKey privateKey = RSAUtil.string2PrivateKey(privateKeyStr);
//加密后的内容Base64解码
byte[] base642Byte = RSAUtil.base642Byte(byte2Base64);
//用私钥解密
byte[] privateDecrypt = RSAUtil.privateDecrypt(base642Byte, privateKey);
//解密后的明文
UserInfoDTO bo = JSONUtil.toBean(new String(privateDecrypt), UserInfoDTO.class);
System.out.println("解密后的明文: " + bo);
} catch (Exception e) {
e.printStackTrace();
}
}
// public static UserInfoDTO decipherToJson(String byte2Base64){
// try {
// //将Base64编码后的私钥转换成PrivateKey对象
// PrivateKey privateKey = RSAUtil.string2PrivateKey(privateKeyStr);
// //加密后的内容Base64解码
// byte[] base642Byte = RSAUtil.base642Byte(byte2Base64);
// //用私钥解密
// byte[] privateDecrypt = RSAUtil.privateDecrypt(base642Byte, privateKey);
// //解密后的明文
// UserInfoDTO bo = JSONUtil.toBean(new String(privateDecrypt), UserInfoDTO.class);
// return bo;
// }catch (Exception e){
// e.printStackTrace();
// }
// return null;
// }
public static Object decipherToJson(String byte2Base64, Class clazz) {
try {
//将Base64编码后的私钥转换成PrivateKey对象
PrivateKey privateKey = RSAUtil.string2PrivateKey(privateKeyStr);
//加密后的内容Base64解码
byte[] base642Byte = RSAUtil.base642Byte(byte2Base64);
//用私钥解密
byte[] privateDecrypt = RSAUtil.privateDecrypt(base642Byte, privateKey);
//解密后的明文
Object bo = JSONUtil.toBean(new String(privateDecrypt), clazz);
return bo;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package com.alex.examples.rsa;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class RSAUtil {
//生成秘钥对
public static KeyPair getKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return keyPair;
}
//获取公钥(Base64编码)
public static String getPublicKey(KeyPair keyPair){
PublicKey publicKey = keyPair.getPublic();
byte[] bytes = publicKey.getEncoded();
return byte2Base64(bytes);
}
//获取私钥(Base64编码)
public static String getPrivateKey(KeyPair keyPair){
PrivateKey privateKey = keyPair.getPrivate();
byte[] bytes = privateKey.getEncoded();
return byte2Base64(bytes);
}
//将Base64编码后的公钥转换成PublicKey对象
public static PublicKey string2PublicKey(String pubStr) throws Exception{
byte[] keyBytes = base642Byte(pubStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
//将Base64编码后的私钥转换成PrivateKey对象
public static PrivateKey string2PrivateKey(String priStr) throws Exception{
byte[] keyBytes = base642Byte(priStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
//公钥加密
public static byte[] publicEncrypt(byte[] content, PublicKey publicKey) throws Exception{
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = cipher.doFinal(content);
return bytes;
}
//私钥解密
public static byte[] privateDecrypt(byte[] content, PrivateKey privateKey) throws Exception{
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] bytes = cipher.doFinal(content);
return bytes;
}
//字节数组转Base64编码
public static String byte2Base64(byte[] bytes){
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(bytes);
}
//Base64编码转字节数组
public static byte[] base642Byte(String base64Key) throws IOException{
BASE64Decoder decoder = new BASE64Decoder();
return decoder.decodeBuffer(base64Key);
}
}
package com.alex.examples.vo;
import com.alex.examples.vo.supper.RsaVo;
import lombok.Data;
import java.io.Serializable;
@Data
public class UserInfoDTO extends RsaVo implements Serializable {
private String name;
private Long amount; //金额,单位分
private Integer userId;
}
package com.alex.examples.vo.supper;
import lombok.Data;
@Data
public class RsaVo {
private String byte2Base64; //加密后的数据
}
package com.alex.examples.controller;
import com.alex.examples.annotation.RsaAnnotation;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@Api(tags = "用户管理")
@RestController
@RequestMapping(value = "/rsa")
public class RsaController {
@RsaAnnotation(className = "com.alex.examples.vo.UserInfoDTO", fileName = "name,amount") //默认金额充值时,对客户端数据进行篡改验证
@ApiOperation("测试默认金额充值时,对客户端数据进行篡改验证")
@PostMapping(value = "/test")
public String test(@RequestBody Map<String, Object> map) {
return "充值成功!";
}
}