SpringBoot接口加密与解密
一、对称/非对称加密
1、简介
对称加密只有一个秘钥,加密和解密都是用同一个秘钥,所以叫做对称加密。
非对称加密有两个秘钥,一个是公钥,一个是私钥。非对称的特点在于,公钥加密的私钥可以解密,但私钥加密的,公钥解不出来,只能验证是否由私钥进行加密
目前常见的加密方式是有两种,一种是对称加密(AES为代表),一种是非对称加密(RSA为代表)
2、RSA和AES介绍
2.1 RSA
特点:只需交换公钥;公/秘钥机制,公钥加密,私钥解密(或者私钥加密,公钥解密);公钥负责加密,私钥负责解密;私钥负责签名,公钥负责验证
缺点:加解密速度慢,特别是解密
2.2 AES
特点:加解密用同一秘钥
优点:速度快,效率高;
缺点:秘钥交换问题
3、RSA/AES组合
对称加密(AES)的优势在于加密较快,但劣势在于秘钥一旦给出去就不安全了。非对称加密(RSA)的优势在于安全,就算提供公钥出去,别人也解密不了数据,但劣势是加密速度较慢
实际使用的过程中常常将两者组合使用(AES+RSA),这样可以安全的传输AES秘钥,避免了RSA加密的慢速度
-
生成一个随机AES秘钥字符串
-
使用RSA公钥加密AES秘钥,然后再用AES秘钥加密真正的内容
-
把skey=加密的AES秘钥,body=AES秘钥加密的内容传过去
-
对面使用RSA私钥解密AES秘钥,然后用AES秘钥解密出内容
4、Base64编码的作用
加密后的数据可能不具备可读性,因此我们一般需要对加密后的数据再使用 Base64 算法进行编码,获取可读字符串。换言之,AES 或者RSA加密方法的返回值是一个 Base64 编码之后的字符串,AES或者RSA 解密方法的参数也是一个 Base64 编码之后的字符串,先对该字符串进行解码,然后再解密。
二、Java实现加解密/加验签
1、全局Config
1 | public class Config { |
2、RSA非对称加密
1 | import javax.crypto.Cipher; |
输出结果
1 | 加密后: |
3、AES对称加密
1 | import org.springframework.util.Base64Utils; |
输出结果
1 | 加密后: |
三、加解密 starter实战
1、介绍
加密解密本身并不是难事,问题是在何时去处理?定义一个过滤器,将请求和响应分别拦截下来进行处理也是一个办法,这种方式虽然粗暴,但是灵活,因为可以拿到一手的请求参数和响应数据。不过 SpringMVC 中给我们提供了 ResponseBodyAdvice
和 RequestBodyAdvice
,利用这两个工具可以对请求和响应进行预处理,非常方便。
2、前期准备
2.1 引入依赖
因为我们这个工具是为 Web 项目开发的,以后必然使用在 Web 环境中,所以这里添加依赖时 scope 设置为 provided
1 | <dependency> |
scope几个属性介绍
-
compile:默认值 他表示被依赖项目需要参与当前项目的编译,还有后续的测试,运行周期也参与其中,是一个比较强的依赖。打包的时候通常需要包含进去
-
test:依赖项目仅仅参与测试相关的工作,包括测试代码的编译和执行,不会被打包,例如:junit
-
runtime:表示被依赖项目无需参与项目的编译,不过后期的测试和运行周期需要其参与。与compile相比,跳过了编译而已。例如JDBC驱动,适用运行和测试阶段
-
provided:打包的时候可以不用包进去,别的设施会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是打包阶段做了exclude操作
-
system:从参与度来说,和provided相同,不过被依赖项不会从maven仓库下载,而是从本地文件系统拿。需要添加systemPath的属性来定义路径
2.2 封装公共相应类
1 | public class RespBean { |
2.3 定义加解密工具类
加密这块有多种方案可以选择,对称加密、非对称加密,其中对称加密又可以使用 AES、DES、3DES 等不同算法,这里我们使用 Java 自带的 Cipher 来实现对称加密,使用 AES 算法
1 | public class AESUtils { |
2.4 定义两个注解
接下来我们定义两个注解 @Decrypt
和 @Encrypt
。在以后使用的过程中,哪个接口方法添加了 @Encrypt 注解就对哪个接口的数据加密返回,哪个接口/参数添加了 @Decrypt 注解就对哪个接口/参数进行解密。另外就是 @Decrypt
可以用在参数上
1 | (RetentionPolicy.RUNTIME) |
2.5 设置自定义key
定义一个 EncryptProperties 类来读取用户配置的 key,这样就可以自定义key。这里设置了默认值,以后如果用户想自己配置 key,只需要在 application.properties 中配置 spring.encrypt.key=xxx
即可。
1 | "spring.encrypt") (prefix = |
3、接口加密与解密
3.1 介绍
ResponseBodyAdvice
在你使用了 @ResponseBody
注解的时候才会生效,RequestBodyAdvice
在你使用了 @RequestBody
注解的时候才会生效,换言之,前后端都是 JSON 交互的时候,这两个才有用
3.2 接口加密
我们自定义 EncryptResponse
类实现 ResponseBodyAdvice
接口,泛型表示接口的返回类型,这里一共要实现两个方法
-
supports:这个方法用来判断什么样的接口需要加密,参数 returnType 表示返回类型,我们这里的判断逻辑就是方法是否含有
@Encrypt
注解,如果有,表示该接口需要加密处理,如果没有,表示该接口不需要加密处理。 -
beforeBodyWrite:这个方法会在数据响应之前执行,也就是我们先对响应数据进行二次处理,处理完成后,才会转成 json 返回。我们这里的处理方式很简单,RespBean 中的 status 是状态码就不用加密了,另外两个字段重新加密后重新设置值即可。
另外需要注意,自定义的 ResponseBodyAdvice 需要用 @ControllerAdvice
注解来标记。
1 | .class) (EncryptProperties |
3.3 接口解密
首先大家注意,DecryptRequest 类我们没有直接实现 RequestBodyAdvice
接口,而是继承自 RequestBodyAdviceAdapter 类,该类是 RequestBodyAdvice 接口的子类,并且实现了接口中的一些方法,这样当我们继承自 RequestBodyAdviceAdapter 时,就只需要根据自己实际需求实现某几个方法即可。
-
supports:该方法用来判断哪些接口需要处理接口解密,我们这里的判断逻辑是方法上或者参数上含有
@Decrypt
注解的接口,处理解密问题。 -
beforeBodyRead:这个方法会在参数转换成具体的对象之前执行,我们先从流中加载到数据,然后对数据进行解密,解密完成后再重新构造 HttpInputMessage 对象返回。
1 | .class) (EncryptProperties |
4、打包发布starter
4.1 定义自动化配置类
1 | // 换成自己的包路径 |
最后,resources
目录下定义 META
-INF,然后再定义 spring.factories
文件,这样当项目启动时,就会自动加载该配置类
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.encryption.EncryptAutoConfiguration |
安装到本地仓库比较简单,直接 mvn install
,或者在 IDEA 中,点击右边的 Maven,然后双击 install
4.2 发布线上使用
发不到线上我们可以使用 JitPack来做。首先我们在 GitHub 上创建一个仓库,将我们的代码上传上去,上传成功后,点击右边的 Create a new release
按钮,发布一个正式版
发布成功后,打开 jitpack,输入仓库的完整路径,点击 lookup 按钮,查找到之后,再点击 Get it
按钮完成构建,构建成功后,JitPack 上会给出项目引用方式,新建项目时引入即可
5、新项目使用
创建实体类
1 | public class User { |
创建测试类,第一个接口使用了 @Encrypt
注解,所以会对该接口的数据进行加密(如果不使用该注解就不加密),第二个接口使用了 @Decrypt
所以会对上传的参数进行解密,注意 @Decrypt
注解既可以放在方法上也可以放在参数上。
1 |
|
参考文章