第十一节:OpenId、UnionId、Session_key

同一个微信开放平台下的相同主体的App、公众号、小程序的unionid是相同的,这样可判断是否同一用户
微信针对不同的用户在不同的应用下都有唯一的一个openId
每个用户对每个公众号的OpenID是唯一的,对于不同公众号,同一用户的openid不同
?例如:
????同一用户在移动应用(网页web授权),APP,公众号,小程序,商户等授权或关注后,openid是不同的
? ? 因此获取在每种应用底下获取到openid的url地址和请求参考也是不一样的
? ? 应用名称? ? ? ? ?URL? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 参数
? ? 小程序? ? ? ? ? ? ?https://api.weixin.qq.com/sns/jscode2session? ? ? ? ? ? ? ? ? appid、
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?secret、
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?js_code=WebAuthCode、
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? grant_type=authorization_code
? ? 微信网页 ? ? ? ? https://api.weixin.qq.com/sns/oauth2/access_token? ? ??appid、
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?secret、
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?code=WebAuthCode、
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?grant_type=authorization_code
????注意:
? ? ? ? 1.这里得到的返回结果:除了openid,还有access_token
? ? ? ? 2.除了小程序外,其它应用开发(如:公众号、web应用、商户)获取openid的url地址是一?样,只是appid和secret参数不同,如果是公众号获取openid,则参数填写公众号的openid和secret,同理商户和其它应用,appid和secret不同也就决定了在这个应用下的用户对应的openid是不同的。
? ? 东湖网上支付,采用了两种支付方式:H5支付和公众号支付,都是采用“微信网页”的同一URL来获取webauthcode来交换openid。这里面涉及到3个openid(用户关注的公众号openid,微信网页移动应用openid,商户openid),那不管是哪种支付,获取的Openid都是由商户的appid和secret参数来获得这个用户在商户应用中的openid。问题来了,参见下面的"问题二"。
问题一:如果我们项目中有了个UserId,要能关联同一微信平台下的全部应用(移动应用、网站应用、公众帐号、小程序),怎么关联呢?
问题二:在公众号内移动应用进行H5支付,怎么确定是同一个用户呢?
open.weixin.qq.com的unionid机制。开放平台和UnionId机制参考说明
使用unionid机制前提open.weixin.qq.com中注册为开发者认证,绑定开发者账号下的所有应用
如果不需要确定区分每种应用中的用户是不是同一用户,则不用理会unionid机制。?
小程序中获取openId方法:
? ? 1.调用wx.login的时候会返回一个web authorize code
? ? 2.通过这个web authorize code去微信后台获取
? ? ? ? 后台地址:https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
? ??????传入appid,secret,JSCODE(web authorize code)去换取到openid,session_key以及unionid等信息
? ? ? ? 方式一:直接在小程序中调用(问题:https://api.weixin.qq.com 不在以下 request 合法域名列表中)
? ? ? ? ? ? 在调试阶段,我们可以在微信开发者工具中禁用这个警告,考虑到小程序的发布版本,
? ? ? ? ? ? 小程序中我们要设置自己的后台合法域名,又不能同时设置两个request合法域名? ? ? ? ? ??

小程序后台设置后台合法域名

? ? ? ? 方式二:通过我们自己的后台去微信平台URL获取
? ? ? ? ????小程序获取到web authorize code传入到我们自己的后台去获取openId
? ? ? ? ? ? 官方:
? ? ? ? ? ? ? ? 1.小程序调用wx.login,wx.getUserInfo获取临时登录凭证code,并回传到开发者服务器
? ? ? ? ? ? ? ? 2.开发者服务器以code换取用户唯一标识openid会话密钥session_key
? ??????????????之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。具体实现流程如下:? ? ?

安全使用openid和session_key

? ? 官方提醒
????????//正常返回的JSON数据包
????????{"openid":"OPENID","session_key":"SESSIONKEY",}
????????//满足UnionID返回条件时,返回的JSON数据包?
? ? ? ? //满足前提open.weixin.qq.com中注册为开发者认证,绑定开发者账号下的所有应用? ? ??
????????{"openid":"OPENID","session_key":"SESSIONKEY","unionid":"UNIONID"}
????????//错误时返回JSON数据包(示例为Code无效)
????????{"errcode":40029,"errmsg":"invalid code"}? ??
? ? 3.获取加密信息
? ? 如果只是拿小程序的openid和unionid,则不用考虑这步,
????因为上面第2步就可拿到openid和unionid(满足unionid机制)

????在调用 wx.login,wx.getUserInfo都会返回一个对象,这个对象有一个属性叫encryptedData? ? ? ? ? ??

得到的encryptedData

这个字段的数据是加密的,所以我们可以通过解密的方式来得到原文信息,解密后为json,结构如下:
? ?????{"openId":"OPENID","nickName":"NICKNAME","gender": GENDER,"city":"CITY","province":"PROVINCE","country":"COUNTRY","avatarUrl":"AVATARURL","unionId":"UNIONID","watermark": {"appid":"APPID","timestamp":TIMESTAMP }}
??新的数据解密方法?
? ??????总的来说还是原来的算法,还是原来的逻辑结构,不同的是解密方式,
????????以前通过session_key得到iv,新方法是直接从前台接口处得到iv来解密,所改变的也只是传输的数据
? ? ? ? 1.对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
? ? ? ? 2.对称解密的目标密文为 Base64_Decode(encryptedData)。
? ? ? ? 3.对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
? ? ? ? 4.对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。
详细流程:
1.小程序客户端调用wx.login,回调里面包含js_code。
2.然后将js_code发送到服务器A(开发者服务器),服务器A向微信服务器发起请求附带js_code、appId、secretkey和grant_type参数,以换取用户的openid和session_key(会话密钥)。
3.服务器A拿到session_key后,生成一个随机数我们叫3rd_session,以3rdSessionId为key,以session_key + openid为value缓存到redis或memcached中;因为微信团队不建议直接将session_key在网络上传输,由开发者自行生成唯一键与session_key关联。其作用是:
????将3rdSessionId返回给客户端,维护小程序登录态。
????通过3rdSessionId找到用户session_key和openid。
4.客户端拿到3rdSessionId后缓存到storage,
5.通过wx.getUserIinfo可以获取到用户敏感数据encryptedData 。
6.客户端将encryptedData、3rdSessionId和偏移量一起发送到服务器A
7.服务器A根据3rdSessionId从缓存中获取session_key
8.在服务器A使用AES解密encryptedData,从而实现用户敏感数据解密
重点在6、7、8三个环节。
服务器端解密代码示例
package com.iups.wx.wxservice;



importjava.io.UnsupportedEncodingException;

import java.security.AlgorithmParameters;

importjava.security.InvalidAlgorithmParameterException;

import java.security.InvalidKeyException;

importjava.security.NoSuchAlgorithmException;

importjava.security.NoSuchProviderException;

import java.security.Security;

importjava.security.spec.InvalidParameterSpecException;

import java.util.HashMap;

import java.util.Map;


import javax.crypto.BadPaddingException;

import javax.crypto.Cipher;

importjavax.crypto.IllegalBlockSizeException;

import javax.crypto.NoSuchPaddingException;

import javax.crypto.spec.IvParameterSpec;

import javax.crypto.spec.SecretKeySpec;


import net.sf.json.JSONObject;


import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.bouncycastle.util.Arrays;

import org.codehaus.xfire.util.Base64;


importcom.iups.wx.common.RemoteInterfaceAddress;

import com.iups.wx.util.HttpsClientUtil;


/**

?*微信小程序信息获取

?*@author Administrator

?*@Date 2017年2月16日11:56:08

?*/

public class WXAppletUserInfo {



???/**

????*获取微信小程序 session_key 和openid

????* @param code

????* @return

????*/

???public JSONObject getSessionKeyOropenid(String code){

???????//微信端登录code值

???????String wxCode = code;

???????String requestUrl = RemoteInterfaceAddress.GETSESSIONKEYOROPENID;

???????Map requestUrlParam = newHashMap();

???????requestUrlParam.put("appid", RemoteInterfaceAddress.AppletAPPID);

???????requestUrlParam.put("secret",RemoteInterfaceAddress.AppletAppSecret);

???????requestUrlParam.put("js_code", wxCode);

???????requestUrlParam.put("grant_type","authorization_code");

???????JSONObject jsonObject = HttpsClientUtil.getInstance().sendGetRequest(requestUrl,requestUrlParam);

???????return jsonObject;

??? }


???/**

????*解密用户敏感数据获取用户信息

????* @param sessionKey数据进行加密签名的密钥

????* @param encryptedData包括敏感数据在内的完整用户信息的加密数据

????* @param iv加密算法的初始向量

????* @return

????*/

???public JSONObject getUserInfo(String encryptedData,StringsessionKey,String iv){

???????//被加密的数据

???????byte[] dataByte = Base64.decode(encryptedData);

???????//加密秘钥

???????byte[] keyByte = Base64.decode(sessionKey);

???????//偏移量

???????byte[] ivByte = Base64.decode(iv);


???????try {

?????????????? //如果密钥不足16位,那么就补足.? 这个if 中的内容很重要

???????????int base = 16;

???????????if (keyByte.length % base != 0) {

??????????????? int groups = keyByte.length /base + (keyByte.length % base != 0 ? 1 : 0);

??????????????? byte[] temp = new byte[groups *base];

??????????????? Arrays.fill(temp, (byte) 0);

??????????????? System.arraycopy(keyByte, 0,temp, 0, keyByte.length);

??????????????? keyByte = temp;

???????????}

???????????//初始化

???????????Security.addProvider(newBouncyCastleProvider());

???????????Cipher cipher =Cipher.getInstance("AES/CBC/PKCS7Padding","BC");

???????????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 = newString(resultByte, "UTF-8");

??????????????? returnJSONObject.fromObject(result);

???????????}

???????} catch (NoSuchAlgorithmException e) {

???????????e.printStackTrace();

???????} catch (NoSuchPaddingException e) {

???????????e.printStackTrace();

???????} catch (InvalidParameterSpecException e) {

???????????e.printStackTrace();

???????} catch (IllegalBlockSizeException e) {

???????????e.printStackTrace();

???????} catch (BadPaddingException e) {

???????????e.printStackTrace();

???????} catch (UnsupportedEncodingException e) {

???????????e.printStackTrace();

???????} catch (InvalidKeyException e) {

???????????e.printStackTrace();

???????} catch (InvalidAlgorithmParameterException e) {

???????????e.printStackTrace();

???????} catch (NoSuchProviderException e) {

???????????e.printStackTrace();

???????}

???????return null;

??? }


}

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容