微信小程序接口加密如何實現(xiàn)
微信小程序請求的所有接口參數(shù)必須加密,那么小程序接口加密如何實現(xiàn)。
微信小程序接口實現(xiàn)加密教程:
場景
小程序請求的所有接口參數(shù)必須加密,后臺返回數(shù)據(jù)也需要加密,并且增加Token驗證
一、小程序端功能編寫
1.下載一份Js版的aesUtil.js源碼。【注:文章末尾會貼出所有的相關(guān)類文件】
2.下載一份Js版的md5.js源碼。
3.在pulic.js中進(jìn)行加解密操作代碼如下,其中秘鑰和秘鑰偏移量要與后臺的一致。
- var CryptoJS = require('aesUtil.js'); //引用AES源碼js
- var md5 = require('md5.js')
- var key = CryptoJS.enc.Utf8.parse("76CAA1C88F7F8D1D"); //十六位十六進(jìn)制數(shù)作為秘鑰
- var iv = CryptoJS.enc.Utf8.parse('91129048100F0494'); //十六位十六進(jìn)制數(shù)作為秘鑰偏移量
- //解密方法
- 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.在網(wǎng)絡(luò)請求幫助類中進(jìn)行參數(shù)的加密和返回數(shù)據(jù)的解密操作。
- var aes = require('../utils/public.js')
- var md5 = require("../utils/md5.js")
- ...
- /**
- * 網(wǎng)絡(luò)請求
- */
- 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ī)則生成Token
- var 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) {
- //判斷請求結(jié)果是否成功
- 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è)務(wù)邏輯自己定義,我這里使用的規(guī)則是根據(jù)請求參數(shù)的字母排序取其value并加上當(dāng)前時間戳再進(jìn)行MD5加密】
- /**
- * 生成Token
- */
- function productionToken(params) {
- var obj = util.objKeySort(params);
- var value = '';
- for (var item in obj) {
- value += obj[item];
- }
- //加上當(dāng)前時間戳
- value += util.getTokenDate(new Date())
- //去除所有空格
- value = value.replace(/\s+/g, "")
- //進(jìn)行UTF-8編碼
- value = encodeURI(value);
- //進(jìn)行MD5碼加密
- value = md5.hex_md5(value)
- return value;
- }
- //util的排序函數(shù)
- function objKeySort(obj) {
- //先用Object內(nèi)置類的keys方法獲取要排序?qū)ο蟮膶傩悦?,再利用Array原型上的sort方法對獲取的屬性名進(jìn)行排序,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;
- }
二、服務(wù)端功能編寫
由于初學(xué)SpringMVC,使用的方式不一定是最優(yōu)最好的,如有不妥善之處,請各位看官多多指教 思路:
通過過濾器攔截請求參數(shù),通過自定義參數(shù)包裝器對參數(shù)進(jìn)行解密。 在攔截器獲取請求的Token并生成服務(wù)器端Token進(jìn)行驗證。 對返回參數(shù)通過JSON轉(zhuǎn)換器進(jìn)行加密處理。
1.重寫HttpServletRequestWrapper,在自定義的HttpServletRequestWrapper 中對參數(shù)進(jìn)行處理
- /**
- * Describe:請求參數(shù)包裝器 主要作用的過濾參數(shù)并解密
- * 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交給父類,以便于調(diào)用對應(yīng)方法的時候,將其輸出,其實父親類的實現(xiàn)方式和第一種new的方式類似
- super(request);
- //將參數(shù)表,賦予給當(dāng)前的Map以便于持有request中的參數(shù)
- this.params.putAll(request.getParameterMap());
- this.modifyParameterValues();
- }
- //重載一個構(gòu)造方法
- public ParameterRequestWrapper(HttpServletRequest request, Map<String, Object> extendParams) {
- this(request);
- addAllParameters(extendParams);//這里將擴(kuò)展參數(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);
- }
- }
- /**
- * 解密成功標(biāo)志
- */
- 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,代表參數(shù)從當(dāng)前類中的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
- **/
- @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 {
- //使用自定義的參數(shù)包裝器對參數(shù)進(jìn)行處理
- ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest) servletRequest);
- filterChain.doFilter(requestWrapper, servletResponse);
- }
- @Override
- public void destroy() {
- }
- }
web.xml中對過濾器進(jìn)行配置
- <!--過濾器-->
- <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結(jié)尾的資源-->
- <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;
- }
- //判斷參數(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;
- }
- //獲取客戶端上傳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;
- }
- /**
- * 驗證失敗 發(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 {
- //對驗證失敗的返回信息進(jìn)行加密
- 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 請求參數(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ù)中的加密相關(guān)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);
- }
- //加上時間戳,去除所有空格 進(jìn)行MD5加密
- String string = sb.append(DateUtils.getDateStr(DateUtils.FORMAT_YYYYMMDDHH)).toString().replace(" ", "");
- return MD5.getMD5(URLEncoder.encode(string, "UTF-8"));
- }
- }
3.對返回數(shù)據(jù)進(jìn)行加密處理,新建JSON轉(zhuǎn)換器繼承自阿里的FastJsonHttpMessageConverter
- /**
- * Describe:Json轉(zhuǎn)換器 將返回數(shù)據(jù)加密
- * 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]======>返回明文數(shù)據(jù):{}" + jsonString);
- //對返回數(shù)據(jù)進(jìn)行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)換器進(jì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>
- <!-- 默認(rèn)的意思就是不配置這個屬性,配置了就不是默認(rèn)了 -->
- <!-- 是否輸出值為null的字段 ,默認(rèn)是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)的開發(fā)文檔,更多小程序開發(fā)文檔可以關(guān)注網(wǎng)站。
HiShop小程序工具提供多類型商城/門店小程序制作,可視化編輯 1秒生成5步上線。通過拖拽、拼接模塊布局小程序商城頁面,所看即所得,只需要美工就能做出精美商城。更多小程序請查看:小程序商店