目前使用Spring Boot来开发rest api服务的业务越来越多,尤其是spring cloud 微服务技术栈的普及,
似乎大家都开始进入了微服务开发的新阶段。
伴随着微服务开发过程的深入,大家都会遇到各种各样的问题和疑问。
- 为什么业务层和数据层要分开?
- 使用什么技术栈来解决无状态的用户鉴权?
- 数据如何保证传输的安全性?
等等……
本文主要讲解一下站长使用较多的数据传输加密方案和后端统一解密方案,
哈哈,用户鉴权的部分后续有新的文章专门介绍。
大家许多人可能会觉得数据传输安全性不是有https来保障吗,自己不需要来实现各种加密算法。
对于应用工程师来说,这个理解可以原谅。
但是对于工作在一线的主程或者架构师来说,这个是不可原谅的。
因为现在业务必须保障其足够的安全性,否则业务将无立足之地,比如最近又发生的某某酒店
上亿条用户数据泄密。
数据在传输至服务器前进行加密,可以保证即使在被网络窃听的情况下,数据不会被篡改和盗取。
重点来了,请往下看。
一、数据传输加密方案
-
前端应用在完成用户鉴权后,使用rsa+aes进行加密传参,同时将用户令牌传回服务器端。
在用户密钥安全的前提下,可以有效保护资源访问的安全性以及传输过程中数据不会被窃取。
具体思路为:
1) 随机生成aes加密算法所需的密钥和向量参数
2)使用rsa加密算法对aes密钥进行加密
3)使用aes加密算法对原始数据进行加密
4)将加密好的数据以及用户令牌一起传回服务器
5)获取服务器对于的响应结果
友情提示:js版本的加密算法可以在github上找到。
二、数据通用解密方案
在spring 4.x开始,spring官方新增了较多特性,本方案就使用到新增的特性。
想必使用过spring的都用过@RequestBody这个注解。对于加密的数据,我们该怎么使用
这个注解来实现统一解密呢?
也许你有其他更好的方案,但是也可以参考一下站长下面的方案。
spring 4.x中增加了方法,可以对RequestBody中的数据实现解析前数据检验与拦截以及解析后动态
设置参数的支持。解析后动态增加参数的,可以自行研究下。下面的干货即为通用解密方案的核心代码
,哈哈,直接看代码吧。
@ControllerAdvice
public class EncodeBodyAdvice extends RequestBodyAdviceAdapter {
@Value("${req.privateKey}")
private String privateKey;
@Value("${req.aesIv}")
private String aesIv;
@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
//判断是否对当前参数进行处理
// return methodParameter.hasMethodAnnotation(RequestBody.class);
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
try{
String source=IOUtils.toString(inputMessage.getBody(), "utf-8");
JSONObject jsonObject=JSONObject.parseObject(source);
if(jsonObject!=null&&jsonObject.containsKey("key")&&jsonObject.containsKey("content")){
//rsa解密
String sourceKey= EncryptUtils.rsaDecryptByPrivateKey(jsonObject.getString("key"),privateKey);
//aes解密
String sourceContent=EncryptUtils.aesCbcPkcs5PaddingDecrypt(jsonObject.getString("content"),sourceKey,aesIv);
InputStream decodeInputStream=IOUtils.toInputStream(sourceContent, StandardCharsets.UTF_8.name());
HttpInputMessage httpInputMessage=new HttpInputMessage() {
@Override
public InputStream getBody() throws IOException {
return decodeInputStream;
}
@Override
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
};
return super.beforeBodyRead(httpInputMessage, parameter, targetType, converterType);
}else{
InputStream decodeInputStream=IOUtils.toInputStream(source, StandardCharsets.UTF_8.name());
HttpInputMessage httpInputMessage=new HttpInputMessage() {
@Override
public InputStream getBody() throws IOException {
return decodeInputStream;
}
@Override
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
};
return super.beforeBodyRead(httpInputMessage, parameter, targetType, converterType);
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
补充一种基于Filter和Form表单的POST请求通用解密方案
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Component
@WebFilter(urlPatterns = { "/" }, filterName = "authorFilter")
public class FormRequestConvertConfig implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper((HttpServletRequest) request) {
@Override
public String getParameter(String name) {
return getParameterMap().get(name)[0];
}
@Override
public String[] getParameterValues(String name) {
return getParameterMap().get(name);
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> newMap = new HashMap<>();
//getParameterMap()返回的时一个不可修改的map ,不能直接向里面put值,
//所以在重写这个方法时要自己new 一个HashMap ,然后在新建的map中放值,
// 最后返回时必须调用Collections.unmodifiableMap(Map<? extends K, ? extends V> m)
// 把map改成不可变的
newMap.putAll(super.getParameterMap());
//TODO 实现自己参数的统一解密方案
newMap.put("body",new String[]{"{\"module\":\"456\"}"}) ;
return Collections.unmodifiableMap(newMap);
}
};
chain.doFilter(requestWrapper,response);
}
@Override
public void destroy() {
}
}
标题:聊聊加解密那些事
作者:TravelEngineers
地址:https://www.mycitymemory.com/articles/2019/07/21/1563698318822.html
版权声明:转载请注明博文地址,尊重作者劳动成果。
作者简介:坐标魔都,一枚爱旅行爱摄影的攻城狮。愿攻城拔寨的路上,你不用996,也不再孤单,加油。