微信开发网页授权获取用户信息

如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

1. 关于OAuth2.0的说明

官方网站:http://oauth.net/   http://oauth.net/2/

权威定义:OAuth is An open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications. 

OAuth是一个开放协议,允许用户让第三方应用以安全且标准的方式获取该用户在某一网站、移动或桌面应用上存储的私密的资源(如用户个人信息、照片、视频、联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth 2.0是OAuth协议的下一版本,但不向后兼容OAuth 1.0。 OAuth 2.0关注客户端开发者的简易性,同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。

OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要分享他们的访问许可或他们数据的所有内容。

新浪微博API目前也使用OAuth 2.0。

2. 微信公众平台OAuth2.0授权

大体流程如下图:

 

2.1 用户请求

用户首先请求第三方网页

2.2 第三方服务器处理

第三方服务器获取用户请求后会进行判断,是否需要获取code(正常请求肯定是需要code的,这里我们可以参考一下官方JsApiPay的mode,来看获取openid的大体流程)

  1 <?php
  2 require_once "../lib/WxPay.Api.php";
  3 /**
  4  * 
  5  * JSAPI支付实现类
  6  * 该类实现了从微信公众平台获取code、通过code获取openid和access_token、
  7  * 生成jsapi支付js接口所需的参数、生成获取共享收货地址所需的参数
  8  * 
  9  * 该类是微信支付提供的样例程序,商户可根据自己的需求修改,或者使用lib中的api自行开发
 10  * 
 11  * @author widy
 12  *
 13  */
 14 class JsApiPay
 15 {
 16     /**
 17      * 
 18      * 网页授权接口微信服务器返回的数据,返回样例如下
 19      * {
 20      *  "access_token":"ACCESS_TOKEN",
 21      *  "expires_in":7200,
 22      *  "refresh_token":"REFRESH_TOKEN",
 23      *  "openid":"OPENID",
 24      *  "scope":"SCOPE",
 25      *  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
 26      * }
 27      * 其中access_token可用于获取共享收货地址
 28      * openid是微信支付jsapi支付接口必须的参数
 29      * @var array
 30      */
 31     public $data = null;
 32     
 33     /**
 34      * 
 35      * 通过跳转获取用户的openid,跳转流程如下:
 36      * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize
 37      * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code
 38      * 
 39      * @return 用户的openid
 40      */
 41     public function GetOpenid()
 42     {
 43         //通过code获得openid
 44         if (!isset($_GET['code'])){
 45             //触发微信返回code码
 46             $baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']);
 47             $url = $this->__CreateOauthUrlForCode($baseUrl);
 48             Header("Location: $url");
 49             exit();
 50         } else {
 51             //获取code码,以获取openid
 52             $code = $_GET['code'];
 53             $openid = $this->getOpenidFromMp($code);
 54             return $openid;
 55         }
 56     }
 57     
 58     /**
 59      * 
 60      * 获取jsapi支付的参数
 61      * @param array $UnifiedOrderResult 统一支付接口返回的数据
 62      * @throws WxPayException
 63      * 
 64      * @return json数据,可直接填入js函数作为参数
 65      */
 66     public function GetJsApiParameters($UnifiedOrderResult)
 67     {
 68         if(!array_key_exists("appid", $UnifiedOrderResult)
 69         || !array_key_exists("prepay_id", $UnifiedOrderResult)
 70         || $UnifiedOrderResult['prepay_id'] == "")
 71         {
 72             throw new WxPayException("参数错误");
 73         }
 74         $jsapi = new WxPayJsApiPay();
 75         $jsapi->SetAppid($UnifiedOrderResult["appid"]);
 76         $timeStamp = time();
 77         $jsapi->SetTimeStamp("$timeStamp");
 78         $jsapi->SetNonceStr(WxPayApi::getNonceStr());
 79         $jsapi->SetPackage("prepay_id=" . $UnifiedOrderResult['prepay_id']);
 80         $jsapi->SetSignType("MD5");
 81         $jsapi->SetPaySign($jsapi->MakeSign());
 82         $parameters = json_encode($jsapi->GetValues());
 83         return $parameters;
 84     }
 85     
 86     /**
 87      * 
 88      * 通过code从工作平台获取openid机器access_token
 89      * @param string $code 微信跳转回来带上的code
 90      * 
 91      * @return openid
 92      */
 93     public function GetOpenidFromMp($code)
 94     {
 95         $url = $this->__CreateOauthUrlForOpenid($code);
 96         //初始化curl
 97         $ch = curl_init();
 98         //设置超时
 99         curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
100         curl_setopt($ch, CURLOPT_URL, $url);
101         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,FALSE);
102         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,FALSE);
103         curl_setopt($ch, CURLOPT_HEADER, FALSE);
104         curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
105         if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0" 
106             && WxPayConfig::CURL_PROXY_PORT != 0){
107             curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
108             curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
109         }
110         //运行curl,结果以jason形式返回
111         $res = curl_exec($ch);
112         curl_close($ch);
113         //取出openid
114         $data = json_decode($res,true);
115         $this->data = $data;
116         $openid = $data['openid'];
117         return $openid;
118     }
119     
120     /**
121      * 
122      * 拼接签名字符串
123      * @param array $urlObj
124      * 
125      * @return 返回已经拼接好的字符串
126      */
127     private function ToUrlParams($urlObj)
128     {
129         $buff = "";
130         foreach ($urlObj as $k => $v)
131         {
132             if($k != "sign"){
133                 $buff .= $k . "=" . $v . "&";
134             }
135         }
136         
137         $buff = trim($buff, "&");
138         return $buff;
139     }
140     
141     /**
142      * 
143      * 获取地址js参数
144      * 
145      * @return 获取共享收货地址js函数需要的参数,json格式可以直接做参数使用
146      */
147     public function GetEditAddressParameters()
148     {    
149         $getData = $this->data;
150         $data = array();
151         $data["appid"] = WxPayConfig::APPID;
152         $data["url"] = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
153         $time = time();
154         $data["timestamp"] = "$time";
155         $data["noncestr"] = "1234568";
156         $data["accesstoken"] = $getData["access_token"];
157         ksort($data);
158         $params = $this->ToUrlParams($data);
159         $addrSign = sha1($params);
160         
161         $afterData = array(
162             "addrSign" => $addrSign,
163             "signType" => "sha1",
164             "scope" => "jsapi_address",
165             "appId" => WxPayConfig::APPID,
166             "timeStamp" => $data["timestamp"],
167             "nonceStr" => $data["noncestr"]
168         );
169         $parameters = json_encode($afterData);
170         return $parameters;
171     }
172     
173     /**
174      * 
175      * 构造获取code的url连接
176      * @param string $redirectUrl 微信服务器回跳的url,需要url编码
177      * 
178      * @return 返回构造好的url
179      */
180     private function __CreateOauthUrlForCode($redirectUrl)
181     {
182         $urlObj["appid"] = WxPayConfig::APPID;
183         $urlObj["redirect_uri"] = "$redirectUrl";
184         $urlObj["response_type"] = "code";
185         $urlObj["scope"] = "snsapi_base";
186         $urlObj["state"] = "STATE"."#wechat_redirect";
187         $bizString = $this->ToUrlParams($urlObj);
188         return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
189     }
190     
191     /**
192      * 
193      * 构造获取open和access_toke的url地址
194      * @param string $code,微信跳转带回的code
195      * 
196      * @return 请求的url
197      */
198     private function __CreateOauthUrlForOpenid($code)
199     {
200         $urlObj["appid"] = WxPayConfig::APPID;
201         $urlObj["secret"] = WxPayConfig::APPSECRET;
202         $urlObj["code"] = $code;
203         $urlObj["grant_type"] = "authorization_code";
204         $bizString = $this->ToUrlParams($urlObj);
205         return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
206     }
207 }

2.3 授权

如果需要获取code,第三方服务器会跳转到授权页面

在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面

请求方法如下:

 https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。  

参数说明:

参数  必须 说明
appid 公众号的唯一标识
redirect_uri 授权后重定向的回调链接地址
response_type 返回类型,请填写code
scope 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息
state 重定向后会带上state参数,开发者可以填写任意参数值
#wechat_redirect 直接在微信打开链接,可以不填此参数。做页面302重定向时候,必须带此参数

 

下图为scope等于snsapi_userinfo时的授权页面:

2.4 用户同意授权

用户同意授权将信息发送到微信公众平台,微信公众平台会内部进行授权验证

用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。

code说明 : code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。

2.5 获取网页授权

验证通过返回给第三方服务器code

通过code换取网页授权access_token

首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。

请求方法如下:

 access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code  

参数说明:

参数  是否必须 说明
appid 公众号的唯一标识
secret 公众号的appsecret
code 填写第一步获取的code参数
grant_type 填写为authorization_code

返回说明:

正确时返回的JSON数据包如下:

{ "access_token":"ACCESS_TOKEN",    

 "expires_in":7200,    

 "refresh_token":"REFRESH_TOKEN",    

 "openid":"OPENID",    

 "scope":"SCOPE" }

参数说明:

 参数 描述
access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
expires_in access_token接口调用凭证超时时间,单位(秒)
refresh_token 用户刷新access_token
openid 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
scope 用户授权的作用域,使用逗号(,)分隔

 

错误时微信会返回JSON数据包如下(示例为Code无效错误):

{"errcode":40029,"errmsg":"invalid code"}

2.6 判断access_token过期

如果access_token过期了需要重新刷取access_token(初次使用可以先忽略)

请求方法如下:

 https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN  

参数说明:

 参数 是否必须 说明
appid 公众号的唯一标识
grant_type 填写为refresh_token
refresh_token 填写通过access_token获取到的refresh_token参数

2.7 拉取用户信息

官方文档有说明,需要scope为snsapi_userinfo

请求方法如下:

 http:GET(请使用https协议) https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN  

参数说明:

参数 描述
access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
openid 用户的唯一标识

返回说明

正确时返回的JSON数据包如下:

{    "openid":" OPENID",  

 " nickname": NICKNAME,   

 "sex":"1",   

 "province":"PROVINCE"   

 "city":"CITY",   

 "country":"COUNTRY",    

 "headimgurl":    "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ

4eMsv84eavHiaiceqxibJxCfHe/46",  

"privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],    

 "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" 

}

参数说明:

 参数 描述
openid 用户的唯一标识
nickname 用户昵称
sex 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
province 用户个人资料填写的省份
city 普通用户个人资料填写的城市
country 国家,如中国为CN
headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
privilege 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)

 

错误时微信会返回JSON数据包如下(示例为openid无效):

{"errcode":40003,"errmsg":" invalid openid "}

其他一些细节性的问题请参考官方文档:https://mp.weixin.qq.com/wiki

猜你喜欢

转载自blog.csdn.net/weixin_36562804/article/details/80219980