SpringBoot接口加解密全过程详解
一、接口为什么要加密
接口加密传输,主要作用: 敏感数据防止泄漏、 保护隐私、 防伪装攻击、 防篡改攻击、 防重放攻击 等等… 4个字概括: 保护数据!
当然不是说接口加密后,就能完完全全的保护我们的数据,但至少能防一部分人拿到我们的数据。
而且接口加密感觉逼格是不是高过一点!!! 二、加密思路1、加密简介
加密算法有很多,在能加密又能解密的算法可分为: 非对称加密算法,常见: RSA 、 DSA 、 ECC
特点:算法复杂,加解密速度慢,但安全性高,一般与对称加密结合使用(对称加密对内容加密,非对称对对称所使用的密钥加密) 对称加密算法,常见: DES 、 3DES 、 AES 、 Blowfish 、 IDEA 、 RC5 、 RC6
特点:加密解密效率高,速度快,适合进行大数据量的加解密 2、加密流程
思路:
假设现在客户端是A,服务端是B,现在A要去B请求接口 1、A要向B发送信息,A和B都要产生一对用于加密的非对称加密公私钥(AB各自生成自己的公私钥) 2、A的私钥保密,A的公钥告诉B;B的私钥保密,B的公钥告诉A。(AB互换公钥) 3、A要给B发送信息时,A用B的公钥加密信息,因为A知道B的公钥。(公钥加密只有私钥能解) 4、A将这个消息发给B(已经用B的公钥加密消息)。 5、B收到这个消息后,B用自己的私钥解密A的消息。其他人收到这个报文都无法解密,因为只有B才有B的私钥。
虽然这样就实现了接口的加密方式,但是呢,非对称加密的加解密速度相比对称加密速度很慢,当传输的数据很大时就更加明显了。
所以我们对称与非对称一起用,理解上面的流程之后,我们在其基础稍微改下: 在A给B发信息的时候,随机生成一个对称加密的密钥,然后用刚生成的密钥加密信息,然后用B的公钥加密刚生成的对称密钥。 A把加密的两个信息发送给B。B收到数据之后,先用自己的私钥解开得到对称密钥,然后再用解开的对称密钥解开对称加密的信息,最终得到A传来的信息。 三、代码实现在当下Java还是SpringBoot为主流框架工作面试必备,今天还是以它来举例。 加解密代码怎么写,这个时候网上已经有很多现成的库了,不用我们操心,我们想的是如何在接口加解密的时候不影响我们自己的业务,也就是不用更改我们已经写好的代码。 很多人的第一反应应该就是AOP吧,对的没错可以使用AOP进行环绕增强。也可以使用 @ControllerAdvice 对Controller进行增强(本文以它来做为例子)。 Spring 提供两个接口 RequestBodyAdvice 、 ResponseBodyAdvice 。实现它们,即可对 Controller 进行增强,第一个是在controller之前增强,第二个就是对controller 的返回值进行增强。 在spring启动的时候会对 RequestMappingHandlerAdapter 的 initControllerAdviceCache() 方法进行初始化。会去把有 @ControllerAdvice 的类进行注入。 1、自定义类
下面就来实现上面的两个接口实现类代码 EncryptRequestAdvice.java这个类的功能就是在请求到controller之前就把前端传上来的数据解密好 我们还要校验是否有必要解密 @ControllerAdvice(basePackages = {"top.lrshuai.encrypt.controller"}) public class EncryptRequestAdvice implements RequestBodyAdvice { @Autowired private KeyConfig keyConfig; /** * 是否需要解码 */ private boolean isDecode; @Override public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { // 方法或类上有注解 if (Utils.hasMethodAnnotation(methodParameter,new Class[]{Encrypt.class,Decode.class})) { isDecode=true; // 这里返回true 才支持 return true; } return false; } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException { if(isDecode){ return new DecodeInputMessage(httpInputMessage, keyConfig); } return httpInputMessage; } @Override public Object afterBodyRead(Object obj, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { // 这里就是已经读取到body了,obj就是 return obj; } @Override public Object handleEmptyBody(Object obj, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { // body 为空的时候调用 return obj; } } 在上面实现类中需要重写: supports() 、 beforeBodyRead() 、 afterBodyRead() 、 handleEmptyBody() 方法 只有在 supports() 返回 true 后面的方法才会支持执行。在 RequestResponseBodyAdviceChain 有判断 我们可以在 beforeBodyRead() 这个方法进行解密处理。 在上面的代码中,我加了自定义注解,因为可能需求是这样的,有些接口加密有些接口不加密,用自定义注解比较方便。 然后 DecodeInputMessage 这个类是自定义实现了 HttpInputMessage 接口,解码逻辑都在里面。如下: DecodeInputMessage.java
这个类就是具体的解码逻辑了 public class DecodeInputMessage implements HttpInputMessage { private HttpHeaders headers; private InputStream body; public DecodeInputMessage(HttpInputMessage httpInputMessage, KeyConfig keyConfig) { // 这里是body 读取之前的处理 this.headers = httpInputMessage.getHeaders(); String encodeAesKey = ""; List keys = this.headers.get(Result.KEY); if (keys != null && keys.size() > 0) { encodeAesKey = keys.get(0); } try { // 1、解码得到aes 密钥 String decodeAesKey = RsaUtils.decodeBase64ByPrivate(keyConfig.getRsaPrivateKey(), encodeAesKey); // 2、从inputStreamReader 得到aes 加密的内容 String encodeAesContent = new BufferedReader(new InputStreamReader(httpInputMessage.getBody())).lines().collect(Collectors.joining(System.lineSeparator())); // 3、AES通过密钥CBC解码 String aesDecode = AesUtils.decodeBase64(encodeAesContent, decodeAesKey, keyConfig.getAesIv().getBytes(), AesUtils.CIPHER_MODE_CBC_PKCS5PADDING); if (!StringUtils.isEmpty(aesDecode)) { // 4、重新写入到controller this.body = new ByteArrayInputStream(aesDecode.getBytes()); } } catch (Exception e) { e.printStackTrace(); } } @Override public InputStream getBody() throws IOException { return body; } @Override public HttpHeaders getHeaders() { return headers; } } 上面的代码注释我觉得都写的清楚了,不多介绍。 EncryptResponseAdvice.java这个类的主要功能就是对返回值进行加密操作 直接在 beforeBodyWrite() 里面执行具体的加密操作即可 supports() 方法也是需要返回 true ,在 RequestResponseBodyAdviceChain.processBody() 中有个判断只有 supports() 返回 true 才会执行 beforeBodyWrite() @Slf4j @ControllerAdvice(basePackages = {"top.lrshuai.encrypt.controller"}) public class EncryptResponseAdvice implements ResponseBodyAdvice
暗黑破坏神不朽圣教军全攻略实用技能与装备流派搭配指南暗黑破坏神不朽的圣教军是一个能抗能输出的职业,拥有强力的AOE与增伤技能。下面请看由Kekays带来的暗黑破坏神不朽圣教军Build全攻略,包含圣教军实用技能与装备流派搭配,希望对
异度神剑3打破英国地区系列最佳首销记录看起来任天堂与Monolith工作室合作的异度神剑3有望取得异度系列游戏最佳销量。根据英国数据统计机构给出的信息显示,这款游戏在英国地区的首发销量达到了异度系列游戏的最高峰。在GF
世界赛要凉了?V5到底为何临阵换将,xlb真的比karsa强吗随着V5再次将卡萨放入替补席,有关于首发打野xlb的讨论声也逐渐变的越来越大,那么本期玩咖宝典就和大家一起讨论一下V5的季后赛形式,以及xlb与karsa的轮换到底是什么情况。得力
众望所归!守望归来全新战令让玩家大呼我可以在守望先锋归来宣布全网免费的时候,就已经有玩家在猜测,接下来是否就应该再出个类似通行证的系统,这样才能够保证游戏的持续收入。果不其然,归来很快就宣布了战令系统的推出计划,并且简单地
原神这些4星角色在须弥会有新体验!坐上须弥快车,练起来派蒙新版本整新活?冻梨不能浪费设计师的成果,将其利益最大化菲谢尔,这是目前来看4星之中在须弥混得最好的角色,没有之一。主要是坐上了草雷激化这架大车,并且在多个计算贴中发现,低配的刻
香克斯心态爆炸,把自己ID修改为beichu?暗示悲伤真的不行这个赛季里面,除了一些排名靠前的队伍,比较受到大家关注之外,还有一些排名比较靠后的队伍也是如此,因为在这个赛季当中,有一支队伍非常的有趣,那就是WE战队。曾经这支队伍是LPL御三家
毁誉参半的暗黑破坏神不朽揭开了动视暴雪的窘境暗黑破坏神不朽国服已于7月25日正式上线!一方面,这款暴雪酝酿了4年之久的暗黑系列第一款手游达到了3500万玩家的预约。在海外上线不到两个月,收入突破1亿美元,成为了吸金速度最快的
正能量激励早安图片语录全力以赴,你会很酷无论你年龄多大,无论你学历多高,无论你有没有颜值,都可以去努力实现梦想。不要担忧未来,不要担心跟别人相差太远,只要你开始行动就一切都来得及!周三,早安!行动是成功的阶梯,行动越多,
王印祥儿歌歌词二首童年文王印祥童年,童年,你是新绿的春天,花儿的笑脸。童年,童年,你像鲜艳的桃园,美丽着明天。童年,童年,你是燃烧的杜鹃,花儿的梦幻。童年,童年,你像尖尖的小荷,灿烂着明天。啦啦啦,
爬满藤蔓的小院今年酷夏,火日炙人,热稍狂,透青天者,久悬火中。虽过三伏节后,目犹光耀,意若曝干,使人闷绝。热中,忽跃出一日凉爽。于是热心欲下楼散步。乘梯步于林阴间,行至隔壁宿墙边,见藤蔓密簇,野
句子摘抄生活在阴沟里,依然有仰望星空的权利1人要甘于平凡,但不能在平凡中沉沦。如果别人朝你扔石头,就不要扔回去了,留作你建高楼的基石。运动中的赛跑,是在有限的路程内看你跑了多少时间人生中的赛跑是在有限的时间内看你跑了多少路