欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-09-08 21:47:21
 微信小程序后台解密用户数据实例详解 微信小程序api文档:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-...

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

微信小程序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包。网上参考了一下,个人感觉加这个依赖是最容易解决问题的。

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

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