IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Boot整合RSA加密数据传输 -> 正文阅读

[Java知识库]Spring Boot整合RSA加密数据传输

简介

吹水时间开始了,是这样的,公司自研开发一个app,因为要运营和上架需要办理安全评估,办理中说到公司app有可能会泄露用户信息,对用户信息没有加密,遇到抓包的可能会导致用户信息泄露,这时我想到了RSA加密数据来保护用户信息,所以自己使用好像有点效果。

一、简单使用RSA加密解密

RSA是jdk自带的,不需要依赖

public class RSA{
    
    private static Map<String, String> map = new HashMap<>();

    public static void main(String[] args)  throws Exception {
        String content = "我喜欢你";
        genKeyPair();
        //加密
        String publicKey = Encryption(content, map.get("publicKey"));
        System.out.println("加密后:"+publicKey);
        //解密
        System.out.println("解密后:"+Decryption(publicKey,map.get("privateKey")));

    }

    private static String Encryption(String content,String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        byte[] decodeBase64 = Base64.decodeBase64(publicKey);
        PublicKey rsa = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decodeBase64));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE,rsa);
        String outStr  = Base64.encodeBase64String(cipher.doFinal(content.getBytes()));
        return outStr;
    }

    private static String Decryption(String publicKey, String privateKey)throws Exception{
        byte[] publicKeyByte = Base64.decodeBase64(publicKey.getBytes("UTF-8"));
        byte[] privateKeyByte = Base64.decodeBase64(privateKey.getBytes("UTF-8"));
        RSAPrivateKey generatePublic = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyByte));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, generatePublic);
        return new String(cipher.doFinal(publicKeyByte));
    }

    /**
     * 随机生成密钥对
     * @throws NoSuchAlgorithmException
     */
    public static void genKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        //私钥
        PrivateKey privateK = keyPair.getPrivate();
        //公钥
        PublicKey publicK = keyPair.getPublic();

        //PrivateKey2String
        String privateKey = new String(Base64.encodeBase64(privateK.getEncoded()));
        //PublicKey2String
        String publicKey = new String(Base64.encodeBase64(publicK.getEncoded()));
        System.out.println("公钥:"+publicKey);
        System.out.println("私钥:"+privateKey);
        map.put("publicKey",publicKey);
        map.put("privateKey",privateKey);
    }
}

结果:
在这里插入图片描述

二、Spring Boot 整合 RSA

可以自己生成一个公钥和私钥,也可以在网站中在线生成:http://www.metools.info/code/c80.html
在这里插入图片描述

application.yml

#swagger
lanys:
  swagger:
    title: 牟某公司
    description: 牟某公司
    termsOfServiceUrl: https://eurasia.plus/swagger-ui.html
    ContactName: xxx
    ContactUrl: https://eurasia.plus/swagger-ui.html
    ContactEmail: 1090613735@qq.com
    version: 1.0
  public:
    kay: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJVBeO0vImDyMvmRV7bgQJxhzOkLtAAUv6UJSMBP3YtWj6/5DNjaD5Ezt7OXqQ/wz9b+FWTNECsCRC7Nc/kvqiIfHZp4yZYtlzXUbRJk0iu4Y1lv6oKHOXjjnxCjmZwSgXuuDOChNMwqv9GhOQSo5X4HNmrUdTSbAUxfJMfu6WMwIDAQAB
  private:
    kay: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMlUF47S8iYPIy+ZFXtuBAnGHM6Qu0ABS/pQlIwE/di1aPr/kM2NoPkTO3s5epD/DP1v4VZM0QKwJELs1z+S+qIh8dmnjJli2XNdRtEmTSK7hjWW/qgoc5eOOfEKOZnBKBe64M4KE0zCq/0aE5BKjlfgc2atR1NJsBTF8kx+7pYzAgMBAAECgYEAnwoh7f5vsWcF2aTplm6rG/l3aipk8UPU/DFJl26eBOIRwy29+tMl1Xy+4THfw8jnPxFxhZX7Ck+Q5Zlo1FzMRX3Z6ltqCy5H58n4KI7L85MI+dO5l8tjCtxFgNd7NY1gwRjKCjWUsdKqOHzs/+vaeZR1kp2feljTpMBzGGPqS4ECQQD3kU7Er79XuHpk6qK56ihiLT6O8ZnZzhBPRCHq89MIDmt2s13RnSfxEeLZpKlmKLC7VYbZeypoiLxKOdRq1X9BAkEA0C+Y6wUM6h3VC+HzrH5/x++6siHRFwwnDp4U9VmPeesa2a84krNaqptMZtPcm4wr0heDzr5PLksfrSUoYJdscwJAIrBiqB60IfeAdumFuaU82Vzbmi3yT9mW4XR7iC94D3XtyukhKUDrGtVVwwdWkTD8apN2XLzsWU9nisMFp56YwQJAFqFV6hY+dSSRCB2js1h842r3zG1IoUy84iXk+Vam9gXMgWU+rRO6A1mmUHcvP+lYyhmsRkkBqnKYGuYzIWzUaQJBAII0gYs7yRJ4FckmnehD1bMdpgsivlO03qimh7V+fCePG8MMZSq5NFNDP0rogNyo/HLxk5V+tPT05bitJJmHTGM=

注意:公钥和私钥要放为一行,不然会报错。

SecurityParameter

/**
 * @author lanys
 * @Description:
 * @date 22/7/2021 上午11:37
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Mapping
@Documented
public @interface SecurityParameter {

    /**
     * 入参是否解密,默认是解密
     */
    boolean inDecode() default true;

    /**
     * 返回数据是否加密,默认加密
     */
    boolean outEncode() default true;
}

RsaDecodeRequestBodyAdvice

请求我们接口前做数据处理

/**
 * @author lanys
 * @Description:
 * @date 22/7/2021 上午11:26
 */
@ControllerAdvice("com.example.materialboot.modules.usercontroller.conterller")
@Slf4j
public class RsaDecodeRequestBodyAdvice implements RequestBodyAdvice {


    @Value("${lanys.private.kay}")
    public String privateKay;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        boolean encode = false;
        if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {
            SecurityParameter methodAnnotation = methodParameter.getMethodAnnotation(SecurityParameter.class);
            encode = methodAnnotation.inDecode();
        }
        if (encode) {
            log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密");
            return new MyHttpInputMessage(httpInputMessage);
        } else {
            return httpInputMessage;
        }
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }


    class MyHttpInputMessage implements HttpInputMessage {

        private HttpHeaders httpHeaders;

        private InputStream inputStream;

        public MyHttpInputMessage(HttpInputMessage httpInputMessage) throws IOException {
            this.httpHeaders = httpInputMessage.getHeaders();
            String easpString = easpString(IOUtils.toString(httpInputMessage.getBody(), "UTF-8"));
            this.inputStream = IOUtils.toInputStream(RSAUtils.decryptDataOnJava(easpString,privateKay));
        }

        @Override
        public InputStream getBody() throws IOException {
            return this.inputStream;
        }

        @Override
        public HttpHeaders getHeaders() {
            return this.httpHeaders;
        }

        /**
         *
         * @param requestData
         * @return
         */
        public String easpString(String requestData){
             if (requestData != null && !requestData.isEmpty()){
                 String s = "{\"requestData\":";
                 if (!requestData.startsWith(s)){
                     throw new RuntimeException("参数【requestData】缺失异常!");
                 }
                 int closeLen = requestData.length()-1;
                 int openLen = "{\"requestData\":".length();
                 String substring = StringUtils.substring(requestData, openLen, closeLen);
                 return substring;
             }
             return  null;
        }
    }
}

EncodeResponseBodyAdvice

获取数据响应时做处理

/**
 * @author lanys
 * @Description: 返回数据加密
 * @date 22/7/2021 上午11:26
 */
@Component
@ControllerAdvice("com.example.materialboot.modules.usercontroller.conterller")
@Slf4j
public class EncodeResponseBodyAdvice implements ResponseBodyAdvice {


    @Autowired
    private RsaUtil rsaUtil;


    @Value("${lanys.public.kay}")
    public String publicKay;

    @Value("${lanys.private.kay}")
    public String privateKay;


    /**
     * 是否支持这个类
     * @param methodParameter
     * @param aClass
     * @return
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        //默认是不使用,改为true
        return true;
    }

    /**
     * 返回前处理
     * @param body
     * @param methodParameter
     * @param mediaType
     * @param aClass
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @return
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        log.info("------响应数据加密-------");
        boolean encode = false;
        if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {
            //获取注解配置的包含和去除字段
            SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);
            //出参是否需要加密
            encode = serializedField.outEncode();
        }
        if (encode) {
            log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密");
            ObjectMapper objectMapper = new ObjectMapper();
            try {
            //获取内容
                String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);
                // 生成一个解数据的秘钥
                String aseKey = getRandomString(16);
                // 将公钥和(生成一个解数据的秘钥)进行加密
                String encrypted = RSAUtils.encryptedDataOnJava(aseKey, publicKay);
                // 将响应数据和(生成一个解数据的秘钥)加密
                String requestData = AesEncryptUtils.encrypt(result, aseKey);
                System.out.println("解密:"+AesEncryptUtils.decrypt(requestData,aseKey));
                Map<String, String> map = new HashMap<>();
                map.put("encrypted", encrypted);
                map.put("requestData", requestData);
                return map;
            } catch (Exception e) {
                e.printStackTrace();
                log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());
            }
        }
        return body;
    }


        /**
         * 创建指定位数的随机字符串
         * @param length 表示生成字符串的长度
         * @return 字符串
         */
    public static String getRandomString(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}

RSAUtils

public class RSAUtils {

	/**
	 * 加密算法RSA
	 */
	public static final String KEY_ALGORITHM = "RSA";
	
 
	/** */
	/**
	 * RSA最大加密明文大小
	 */
	private static final int MAX_ENCRYPT_BLOCK = 117;
 
	/** */
	/**
	 * RSA最大解密密文大小
	 */
	private static final int MAX_DECRYPT_BLOCK = 128;


	/**
	 * 私钥解密
	 * @param encryptedData
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
		byte[] keyBytes = Base64.decodeBase64(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, privateK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}


	/**
	 * java端私钥解密
	 */
	public static String decryptDataOnJava(String data, String PRIVATEKEY) {
		String temp = "";
		try {
			byte[] rs = Base64.decodeBase64(data);
			temp = new String(RSAUtils.decryptByPrivateKey(rs, PRIVATEKEY),"UTF-8");

		} catch (Exception e) {
			e.printStackTrace();
		}
		return temp;
	}


	/**
	 * 公钥加密
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
		byte[] keyBytes = Base64.decodeBase64(publicKey);
		Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(keyBytes));
		// 对数据加密
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, publicK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段加密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_ENCRYPT_BLOCK;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return encryptedData;
	}

	/**
	 * java端公钥加密
	 */
	public static String encryptedDataOnJava(String data, String PUBLICKEY) {
		try {
			data = Base64.encodeBase64String(encryptByPublicKey(data.getBytes(), PUBLICKEY));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return data;
	}
	
}

AesEncryptUtils

前端工具:前端传过来的数据也是加密的,我们也会解密

public class AesEncryptUtils {
 
    //参数分别代表 算法名称/加密模式/数据填充方式
    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";

    /**
     * 加密
     * @param content 加密的字符串
     * @param encryptKey key值
     * @return
     * @throws Exception
     */
    public static String encrypt(String content, String encryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
        byte[] b = cipher.doFinal(content.getBytes("utf-8"));
        // 采用base64算法进行转码,避免出现中文乱码
        return Base64.encodeBase64String(b);
 
    }

    @Value("${lanys.public.kay}")
    public String publicKay;

    @Value("${lanys.private.kay}")
    public String privateKay;


 
    /**
     * 解密
     * @param encryptStr 解密的字符串
     * @param decryptKey 解密的key值
     * @return
     * @throws Exception
     */
    public static String decrypt(String encryptStr, String decryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));
        // 采用base64算法进行转码,避免出现中文乱码
        byte[] encryptBytes = Base64.decodeBase64(encryptStr);
        System.out.println(encryptBytes.length);
        byte[] decryptBytes = cipher.doFinal(encryptBytes);

        return new String(decryptBytes);
    }
}

2.读入数据

代码如下(示例):

data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())

UserController

/**
 * @ClassName UserController
 * @Author lanys
 * @Data 1/4/2021 下午4:56
 */
@Api(tags = "用户管理")
@RestController
@RequestMapping("v1/user")
public class UserController {

    @Autowired
    private SysUserService userService;

    @SecurityParameter(inDecode = false,outEncode = true)
    @GetMapping("list")
    @RequiresPermissions("sys:user:save,sys:role:select")
    @ApiImplicitParams({ @ApiImplicitParam(name = HttpHeaders.AUTHORIZATION, value = "token", required = true, paramType = "header")})
    public Result UserList(){
        return Result.success().put(userService.userList());
    }

}

这时响应加密:
在这里插入图片描述
encrypted:是加密后的公钥,只需要前端用私钥进行解密获取生成的解密数据秘钥,在根据获取的秘钥对数据进行解密就能获取所响应的数据类型
requestData:指的是加密后的数据

解密测试

    /**
     * 解密
     */
    @ApiOperation(value = "解密测试")
    @PostMapping("/decryption")
    @ApiImplicitParam(name = "data",value = "数据",paramType = "query")
    @ResponseBody
    public Result decryption(String key,String data) throws Exception {
        String decryptDataOnJava = RSAUtils.decryptDataOnJava(key, privateKay);
        String decrypt = AesEncryptUtils.decrypt(data, decryptDataOnJava);
        JSONObject jsonObject =  JSON.parseObject(decrypt);
        String jsonData = jsonObject.getString("data");
        List<SysUser> dataArr = JSONArray.parseArray(jsonData,SysUser.class);
        System.out.println(decrypt);
        return Result.success().put(dataArr);
    }

在这里插入图片描述

总结

最后解密后是String类型的数据,输出是JSON的样子,需要JSON对数据进行转换就可以转换自己原有的数据类型,我是参考网上的案例进行使用,其中也有自己改的代码,吹水结束。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-23 10:35:34  更:2021-07-23 10:38:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 4:37:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码