微信小程序请求的所有接口参数必须加密,那么小程序接口加密如何实现。
微信小程序接口实现加密教程:
场景
小程序请求的所有接口参数必须加密,后台返回数据也需要加密,并且增加Token验证
一、小程序端功能编写
1.下载一份Js版的aesUtil.js源码。【注:文章末尾会贴出所有的相关类文件】
2.下载一份Js版的md5.js源码。
3.在pulic.js中进行加解密操作代码如下,其中秘钥和秘钥偏移量要与后台的一致。
-
var CryptoJS = require('aesUtil.js'); //引用AES源码js
-
var md5 = require('md5.js')
-
-
var key = CryptoJS.enc.Utf8.parse("76CAA1C88F7F8D1D"); //十六位十六进制数作为秘钥
-
var iv = CryptoJS.enc.Utf8.parse('91129048100F0494'); //十六位十六进制数作为秘钥偏移量
-
//解密方法
-
function Decrypt(word) {
-
var encryptedHexStr = CryptoJS.enc.Hex.parse(word);
-
var srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
-
var decrypt = CryptoJS.AES.decrypt(srcs, key, {
-
iv: iv,
-
mode: CryptoJS.mode.CBC,
-
padding: CryptoJS.pad.Pkcs7
-
});
-
var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
-
return decryptedStr.toString();
-
}
-
//加密方法
-
function Encrypt(word) {
-
var srcs = CryptoJS.enc.Utf8.parse(word);
-
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
-
iv: iv,
-
mode: CryptoJS.mode.CBC,
-
padding: CryptoJS.pad.Pkcs7
-
});
-
return encrypted.ciphertext.toString().toUpperCase();
-
}
-
-
//暴露接口
-
module.exports.Decrypt = Decrypt;
-
module.exports.Encrypt = Encrypt;
4.在网络请求帮助类中进行参数的加密和返回数据的解密操作。
-
var aes = require('../utils/public.js')
-
var md5 = require("../utils/md5.js")
-
-
...
-
-
/**
-
* 网络请求
-
*/
-
function request(method, loading, url, params, success, fail) {
-
var url = BASE_URL + url;
-
//请求参数转为JSON字符串
-
var jsonStr = JSON.stringify(params);
-
console.log(url + ' params=> ' + jsonStr)
-
//根据特定规则生成Token
-
var token = productionToken(params);
-
//加密请求参数
-
var aesData = aes.Encrypt(jsonStr)
-
console.log('请求=>明文参数:' + jsonStr)
-
console.log('请求=>加密参数:' + aesData)
-
...
-
wx.request({
-
url: url,
-
method: method,
-
header: {
-
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
-
'Token': token
-
},
-
data: {
-
aesData: aesData
-
},
-
// data: params,
-
success: function(res) {
-
//判断请求结果是否成功
-
if (res.statusCode == 200 && res.data != '' && res.data != null) {
-
//解密返回数据
-
console.log('返回=>加密数据:' + res.data);
-
var result = aes.Decrypt(res.data);
-
console.log('返回=>明文数据:'+result);
-
success(JSON.parse(result))
-
} else {
-
fail()
-
}
-
},
-
fail: function(res) {
-
fail()
-
},
-
})
-
}
其中生成Token的规则,【生成Token的规则可根据具体的业务逻辑自己定义,我这里使用的规则是根据请求参数的字母排序取其value并加上当前时间戳再进行MD5加密】
-
/**
-
* 生成Token
-
*/
-
function productionToken(params) {
-
var obj = util.objKeySort(params);
-
var value = '';
-
for (var item in obj) {
-
value += obj[item];
-
}
-
//加上当前时间戳
-
value += util.getTokenDate(new Date())
-
//去除所有空格
-
value = value.replace(/\s+/g, "")
-
//进行UTF-8编码
-
value = encodeURI(value);
-
//进行MD5码加密
-
value = md5.hex_md5(value)
-
return value;
-
}
-
//util的排序函数
-
function objKeySort(obj) {
-
//先用Object内置类的keys方法获取要排序对象的属性名,再利用Array原型上的sort方法对获取的属性名进行排序,newkey是一个数组
-
var newkey = Object.keys(obj).sort();
-
//创建一个新的对象,用于存放排好序的键值对
-
var newObj = {};
-
//遍历newkey数组
-
for (var i = 0; i < newkey.length; i++) {
-
//向新创建的对象中按照排好的顺序依次增加键值对
-
newObj[newkey[i]] = obj[newkey[i]];
-
}
-
//返回排好序的新对象
-
return newObj;
-
}
二、服务端功能编写
由于初学SpringMVC,使用的方式不一定是最优最好的,如有不妥善之处,请各位看官多多指教 思路:
通过过滤器拦截请求参数,通过自定义参数包装器对参数进行解密。 在拦截器获取请求的Token并生成服务器端Token进行验证。 对返回参数通过JSON转换器进行加密处理。
1.重写HttpServletRequestWrapper,在自定义的HttpServletRequestWrapper 中对参数进行处理
-
/**
-
* Describe:请求参数包装器 主要作用的过滤参数并解密
-
* Created by 吴蜀黍 on 2018-08-07 09:37
-
**/
-
@Slf4j
-
public class ParameterRequestWrapper extends HttpServletRequestWrapper {
-
-
private Map<String, String[]> params = new HashMap<>();
-
-
@SuppressWarnings("unchecked")
-
public ParameterRequestWrapper(HttpServletRequest request) {
-
// 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
-
super(request);
-
//将参数表,赋予给当前的Map以便于持有request中的参数
-
this.params.putAll(request.getParameterMap());
-
this.modifyParameterValues();
-
}
-
-
//重载一个构造方法
-
public ParameterRequestWrapper(HttpServletRequest request, Map<String, Object> extendParams) {
-
this(request);
-
addAllParameters(extendParams);//这里将扩展参数写入参数表
-
}
-
-
private void modifyParameterValues() {//将parameter的值去除空格后重写回去
-
-
//获取加密数据
-
String aesParameter = getParameter(Constants.NetWork.AES_DATA);
-
log.debug("[modifyParameterValues]==========>加密数据:{}", aesParameter);
-
//解密
-
String decryptParameter = null;
-
try {
-
decryptParameter = AesUtils.decrypt(aesParameter, Constants.AES.AES_KEY);
-
log.debug("[modifyParameterValues]==========> 解密数据:{}", decryptParameter);
-
Map<String, Object> map = JSON.parseObject(decryptParameter);
-
Set<String> set = map.keySet();
-
for (String key : set) {
-
params.put(key, new String[]{String.valueOf(map.get(key))});
-
}
-
aesFlag(true);
-
} catch (CommonBusinessException e) {
-
aesFlag(false);
-
log.error("[modifyParameterValues]", e);
-
log.debug("[modifyParameterValues]==========>", e);
-
}
-
}
-
-
/**
-
* 解密成功标志
-
*/
-
private void aesFlag(boolean flag) {
-
params.put(Constants.NetWork.AES_SUCCESS, new String[]{String.valueOf(flag)});
-
}
-
-
@Override
-
public Map<String, String[]> getParameterMap() {
-
// return super.getParameterMap();
-
return params;
-
}
-
-
@Override
-
public Enumeration<String> getParameterNames() {
-
return new Vector<>(params.keySet()).elements();
-
}
-
-
@Override
-
public String getParameter(String name) {//重写getParameter,代表参数从当前类中的map获取
-
String[] values = params.get(name);
-
if (values == null || values.length == 0) {
-
return null;
-
}
-
return values[0];
-
}
-
-
public String[] getParameterValues(String name) {//同上
-
return params.get(name);
-
}
-
-
-
public void addAllParameters(Map<String, Object> otherParams) {//增加多个参数
-
for (Map.Entry<String, Object> entry : otherParams.entrySet()) {
-
addParameter(entry.getKey(), entry.getValue());
-
}
-
}
-
-
-
public void addParameter(String name, Object value) {//增加参数
-
if (value != null) {
-
if (value instanceof String[]) {
-
params.put(name, (String[]) value);
-
} else if (value instanceof String) {
-
params.put(name, new String[]{(String) value});
-
} else {
-
params.put(name, new String[]{String.valueOf(value)});
-
}
-
}
-
}
-
}
新建过滤器,在拦截器中调用自定义的参数包装器
-
/**
-
* Describe:请求参数过滤器
-
* Created by 吴蜀黍 on 2018-08-07 10:02
-
**/
-
@Slf4j
-
public class ParameterFilter implements Filter {
-
@Override
-
public void init(FilterConfig filterConfig) throws ServletException {
-
}
-
-
@Override
-
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
-
//使用自定义的参数包装器对参数进行处理
-
ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest) servletRequest);
-
filterChain.doFilter(requestWrapper, servletResponse);
-
}
-
-
@Override
-
public void destroy() {
-
}
-
}
web.xml中对过滤器进行配置
-
<!--过滤器-->
-
<filter>
-
<filter-name>parameterFilter</filter-name>
-
<filter-class>com.xxx.xxx.config.filter.ParameterFilter</filter-class>
-
</filter>
-
<filter-mapping>
-
<filter-name>parameterFilter</filter-name>
-
<!-- 过滤所有以.json结尾的资源-->
-
<url-pattern>*.json</url-pattern>
-
</filter-mapping>
AES加解密操作
-
/**
-
* Describe:AES 加密
-
* Created by 吴蜀黍 on 2018-08-03 17:47
-
**/
-
public class AesUtils {
-
private static final String CHARSET_NAME = "UTF-8";
-
private static final String AES_NAME = "AES";
-
private static final String ALGORITHM = "AES/CBC/PKCS7Padding";
-
private static final String IV = Constants.AES.AES_IV;
-
-
static {
-
Security.addProvider(new BouncyCastleProvider());
-
}
-
-
/**
-
* 加密
-
*/
-
public static String encrypt(@NotNull String content, @NotNull String key) throws CommonBusinessException {
-
try {
-
Cipher cipher = Cipher.getInstance(ALGORITHM);
-
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(CHARSET_NAME), AES_NAME);
-
AlgorithmParameterSpec paramSpec = new IvParameterSpec(IV.getBytes());
-
cipher.init(Cipher.ENCRYPT_MODE, keySpec, paramSpec);
-
return ParseSystemUtil.parseByte2HexStr(cipher.doFinal(content.getBytes(CHARSET_NAME)));
-
} catch (Exception ex) {
-
throw new CommonBusinessException("加密失败");
-
}
-
}
-
-
/**
-
* 解密
-
*/
-
public static String decrypt(@NotNull String content, @NotNull String key) throws CommonBusinessException {
-
try {
-
Cipher cipher = Cipher.getInstance(ALGORITHM);
-
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(CHARSET_NAME), AES_NAME);
-
AlgorithmParameterSpec paramSpec = new IvParameterSpec(IV.getBytes());
-
cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec);
-
return new String(cipher.doFinal(Objects.requireNonNull(ParseSystemUtil.parseHexStr2Byte(content))), CHARSET_NAME);
-
} catch (Exception ex) {
-
throw new CommonBusinessException("解密失败");
-
}
-
}
-
-
}
2.新建拦截器,验证Token以及解密的判断
-
-
@Override
-
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
-
//如果不是映射到方法直接通过
-
if (!(handler instanceof HandlerMethod)) {
-
return true;
-
}
-
//判断参数包装器中对请求参数的解密是否成功
-
boolean aesSuccess = Boolean.parseBoolean(httpServletRequest.getParameter(Constants.NetWork.AES_SUCCESS));
-
if (!aesSuccess) {
-
this.sendMsg(Constants.NetWork.CODE_DECRYPTION_FAILURE, Constants.NetWork.MEG_AES_FAIL, httpServletResponse);
-
return false;
-
}
-
//获取客户端上传Token
-
String token = httpServletRequest.getHeader(Constants.NetWork.TOKEN_HEAD_KEY);
-
if (StringUtils.isNullOrEmpty(token)) {
-
sendMsg(Constants.NetWork.CODE_TOKEN_INVALID, Constants.NetWork.MSG_TOKEN_EMPTY, httpServletResponse);
-
return false;
-
}
-
//验证Token的有效性
-
if (!TokenUtils.verificationToken(token, httpServletRequest.getParameterMap())) {
-
sendMsg(Constants.NetWork.CODE_TOKEN_INVALID, Constants.NetWork.MSG_TOKEN_INVALID, httpServletResponse);
-
return false;
-
}
-
return true;
-
}
-
-
/**
-
* 验证失败 发送消息
-
*/
-
private void sendMsg(String msgCode, String msg, HttpServletResponse httpServletResponse) throws IOException {
-
httpServletResponse.setContentType("application/json; charset=utf-8");
-
PrintWriter writer = httpServletResponse.getWriter();
-
String jsonString = JSON.toJSONString(StandardResult.create(msgCode, msg));
-
try {
-
//对验证失败的返回信息进行加密
-
jsonString = AesUtils.encrypt(jsonString, Constants.AES.AES_KEY);
-
} catch (CommonBusinessException e) {
-
e.printStackTrace();
-
jsonString = null;
-
log.error("[sendMsg]", e);
-
}
-
writer.print(jsonString);
-
writer.close();
-
httpServletResponse.flushBuffer();
-
}
在spring中对拦截器注册
-
<mvc:interceptors>
-
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
-
<mvc:interceptor>
-
<!-- 拦截所有请求 -->
-
<mvc:mapping path="/**"/>
-
<!-- 需排除拦截的地址 -->
-
<!--<mvc:exclude-mapping path="/"/>-->
-
<bean class="com.xxx.xxx.config.interceptor.AsyncHandlerInterceptor"/>
-
</mvc:interceptor>
-
</mvc:interceptors>
Token的验证
-
-
/**
-
* Describe:Token帮助类
-
* Created by 吴蜀黍 on 2018-08-04 14:48
-
**/
-
@Slf4j
-
public class TokenUtils {
-
/**
-
* 验证Token
-
*
-
* @param token 客户端上传Token
-
* @param mapTypes 请求参数集合
-
* @return boolean
-
*/
-
public static boolean verificationToken(String token, Map mapTypes) {
-
try {
-
return StringUtils.saleEquals(token, getToken(mapTypes));
-
} catch (UnsupportedEncodingException e) {
-
log.error("[verificationToken]", e);
-
return false;
-
}
-
}
-
-
-
/**
-
* 通过客户端请求参数产生Token
-
*/
-
private static String getToken(Map mapTypes) throws UnsupportedEncodingException {
-
List<String> mapKes = new ArrayList<>();
-
for (Object obj : mapTypes.keySet()) {
-
String value = String.valueOf(obj);
-
//去除参数中的加密相关key
-
if (StringUtils.saleEquals(value, Constants.NetWork.AES_SUCCESS) ||
-
StringUtils.saleEquals(value, Constants.NetWork.AES_DATA)) {
-
break;
-
}
-
mapKes.add(value);
-
}
-
//排序key
-
Collections.sort(mapKes);
-
StringBuilder sb = new StringBuilder();
-
for (String key : mapKes) {
-
String value = ((String[]) mapTypes.get(key))[0];
-
sb.append(value);
-
}
-
//加上时间戳,去除所有空格 进行MD5加密
-
String string = sb.append(DateUtils.getDateStr(DateUtils.FORMAT_YYYYMMDDHH)).toString().replace(" ", "");
-
return MD5.getMD5(URLEncoder.encode(string, "UTF-8"));
-
}
-
}
3.对返回数据进行加密处理,新建JSON转换器继承自阿里的FastJsonHttpMessageConverter
-
/**
-
* Describe:Json转换器 将返回数据加密
-
* Created by 吴蜀黍 on 2018-08-07 13:57
-
**/
-
@Slf4j
-
public class JsonMessageConverter extends FastJsonHttpMessageConverter {
-
-
@Override
-
protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,
-
HttpMessageNotWritableException {
-
OutputStream out = outputMessage.getBody();
-
try {
-
String jsonString = JSON.toJSONString(object);
-
log.debug("[writeInternal]======>返回明文数据:{}" + jsonString);
-
//对返回数据进行AES加密
-
jsonString = AesUtils.encrypt(jsonString, Constants.AES.AES_KEY);
-
log.debug("[writeInternal]======>返回加密数据:{}" + jsonString);
-
out.write(jsonString.getBytes());
-
} catch (CommonBusinessException e) {
-
e.printStackTrace();
-
log.error("[writeInternal]======>", e);
-
}
-
out.close();
-
}
-
}
spring中对JSON转换器进行配置
-
<mvc:message-converters>
-
<!--<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">-->
-
<bean class="com.xxx.xxx.config.converter.JsonMessageConverter">
-
<property name="supportedMediaTypes">
-
<list>
-
<value>text/html;charset=UTF-8</value>
-
<value>application/json</value>
-
<value>application/xml;charset=UTF-8</value>
-
</list>
-
</property>
-
<property name="features">
-
<list>
-
<!-- 默认的意思就是不配置这个属性,配置了就不是默认了 -->
-
<!-- 是否输出值为null的字段 ,默认是false-->
-
<value>WriteMapNullValue</value>
-
<value>WriteNullNumberAsZero</value>
-
<value>WriteNullListAsEmpty</value>
-
<value>WriteNullStringAsEmpty</value>
-
<value>WriteNullBooleanAsFalse</value>
-
<value>WriteDateUseDateFormat</value>
-
</list>
-
</property>
-
</bean>
-
</mvc:message-converters>
以上就是微信小程序接口加密如何实现的开发文档,更多小程序开发文档可以关注网站。
小程序工具提供多类型商城/门店小程序制作,可视化编辑 1秒生成5步上线。通过拖拽、拼接模块布局小程序商城页面,所看即所得,只需要美工就能做出精美商城。更多小程序请查看:小程序商店