溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

微信小程序接口加密怎么實現(xiàn)

發(fā)布時間:2022-03-11 10:48:47 來源:億速云 閱讀:513 作者:iii 欄目:開發(fā)技術

本篇內(nèi)容主要講解“微信小程序接口加密怎么實現(xiàn)”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“微信小程序接口加密怎么實現(xiàn)”吧!

微信小程序接口加密怎么實現(xiàn)

場景

小程序請求的所有接口參數(shù)必須加密,后臺返回數(shù)據(jù)也需要加密,并且增加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.  

  4. var key = CryptoJS.enc.Utf8.parse("76CAA1C88F7F8D1D"); //十六位十六進制數(shù)作為秘鑰

  5. var iv = CryptoJS.enc.Utf8.parse('91129048100F0494'); //十六位十六進制數(shù)作為秘鑰偏移量

  6. //解密方法

  7. function Decrypt(word) {

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

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

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

  11. iv: iv,

  12. mode: CryptoJS.mode.CBC,

  13. padding: CryptoJS.pad.Pkcs7

  14. });

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

  16. return decryptedStr.toString();

  17. }

  18. //加密方法

  19. function Encrypt(word) {

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

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

  22. iv: iv,

  23. mode: CryptoJS.mode.CBC,

  24. padding: CryptoJS.pad.Pkcs7

  25. });

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

  27. }

  28.  

  29. //暴露接口

  30. module.exports.Decrypt = Decrypt;

  31. module.exports.Encrypt = Encrypt;

4.在網(wǎng)絡請求幫助類中進行參數(shù)的加密和返回數(shù)據(jù)的解密操作。

var aes = require('../utils/public.js')var md5 = require("../utils/md5.js") ... /*** 網(wǎng)絡請求*/function request(method, loading, url, params, success, fail) {var url = BASE_URL + url;//請求參數(shù)轉(zhuǎn)為JSON字符串var jsonStr = JSON.stringify(params);console.log(url + ' params=> ' + jsonStr)//根據(jù)特定規(guī)則生成Tokenvar token = productionToken(params);//加密請求參數(shù)var aesData = aes.Encrypt(jsonStr)console.log('請求=>明文參數(shù):' + jsonStr)console.log('請求=>加密參數(shù):' + 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) {//解密返回數(shù)據(jù)console.log('返回=>加密數(shù)據(jù):' + res.data);var result = aes.Decrypt(res.data);console.log('返回=>明文數(shù)據(jù):'+result);success(JSON.parse(result))} else {fail()}},fail: function(res) {fail()},})}

其中生成Token的規(guī)則,【生成Token的規(guī)則可根據(jù)具體的業(yè)務邏輯自己定義,我這里使用的規(guī)則是根據(jù)請求參數(shù)的字母排序取其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的排序函數(shù)function objKeySort(obj) {//先用Object內(nèi)置類的keys方法獲取要排序?qū)ο蟮膶傩悦?,再利用Array原型上的sort方法對獲取的屬性名進行排序,newkey是一個數(shù)組var newkey = Object.keys(obj).sort();//創(chuàng)建一個新的對象,用于存放排好序的鍵值對  var newObj = {};//遍歷newkey數(shù)組for (var i = 0; i < newkey.length; i++) {//向新創(chuàng)建的對象中按照排好的順序依次增加鍵值對newObj[newkey[i]] = obj[newkey[i]];}//返回排好序的新對象return newObj;}

二、服務端功能編寫

由于初學SpringMVC,使用的方式不一定是最優(yōu)最好的,如有不妥善之處,請各位看官多多指教  思路:

通過過濾器攔截請求參數(shù),通過自定義參數(shù)包裝器對參數(shù)進行解密。  在攔截器獲取請求的Token并生成服務器端Token進行驗證。  對返回參數(shù)通過JSON轉(zhuǎn)換器進行加密處理。

 1.重寫HttpServletRequestWrapper,在自定義的HttpServletRequestWrapper 中對參數(shù)進行處理

/*** Describe:請求參數(shù)包裝器 主要作用的過濾參數(shù)并解密* Created by 吳蜀黍 on 2018-08-07 09:37**/@Slf4jpublic class ParameterRequestWrapper extends HttpServletRequestWrapper { private Map<String, String[]> params = new HashMap<>(); @SuppressWarnings("unchecked")public ParameterRequestWrapper(HttpServletRequest request) {// 將request交給父類,以便于調(diào)用對應方法的時候,將其輸出,其實父親類的實現(xiàn)方式和第一種new的方式類似super(request);//將參數(shù)表,賦予給當前的Map以便于持有request中的參數(shù)this.params.putAll(request.getParameterMap());this.modifyParameterValues();} //重載一個構造方法public ParameterRequestWrapper(HttpServletRequest request, Map<String, Object> extendParams) {this(request);addAllParameters(extendParams);//這里將擴展參數(shù)寫入?yún)?shù)表} private void modifyParameterValues() {//將parameter的值去除空格后重寫回去 //獲取加密數(shù)據(jù)String aesParameter = getParameter(Constants.NetWork.AES_DATA);log.debug("[modifyParameterValues]==========>加密數(shù)據(jù):{}", aesParameter);//解密String decryptParameter = null;try {decryptParameter = AesUtils.decrypt(aesParameter, Constants.AES.AES_KEY);log.debug("[modifyParameterValues]==========> 解密數(shù)據(jù):{}", 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)});} @Overridepublic Map<String, String[]> getParameterMap() {// return super.getParameterMap();return params;} @Overridepublic Enumeration<String> getParameterNames() {return new Vector<>(params.keySet()).elements();} @Overridepublic String getParameter(String name) {//重寫getParameter,代表參數(shù)從當前類中的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) {//增加多個參數(shù)for (Map.Entry<String, Object> entry : otherParams.entrySet()) {addParameter(entry.getKey(), entry.getValue());}}  public void addParameter(String name, Object value) {//增加參數(shù)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)});}}}}

新建過濾器,在攔截器中調(diào)用自定義的參數(shù)包裝器

/*** Describe:請求參數(shù)過濾器* 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 {//使用自定義的參數(shù)包裝器對參數(shù)進行處理ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest) servletRequest);filterChain.doFilter(requestWrapper, servletResponse);} @Overridepublic 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以及解密的判斷

@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {//如果不是映射到方法直接通過if (!(handler instanceof HandlerMethod)) {return true;}//判斷參數(shù)包裝器中對請求參數(shù)的解密是否成功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;} /*** 驗證失敗 發(fā)送消息*/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();}
  1.  

在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**/@Slf4jpublic class TokenUtils {/*** 驗證Token** @param token 客戶端上傳Token* @param mapTypes 請求參數(shù)集合* @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;}}  /*** 通過客戶端請求參數(shù)產(chǎn)生Token*/private static String getToken(Map mapTypes) throws UnsupportedEncodingException {List<String> mapKes = new ArrayList<>();for (Object obj : mapTypes.keySet()) {String value = String.valueOf(obj);//去除參數(shù)中的加密相關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"));}}
  1.  

3.對返回數(shù)據(jù)進行加密處理,新建JSON轉(zhuǎn)換器繼承自阿里的FastJsonHttpMessageConverter

/*** Describe:Json轉(zhuǎn)換器 將返回數(shù)據(jù)加密* 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]======>返回明文數(shù)據(jù):{}" + jsonString);//對返回數(shù)據(jù)進行AES加密jsonString = AesUtils.encrypt(jsonString, Constants.AES.AES_KEY);log.debug("[writeInternal]======>返回加密數(shù)據(jù):{}" + jsonString);out.write(jsonString.getBytes());} catch (CommonBusinessException e) {e.printStackTrace();log.error("[writeInternal]======>", e);}out.close();}}

spring中對JSON轉(zhuǎn)換器進行配置

<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>

到此,相信大家對“微信小程序接口加密怎么實現(xiàn)”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關內(nèi)容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI