微信小程序后台解密用户数据实例详解


Posted in Javascript onJune 28, 2017

 微信小程序后台解密用户数据实例详解

微信小程序API文档:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html

openId : 用户在当前小程序的唯一标识

因为最近根据API调用https://api.weixin.qq.com/sns/jscode2session所以需要配置以下服务,但是官方是不赞成这种做法的,
而且最近把在服务器配置的方法给关闭了。也就是说要获取用户openid,地区等信息只能在后台获取。

一下是官方的流程

那么问题来了,代码怎么实现呢,以下是用java后台的实现

微信客户端的代码实现是这样的

wx.login({ 
   success: function (r) { 
    if (r.code) { 
     var code = r.code;//登录凭证 
     if (code) { 
      //2、调用获取用户信息接口 
      wx.getUserInfo({ 
       success: function (res) { 
        //发起网络请求 
        wx.request({ 
         url: that.data.net + '/decodeUser.json', 
         header: { 
          "content-type": "application/x-www-form-urlencoded" 
         }, 
         method: "POST", 
         data: { 
          encryptedData: res.encryptedData, 
          iv: res.iv, 
          code: code 
         }, 
         success: function (result) { 
          // wx.setStorage({ 
          //  key: 'openid', 
          //  data: res.data.openid, 
          // }) 
          console.log(result) 
         } 
        }) 
       }, 
       fail: function () { 
        console.log('获取用户信息失败') 
       } 
      }) 
     } else { 
      console.log('获取用户登录态失败!' + r.errMsg) 
     } 

    } else { 
    } 
   } 
  })

(服务端 java)自己的服务器发送code到微信服务器获取openid(用户唯一标识)和session_key(会话密钥),
最后将encryptedData、iv、session_key通过AES解密获取到用户敏感数据

1、获取秘钥并处理解密的controller

/** 
   * 解密用户敏感数据 
   * 
   * @param encryptedData 明文,加密数据 
   * @param iv      加密算法的初始向量 
   * @param code     用户允许登录后,回调内容会带上 code(有效期五分钟),开发者需要将 code 发送到开发者服务器后台,使用code 换取 session_key api,将 code 换成 openid 和 session_key 
   * @return 
   */ 
  @ResponseBody 
  @RequestMapping(value = "/decodeUser", method = RequestMethod.POST) 
  public Map decodeUser(String encryptedData, String iv, String code) { 

    Map map = new HashMap(); 

    //登录凭证不能为空 
    if (code == null || code.length() == 0) { 
      map.put("status", 0); 
      map.put("msg", "code 不能为空"); 
      return map; 
    } 

    //小程序唯一标识  (在微信小程序管理后台获取) 
    String wxspAppid = "wxd8980e77d335c871"; 
    //小程序的 app secret (在微信小程序管理后台获取) 
    String wxspSecret = "85d29ab4fa8c797423f2d7da5dd514cf"; 
    //授权(必填) 
    String grant_type = "authorization_code"; 


    //////////////// 1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid //////////////// 
    //请求参数 
    String params = "appid=" + wxspAppid + "&secret=" + wxspSecret + "&js_code=" + code + "&grant_type=" + grant_type; 
    //发送请求 
    String sr = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session", params); 
    //解析相应内容(转换成json对象) 
    JSONObject json = JSONObject.fromObject(sr); 
    //获取会话密钥(session_key) 
    String session_key = json.get("session_key").toString(); 
    //用户的唯一标识(openid) 
    String openid = (String) json.get("openid"); 

    //////////////// 2、对encryptedData加密数据进行AES解密 //////////////// 
    try { 
      String result = AesCbcUtil.decrypt(encryptedData, session_key, iv, "UTF-8"); 
      if (null != result && result.length() > 0) { 
        map.put("status", 1); 
        map.put("msg", "解密成功"); 

        JSONObject userInfoJSON = JSONObject.fromObject(result); 
        Map userInfo = new HashMap(); 
        userInfo.put("openId", userInfoJSON.get("openId")); 
        userInfo.put("nickName", userInfoJSON.get("nickName")); 
        userInfo.put("gender", userInfoJSON.get("gender")); 
        userInfo.put("city", userInfoJSON.get("city")); 
        userInfo.put("province", userInfoJSON.get("province")); 
        userInfo.put("country", userInfoJSON.get("country")); 
        userInfo.put("avatarUrl", userInfoJSON.get("avatarUrl")); 
        userInfo.put("unionId", userInfoJSON.get("unionId")); 
        map.put("userInfo", userInfo); 
        return map; 
      } 
    } catch (Exception e) { 
      e.printStackTrace(); 
    } 
    map.put("status", 0); 
    map.put("msg", "解密失败"); 
    return map; 
  }

解密工具类 AesCbcUtil

import org.apache.commons.codec.binary.Base64; 
import org.bouncycastle.jce.provider.BouncyCastleProvider; 

import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.NoSuchPaddingException; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 
import java.io.UnsupportedEncodingException; 
import java.security.*; 
import java.security.spec.InvalidParameterSpecException; 

/** 
 * Created by lsh 
 * AES-128-CBC 加密方式 
 * 注: 
 * AES-128-CBC可以自己定义“密钥”和“偏移量“。 
 * AES-128是jdk自动生成的“密钥”。 
 */ 
public class AesCbcUtil { 


  static { 
    //BouncyCastle是一个开源的加解密解决方案,主页在http://www.bouncycastle.org/ 
    Security.addProvider(new BouncyCastleProvider()); 
  } 

  /** 
   * AES解密 
   * 
   * @param data      //密文,被加密的数据 
   * @param key      //秘钥 
   * @param iv       //偏移量 
   * @param encodingFormat //解密后的结果需要进行的编码 
   * @return 
   * @throws Exception 
   */ 
  public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception { 
//    initialize(); 

    //被加密的数据 
    byte[] dataByte = Base64.decodeBase64(data); 
    //加密秘钥 
    byte[] keyByte = Base64.decodeBase64(key); 
    //偏移量 
    byte[] ivByte = Base64.decodeBase64(iv); 


    try { 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 

      SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); 

      AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); 
      parameters.init(new IvParameterSpec(ivByte)); 

      cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化 

      byte[] resultByte = cipher.doFinal(dataByte); 
      if (null != resultByte && resultByte.length > 0) { 
        String result = new String(resultByte, encodingFormat); 
        return result; 
      } 
      return null; 
    } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
    } catch (NoSuchPaddingException e) { 
      e.printStackTrace(); 
    } catch (InvalidParameterSpecException e) { 
      e.printStackTrace(); 
    } catch (InvalidKeyException e) { 
      e.printStackTrace(); 
    } catch (InvalidAlgorithmParameterException e) { 
      e.printStackTrace(); 
    } catch (IllegalBlockSizeException e) { 
      e.printStackTrace(); 
    } catch (BadPaddingException e) { 
      e.printStackTrace(); 
    } catch (UnsupportedEncodingException e) { 
      e.printStackTrace(); 
    } 

    return null; 
  } 

}

发送请求的工具类HttpRequest

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.io.PrintWriter; 
import java.net.URL; 
import java.net.URLConnection; 
import java.util.List; 
import java.util.Map; 

/** 
 * Created by lsh on 2017/6/22. 
 */ 
public class HttpRequest { 
  /** 
   * 向指定URL发送GET方法的请求 
   * 
   * @param url 
   *      发送请求的URL 
   * @param param 
   *      请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 
   * @return URL 所代表远程资源的响应结果 
   */ 
  public static String sendGet(String url, String param) { 
    String result = ""; 
    BufferedReader in = null; 
    try { 
      String urlNameString = url + "?" + param; 
      URL realUrl = new URL(urlNameString); 
      // 打开和URL之间的连接 
      URLConnection connection = realUrl.openConnection(); 
      // 设置通用的请求属性 
      connection.setRequestProperty("accept", "*/*"); 
      connection.setRequestProperty("connection", "Keep-Alive"); 
      connection.setRequestProperty("user-agent", 
          "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); 
      // 建立实际的连接 
      connection.connect(); 
      // 获取所有响应头字段 
      Map<String, List<String>> map = connection.getHeaderFields(); 
      // 遍历所有的响应头字段 
      for (String key : map.keySet()) { 
        System.out.println(key + "--->" + map.get(key)); 
      } 
      // 定义 BufferedReader输入流来读取URL的响应 
      in = new BufferedReader(new InputStreamReader( 
          connection.getInputStream())); 
      String line; 
      while ((line = in.readLine()) != null) { 
        result += line; 
      } 
    } catch (Exception e) { 
      System.out.println("发送GET请求出现异常!" + e); 
      e.printStackTrace(); 
    } 
    // 使用finally块来关闭输入流 
    finally { 
      try { 
        if (in != null) { 
          in.close(); 
        } 
      } catch (Exception e2) { 
        e2.printStackTrace(); 
      } 
    } 
    return result; 
  } 

  /** 
   * 向指定 URL 发送POST方法的请求 
   * 
   * @param url 
   *      发送请求的 URL 
   * @param param 
   *      请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 
   * @return 所代表远程资源的响应结果 
   */ 
  public static String sendPost(String url, String param) { 
    PrintWriter out = null; 
    BufferedReader in = null; 
    String result = ""; 
    try { 
      URL realUrl = new URL(url); 
      // 打开和URL之间的连接 
      URLConnection conn = realUrl.openConnection(); 
      // 设置通用的请求属性 
      conn.setRequestProperty("accept", "*/*"); 
      conn.setRequestProperty("connection", "Keep-Alive"); 
      conn.setRequestProperty("user-agent", 
          "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); 
      // 发送POST请求必须设置如下两行 
      conn.setDoOutput(true); 
      conn.setDoInput(true); 
      // 获取URLConnection对象对应的输出流 
      out = new PrintWriter(conn.getOutputStream()); 
      // 发送请求参数 
      out.print(param); 
      // flush输出流的缓冲 
      out.flush(); 
      // 定义BufferedReader输入流来读取URL的响应 
      in = new BufferedReader( 
          new InputStreamReader(conn.getInputStream())); 
      String line; 
      while ((line = in.readLine()) != null) { 
        result += line; 
      } 
    } catch (Exception e) { 
      System.out.println("发送 POST 请求出现异常!"+e); 
      e.printStackTrace(); 
    } 
    //使用finally块来关闭输出流、输入流 
    finally{ 
      try{ 
        if(out!=null){ 
          out.close(); 
        } 
        if(in!=null){ 
          in.close(); 
        } 
      } 
      catch(IOException ex){ 
        ex.printStackTrace(); 
      } 
    } 
    return result; 
  } 
}

另外由于需求使用解密的工具类所有要在pom文件加上这个依赖

<dependency> 
  <groupId>org.bouncycastle</groupId> 
  <artifactId>bcprov-ext-jdk16</artifactId> 
  <version>1.46</version> 
  <type>jar</type> 
  <scope>compile</scope> 
</dependency>

这样才能引入bcprov这个jar包。网上参考了一下,个人感觉加这个依赖是最容易解决问题的。

最近打算弄个关于微信运动的小程序,解密这块估计也要用到。大家有疑问可以一起留言交流

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

Javascript 相关文章推荐
javascript 表单规则集合对象
Jul 21 Javascript
浅析jQuery对select操作小结(遍历option,操作option)
Jul 04 Javascript
JS 获取滚动条高度示例代码
Oct 24 Javascript
JQuery拖动表头边框线调整表格列宽效果代码
Sep 10 Javascript
用Node.js通过sitemap.xml批量抓取美女图片
May 28 Javascript
测试IE浏览器对JavaScript的AngularJS的兼容性
Jun 19 Javascript
jQuery时间插件jquery.clock.js用法实例(5个示例)
Jan 14 Javascript
浅析BootStrap栅格系统
Jun 07 Javascript
如何正确理解javascript的模块化
Mar 02 Javascript
微信小程序wx.getImageInfo()如何获取图片信息
Jan 26 Javascript
Vue的transition-group与Virtual Dom Diff算法的使用
Dec 09 Javascript
vue学习之Vue-Router用法实例分析
Jan 06 Javascript
JavaScript箭头函数_动力节点Java学院整理
Jun 28 #Javascript
JavaScript之filter_动力节点Java学院整理
Jun 28 #Javascript
JavaScript高阶函数_动力节点Java学院整理
Jun 28 #Javascript
JavaScript之Date_动力节点Java学院整理
Jun 28 #Javascript
ES6深入理解之“let”能替代”var“吗?
Jun 28 #Javascript
jQuery、layer实现弹出层的打开、关闭功能
Jun 28 #jQuery
AngularJS实现单一页面内设置跳转路由的方法
Jun 28 #Javascript
You might like
php url路由入门实例
2014/04/23 PHP
php简单统计在线人数的方法
2016/05/10 PHP
PHP实现QQ快速登录的方法
2016/09/28 PHP
php用wangeditor3实现图片上传功能
2019/08/22 PHP
phpstudy后门rce批量利用脚本的实现
2019/12/12 PHP
php7 错误处理机制修改实例分析
2020/05/25 PHP
用jquery实现学校的校历(asp.net+jquery ui 1.72)
2010/01/01 Javascript
location.href语句与火狐不兼容的问题
2010/07/04 Javascript
CSS(js)限制页面显示的文本字符长度
2012/12/27 Javascript
jQuery对象数据缓存Cache原理及jQuery.data方法区别介绍
2013/04/07 Javascript
javascript中直接写php代码的方法
2013/07/31 Javascript
javascript继承机制实例详解
2014/11/20 Javascript
简单实现JS对dom操作封装
2015/12/02 Javascript
微信js-sdk地理位置接口用法示例
2016/10/12 Javascript
JS日期对象简单操作(获取当前年份、星期、时间)
2016/10/26 Javascript
微信小程序 本地数据读取实例
2017/04/27 Javascript
JS Input里添加小图标的两种方法
2017/11/11 Javascript
详解vue项目的构建,打包,发布全过程
2017/11/23 Javascript
解决vue-cli + webpack 新建项目出错的问题
2018/03/20 Javascript
基于Vue-Cli 打包自动生成/抽离相关配置文件的实现方法
2018/12/09 Javascript
vue-router的钩子函数用法实例分析
2019/10/26 Javascript
js实现飞机大战游戏
2020/08/26 Javascript
Python中利用原始套接字进行网络编程的示例
2015/05/04 Python
Python3解决棋盘覆盖问题的方法示例
2017/12/07 Python
Python操作redis和mongoDB的方法
2019/12/19 Python
Tensorflow限制CPU个数实例
2020/02/06 Python
C#中有没有运算符重载?能否使用指针?
2014/05/05 面试题
信息专业学生学习的自我评价
2014/02/17 职场文书
研究生考核个人自我鉴定
2014/03/27 职场文书
教研活动总结
2014/04/28 职场文书
离婚协议书范本(2014版)
2014/09/28 职场文书
公务员年度考核个人总结
2015/02/12 职场文书
2019年英语版感谢信(8篇)
2019/09/29 职场文书
SpringBoot项目中控制台日志的保存配置操作
2021/06/18 Java/Android
Ubuntu18.04下QT开发Android无法连接设备问题解决实现
2022/06/01 Java/Android
Django框架中表单的用法
2022/06/10 Python