如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
关于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。
微信公众平台OAuth2.0授权
大体流程如下图:
用户请求
用户首先请求第三方网页
第三方服务器处理
第三方服务器获取用户请求后会进行判断,是否需要获取code(正常请求肯定是需要code的,这里我们可以参考一下官方JsApiPay的mode,来看获取openid的大体流程)
1 __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 //取出openid114 $data = json_decode($res,true);115 $this->data = $data;116 $openid = $data['openid'];117 return $openid;118 }119 120 /**121 * 122 * 拼接签名字符串123 * @param array $urlObj124 * 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 返回构造好的url179 */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,微信跳转带回的code195 * 196 * @return 请求的url197 */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 }
授权
如果需要获取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时的授权页面:
用户同意授权
用户同意授权将信息发送到微信公众平台,微信公众平台会内部进行授权验证
用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
code说明 : code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,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"}
判断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参数 |
拉取用户信息
官方文档有说明,需要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/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/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 "}
其他一些细节性的问题请参考官方文档: