小程序模板網(wǎng)

小程序encryptedData

發(fā)布時(shí)間:2017-12-16 14:56 所屬欄目:小程序開發(fā)教程

準(zhǔn)備知識(shí):

  1. Base64編解碼
  2. AES算法、填充模式、偏移向量
  3. session_key會(huì)話密鑰,以及怎么存儲(chǔ)和獲取

以上3點(diǎn)對(duì)于理解解密流程非常重要。

根據(jù)官方文檔,我梳理了大致的解密流程,如下:

  1. 小程序客戶端調(diào)用wx.login,回調(diào)里面包含js_code。
  2. 然后將js_code發(fā)送到服務(wù)器A(開發(fā)者服務(wù)器),服務(wù)器A向微信服務(wù)器發(fā)起請(qǐng)求附帶js_code、appId、secretkey和grant_type參數(shù),以換取用戶的openid和session_key(會(huì)話密鑰)。
  3. 服務(wù)器A拿到session_key后,生成一個(gè)隨機(jī)數(shù)我們叫3rd_session,以3rdSessionId為key,以session_key + openid為value緩存到redis或memcached中;因?yàn)槲⑿艌F(tuán)隊(duì)不建議直接將session_key在網(wǎng)絡(luò)上傳輸,由開發(fā)者自行生成唯一鍵與session_key關(guān)聯(lián)。其作用是:
    1. 將3rdSessionId返回給客戶端,維護(hù)小程序登錄態(tài)。
    2. 通過3rdSessionId找到用戶session_key和openid。
  4. 客戶端拿到3rdSessionId后緩存到storage,
  5. 通過wx.getUserIinfo可以獲取到用戶敏感數(shù)據(jù)encryptedData 。
  6. 客戶端將encryptedData、3rdSessionId和偏移量一起發(fā)送到服務(wù)器A
  7. 服務(wù)器A根據(jù)3rdSessionId從緩存中獲取session_key
  8. 在服務(wù)器A使用AES解密encryptedData,從而實(shí)現(xiàn)用戶敏感數(shù)據(jù)解密

重點(diǎn)在6、7、8三個(gè)環(huán)節(jié)。
AES解密三個(gè)參數(shù):

  • 密文 encryptedData
  • 密鑰 aesKey
  • 偏移向量 iv

服務(wù)端解密流程:

  1. 密文和偏移向量由客戶端發(fā)送給服務(wù)端,對(duì)這兩個(gè)參數(shù)在服務(wù)端進(jìn)行Base64_decode解編碼操作。
  2. 根據(jù)3rdSessionId從緩存中獲取session_key,對(duì)session_key進(jìn)行Base64_decode可以得到aesKey,aes密鑰。
  3. 調(diào)用aes解密方法,算法為 AES-128-CBC,數(shù)據(jù)采用PKCS#7填充。

下面結(jié)合小程序?qū)嵗f明解密流程:

  1. 微信登錄,獲取用戶信息

    var that = this;
    wx.login({
    success: function (res) {
        //微信js_code
        that.setData({wxcode: res.code});
        //獲取用戶信息
        wx.getUserInfo({
            success: function (res) {
                //獲取用戶敏感數(shù)據(jù)密文和偏移向量
                that.setData({encryptedData: res.encryptedData})
                that.setData({iv: res.iv})
            }
        })
    }
    })
  2. 使用code換取3rdSessionId

    var httpclient = require('../../utils/httpclient.js')
    VAR that = this
    //httpclient.req(url, data, method, success, fail)
    httpclient.req(
      'http://localhost:8090/wxappservice/api/v1/wx/getSession',
      {
          apiName: 'WX_CODE',
          code: this.data.wxcode
      },
      'GET',
      function(result){
        var thirdSessionId = result.data.data.sessionId;
        that.setData({thirdSessionId: thirdSessionId})
        //將thirdSessionId放入小程序緩存
        wx.setStorageSync('thirdSessionId', thirdSessionId)
      },
      function(result){
        console.log(result)
      }
    );
  3. 發(fā)起解密請(qǐng)求

    //httpclient.req(url, data, method, success, fail)
    httpclient.req(
    'http://localhost:8090/wxappservice/api/v1/wx/decodeUserInfo',
      {
        apiName: 'WX_DECODE_USERINFO',
        encryptedData: this.data.encryptedData,
        iv: this.data.iv,
        sessionId: wx.getStorageSync('thirdSessionId')
      },
      'GET',
      function(result){
      //解密后的數(shù)據(jù)
        console.log(result.data)
      },
      function(result){
        console.log(result)
      }
    );
  4. 服務(wù)端解密實(shí)現(xiàn)(java)

    /**
     * 解密用戶敏感數(shù)據(jù)
     * @param encryptedData 明文
     * @param iv            加密算法的初始向量
     * @param sessionId     會(huì)話ID
     * @return
     */
    @Api(name = ApiConstant.WX_DECODE_USERINFO)
    @RequestMapping(value = "/api/v1/wx/decodeUserInfo", method = RequestMethod.GET, produces = "application/json")
    public Map<String,Object> decodeUserInfo(@RequestParam(required = true,value = "encryptedData")String encryptedData,
            @RequestParam(required = true,value = "iv")String iv,
            @RequestParam(required = true,value = "sessionId")String sessionId){
    
        //從緩存中獲取session_key
        Object wxSessionObj = redisUtil.get(sessionId);
        if(null == wxSessionObj){
            return rtnParam(40008, null);
        }
        String wxSessionStr = (String)wxSessionObj;
        String sessionKey = wxSessionStr.split("#")[0];
    
        try {
            AES aes = new AES();
            byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv));
            if(null != resultByte && resultByte.length > 0){
                String userInfo = new String(resultByte, "UTF-8");
                return rtnParam(0, userInfo);
            }
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return rtnParam(50021, null);
    }
  5. AES解密核心代碼

    public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
        initialize();
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            Key sKeySpec = new SecretKeySpec(keyByte, "AES");
    
            cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化 
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();  
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();  
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


易優(yōu)小程序(企業(yè)版)+靈活api+前后代碼開源 碼云倉庫:starfork
本文地址:http://22321a.com/wxmini/doc/course/18179.html 復(fù)制鏈接 如需定制請(qǐng)聯(lián)系易優(yōu)客服咨詢:800182392 點(diǎn)擊咨詢
QQ在線咨詢