千家信息网

微信小程序接口加密怎么实现

发表于:2024-11-30 作者:千家信息网编辑
千家信息网最后更新 2024年11月30日,本篇内容主要讲解"微信小程序接口加密怎么实现",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"微信小程序接口加密怎么实现"吧!场景小程序请求的所有接口参数必须
千家信息网最后更新 2024年11月30日微信小程序接口加密怎么实现

本篇内容主要讲解"微信小程序接口加密怎么实现",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"微信小程序接口加密怎么实现"吧!

场景

小程序请求的所有接口参数必须加密,后台返回数据也需要加密,并且增加Token验证

一、小程序端功能编写

1.下载一份Js版的aesUtil.js源码。
2.下载一份Js版的md5.js源码。
3.在pulic.js中进行加解密操作代码如下,其中秘钥和秘钥偏移量要与后台的一致。

  1. var CryptoJS = require('aesUtil.js'); //引用AES源码js

  2. var md5 = require('md5.js')

  3. var key = CryptoJS.enc.Utf8.parse("76CAA1C88F7F8D1D"); //十六位十六进制数作为秘钥

  4. var iv = CryptoJS.enc.Utf8.parse('91129048100F0494'); //十六位十六进制数作为秘钥偏移量

  5. //解密方法

  6. function Decrypt(word) {

  7. var encryptedHexStr = CryptoJS.enc.Hex.parse(word);

  8. var srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);

  9. var decrypt = CryptoJS.AES.decrypt(srcs, key, {

  10. iv: iv,

  11. mode: CryptoJS.mode.CBC,

  12. padding: CryptoJS.pad.Pkcs7

  13. });

  14. var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);

  15. return decryptedStr.toString();

  16. }

  17. //加密方法

  18. function Encrypt(word) {

  19. var srcs = CryptoJS.enc.Utf8.parse(word);

  20. var encrypted = CryptoJS.AES.encrypt(srcs, key, {

  21. iv: iv,

  22. mode: CryptoJS.mode.CBC,

  23. padding: CryptoJS.pad.Pkcs7

  24. });

  25. return encrypted.ciphertext.toString().toUpperCase();

  26. }

  27. //暴露接口

  28. module.exports.Decrypt = Decrypt;

  29. 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)//根据特定规则生成Tokenvar 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**/@Slf4jpublic class ParameterRequestWrapper extends HttpServletRequestWrapper { private Map 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 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 map = JSON.parseObject(decryptParameter);Set 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)});} @Overridepublic Map getParameterMap() {// return super.getParameterMap();return params;} @Overridepublic Enumeration getParameterNames() {return new Vector<>(params.keySet()).elements();} @Overridepublic 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 otherParams) {//增加多个参数for (Map.Entry 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**/@Slf4jpublic class ParameterFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {} @Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//使用自定义的参数包装器对参数进行处理ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest) servletRequest);filterChain.doFilter(requestWrapper, servletResponse);} @Overridepublic void destroy() {}}

web.xml中对过滤器进行配置

parameterFiltercom.xxx.xxx.config.filter.ParameterFilterparameterFilter*.json

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以及解密的判断

@Overridepublic 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;}//获取客户端上传TokenString 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中对拦截器注册

    Token的验证

    /*** Describe:Token帮助类* Created by 吴蜀黍 on 2018-08-04 14:48**/@Slf4jpublic 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 mapKes = new ArrayList<>();for (Object obj : mapTypes.keySet()) {String value = String.valueOf(obj);//去除参数中的加密相关keyif (StringUtils.saleEquals(value, Constants.NetWork.AES_SUCCESS) ||StringUtils.saleEquals(value, Constants.NetWork.AES_DATA)) {break;}mapKes.add(value);}//排序keyCollections.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**/@Slf4jpublic class JsonMessageConverter extends FastJsonHttpMessageConverter { @Overrideprotected 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转换器进行配置

      text/html;charset=UTF-8application/jsonapplication/xml;charset=UTF-8WriteMapNullValueWriteNullNumberAsZeroWriteNullListAsEmptyWriteNullStringAsEmptyWriteNullBooleanAsFalseWriteDateUseDateFormat

      到此,相信大家对"微信小程序接口加密怎么实现"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

      0