微信小程序后台解密用户数据实例详解
微信小程序后台解密用户数据实例详解
微信小程序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){ varcode=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
/** *解密用户敏感数据 * *@paramencryptedData明文,加密数据 *@paramiv加密算法的初始向量 *@paramcode用户允许登录后,回调内容会带上code(有效期五分钟),开发者需要将code发送到开发者服务器后台,使用code换取session_keyapi,将code换成openid和session_key *@return */ @ResponseBody @RequestMapping(value="/decodeUser",method=RequestMethod.POST) publicMapdecodeUser(StringencryptedData,Stringiv,Stringcode){ Mapmap=newHashMap(); //登录凭证不能为空 if(code==null||code.length()==0){ map.put("status",0); map.put("msg","code不能为空"); returnmap; } //小程序唯一标识(在微信小程序管理后台获取) StringwxspAppid="wxd8980e77d335c871"; //小程序的appsecret(在微信小程序管理后台获取) StringwxspSecret="85d29ab4fa8c797423f2d7da5dd514cf"; //授权(必填) Stringgrant_type="authorization_code"; ////////////////1、向微信服务器使用登录凭证code获取session_key和openid//////////////// //请求参数 Stringparams="appid="+wxspAppid+"&secret="+wxspSecret+"&js_code="+code+"&grant_type="+grant_type; //发送请求 Stringsr=HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session",params); //解析相应内容(转换成json对象) JSONObjectjson=JSONObject.fromObject(sr); //获取会话密钥(session_key) Stringsession_key=json.get("session_key").toString(); //用户的唯一标识(openid) Stringopenid=(String)json.get("openid"); ////////////////2、对encryptedData加密数据进行AES解密//////////////// try{ Stringresult=AesCbcUtil.decrypt(encryptedData,session_key,iv,"UTF-8"); if(null!=result&&result.length()>0){ map.put("status",1); map.put("msg","解密成功"); JSONObjectuserInfoJSON=JSONObject.fromObject(result); MapuserInfo=newHashMap(); 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); returnmap; } }catch(Exceptione){ e.printStackTrace(); } map.put("status",0); map.put("msg","解密失败"); returnmap; }
解密工具类AesCbcUtil
importorg.apache.commons.codec.binary.Base64; importorg.bouncycastle.jce.provider.BouncyCastleProvider; importjavax.crypto.BadPaddingException; importjavax.crypto.Cipher; importjavax.crypto.IllegalBlockSizeException; importjavax.crypto.NoSuchPaddingException; importjavax.crypto.spec.IvParameterSpec; importjavax.crypto.spec.SecretKeySpec; importjava.io.UnsupportedEncodingException; importjava.security.*; importjava.security.spec.InvalidParameterSpecException; /** *Createdbylsh *AES-128-CBC加密方式 *注: *AES-128-CBC可以自己定义“密钥”和“偏移量“。 *AES-128是jdk自动生成的“密钥”。 */ publicclassAesCbcUtil{ static{ //BouncyCastle是一个开源的加解密解决方案,主页在http://www.bouncycastle.org/ Security.addProvider(newBouncyCastleProvider()); } /** *AES解密 * *@paramdata//密文,被加密的数据 *@paramkey//秘钥 *@paramiv//偏移量 *@paramencodingFormat//解密后的结果需要进行的编码 *@return *@throwsException */ publicstaticStringdecrypt(Stringdata,Stringkey,Stringiv,StringencodingFormat)throwsException{ //initialize(); //被加密的数据 byte[]dataByte=Base64.decodeBase64(data); //加密秘钥 byte[]keyByte=Base64.decodeBase64(key); //偏移量 byte[]ivByte=Base64.decodeBase64(iv); try{ Ciphercipher=Cipher.getInstance("AES/CBC/PKCS7Padding"); SecretKeySpecspec=newSecretKeySpec(keyByte,"AES"); AlgorithmParametersparameters=AlgorithmParameters.getInstance("AES"); parameters.init(newIvParameterSpec(ivByte)); cipher.init(Cipher.DECRYPT_MODE,spec,parameters);//初始化 byte[]resultByte=cipher.doFinal(dataByte); if(null!=resultByte&&resultByte.length>0){ Stringresult=newString(resultByte,encodingFormat); returnresult; } returnnull; }catch(NoSuchAlgorithmExceptione){ e.printStackTrace(); }catch(NoSuchPaddingExceptione){ e.printStackTrace(); }catch(InvalidParameterSpecExceptione){ e.printStackTrace(); }catch(InvalidKeyExceptione){ e.printStackTrace(); }catch(InvalidAlgorithmParameterExceptione){ e.printStackTrace(); }catch(IllegalBlockSizeExceptione){ e.printStackTrace(); }catch(BadPaddingExceptione){ e.printStackTrace(); }catch(UnsupportedEncodingExceptione){ e.printStackTrace(); } returnnull; } }
发送请求的工具类HttpRequest
importjava.io.BufferedReader; importjava.io.IOException; importjava.io.InputStreamReader; importjava.io.PrintWriter; importjava.net.URL; importjava.net.URLConnection; importjava.util.List; importjava.util.Map; /** *Createdbylshon2017/6/22. */ publicclassHttpRequest{ /** *向指定URL发送GET方法的请求 * *@paramurl *发送请求的URL *@paramparam *请求参数,请求参数应该是name1=value1&name2=value2的形式。 *@returnURL所代表远程资源的响应结果 */ publicstaticStringsendGet(Stringurl,Stringparam){ Stringresult=""; BufferedReaderin=null; try{ StringurlNameString=url+"?"+param; URLrealUrl=newURL(urlNameString); //打开和URL之间的连接 URLConnectionconnection=realUrl.openConnection(); //设置通用的请求属性 connection.setRequestProperty("accept","*/*"); connection.setRequestProperty("connection","Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0(compatible;MSIE6.0;WindowsNT5.1;SV1)"); //建立实际的连接 connection.connect(); //获取所有响应头字段 Map>map=connection.getHeaderFields(); //遍历所有的响应头字段 for(Stringkey:map.keySet()){ System.out.println(key+"--->"+map.get(key)); } //定义BufferedReader输入流来读取URL的响应 in=newBufferedReader(newInputStreamReader( connection.getInputStream())); Stringline; while((line=in.readLine())!=null){ result+=line; } }catch(Exceptione){ System.out.println("发送GET请求出现异常!"+e); e.printStackTrace(); } //使用finally块来关闭输入流 finally{ try{ if(in!=null){ in.close(); } }catch(Exceptione2){ e2.printStackTrace(); } } returnresult; } /** *向指定URL发送POST方法的请求 * *@paramurl *发送请求的URL *@paramparam *请求参数,请求参数应该是name1=value1&name2=value2的形式。 *@return所代表远程资源的响应结果 */ publicstaticStringsendPost(Stringurl,Stringparam){ PrintWriterout=null; BufferedReaderin=null; Stringresult=""; try{ URLrealUrl=newURL(url); //打开和URL之间的连接 URLConnectionconn=realUrl.openConnection(); //设置通用的请求属性 conn.setRequestProperty("accept","*/*"); conn.setRequestProperty("connection","Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0(compatible;MSIE6.0;WindowsNT5.1;SV1)"); //发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); //获取URLConnection对象对应的输出流 out=newPrintWriter(conn.getOutputStream()); //发送请求参数 out.print(param); //flush输出流的缓冲 out.flush(); //定义BufferedReader输入流来读取URL的响应 in=newBufferedReader( newInputStreamReader(conn.getInputStream())); Stringline; while((line=in.readLine())!=null){ result+=line; } }catch(Exceptione){ System.out.println("发送POST请求出现异常!"+e); e.printStackTrace(); } //使用finally块来关闭输出流、输入流 finally{ try{ if(out!=null){ out.close(); } if(in!=null){ in.close(); } } catch(IOExceptionex){ ex.printStackTrace(); } } returnresult; } }
另外由于需求使用解密的工具类所有要在pom文件加上这个依赖
org.bouncycastle bcprov-ext-jdk16 1.46 jar compile
这样才能引入bcprov这个jar包。网上参考了一下,个人感觉加这个依赖是最容易解决问题的。
最近打算弄个关于微信运动的小程序,解密这块估计也要用到。大家有疑问可以一起留言交流
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!