|
微信的授权登录和QQ、新浪等平台的授权登录都大同小异,均采用OauthOAuth2.0鉴权方式。
微信授权分为两种:
1、静默授权
2、弹窗授权,需要用户手动同意
两种scope的区别说明
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。 用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
具体而言,网页授权流程分为四步:
1、引导用户进入授权页面同意授权,获取code
2、通过code换取网页授权access_token(与基础支持中的access_token不同)
3、如果需要,开发者可以刷新网页授权access_token,避免过期
4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制) 以下是封装的微信操作类,需要用到两个数据表,用于保存access_token、ticket,由于他们具有一定有效期,且每天请求数有上限,所以开发者需自行保存,以下是代码:
<?php
/**
* 微信操作表
* wxtoken 表结构
* id
* access_token
* addtime
* wxticket 表结构
* id
* ticket
* addtime
*/
class WX {
private $appid;
private $appserect;
private $curl;
private $msg;
protected $errs = array(
&#39;-1&#39; => &#39;系统繁忙,此时请开发者稍候再试&#39;,
&#39;0&#39; => &#39;请求成功&#39;,
&#39;40001&#39; => &#39;AppSecret错误或者AppSecret不属于这个公众号,请开发者确认AppSecret的正确性&#39;,
&#39;40002&#39; => &#39;请确保grant_type字段值为client_credential&#39;,
&#39;40164&#39; => &#39;调用接口的IP地址不在白名单中,请在接口IP白名单中进行设置。&#39;,
);
function __construct($appid, $appserect) {
$this->appid = $appid;
$this->appserect = $appserect;
$this->curl = new Curl();
}
/*
微信网页授权登录 需要在公众号设置 - 功能设置 - 网页授权域名
第一步:用户同意授权,获取code
scope : snsapi_base 只能获取openid 直接跳转
snsapi_userinfo
*/
public function getCode($redirect_uri, $scope = &#39;snsapi_userinfo&#39;,$state = &#39;1&#39;) {
$url = &#34;https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appid}&redirect_uri={$redirect_uri}&response_type=code&scope={$scope}&state={$state}#wechat_redirect&#34;;
header(&#34;Location:{$url}&#34;);
exit;
}
/*
第二步:通过code换取网页授权access_token
*/
public function getAccessTokenByCode($code) {
$url = &#34;https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appid}&secret={$this->appserect}&code={$code}&grant_type=authorization_code&#34;;
// exit($url);
// $curl = new Curl();
$result = $this->curl->doGet($url);
if (!$result) {
// $this->curl->getError()
$this->msg = &#34;获取token失败&#34;;
return false;
}
$result = json_decode($result, true);
if ($result[&#39;errcode&#39;]) {
$this->msg = $result[&#39;errmsg&#39;];
return false;
}
return $result;
}
// 第三步:刷新access_token(如果需要) 通过code 获取openid $type 0静默授权 1弹窗授权
public function getUserInfo($code, $type = 0, $lang = &#39;zh_CN &#39;) {
$result = $this->getAccessTokenByCode($code);
if (!$result) {
return false;
}
$member = C::t(PT_USER)->getByOpenid($result[&#39;openid&#39;]);
if ($member) {
return $member;
} else {
if ($type) {
$url = &#34;https://api.weixin.qq.com/sns/userinfo?access_token={$result[&#39;access_token&#39;]}&openid={$result[&#39;openid&#39;]}&lang={$lang}&#34;;
// $return = $this->curl->doGet($url);
// 这接口有病 强制显示文件头
$return = file_get_contents($url);
if (!$return) {
$this->msg = &#39;获取用户信息失败&#39;;
return false;
}
$return = json_decode($return, true);
if (!$return) {
$this->msg = &#39;获取用户信息返回失败&#39;;
return false;
}
// file_put_contents(&#39;ccc.txt&#39;,print_r($return,true),FILE_APPEND);
$data = array(
&#39;openid&#39; => $return[&#39;openid&#39;],
&#39;name&#39; => $return[&#39;nickname&#39;],
&#39;sex&#39; => $return[&#39;sex&#39;],
&#39;province&#39; => $return[&#39;province&#39;],
&#39;city&#39; => $return[&#39;city&#39;],
&#39;country&#39; => $return[&#39;country&#39;],
&#39;img&#39; => $return[&#39;headimgurl&#39;],
&#39;bindtel&#39; => 0,
);
} else {
$data = array(
&#39;openid&#39; => $result[&#39;openid&#39;],
&#39;username&#39; => &#34;微信用户_&#34; . random(6,1)
);
}
$name = rand(100000, 1000000000);
$e = $name . &#34;@qq.com&#34;;
$password = $e;
$id = UserAddEdit(0, $data[&#39;username&#39;], $password, $e,10,0,&#34;&#34;, $msg);
if ($id <= 0) {
$this->msg = $msg;
return false;
}
C::t(PT_USER)->update($data, $id);
$member = C::t(PT_USER)->get($id);
return $member;
}
}
/*
公众号 安全中心 设置IP白名单
公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
*/
public function getAccessToken($type) {
$addtime = TIMESTAMP - 7200;
$url = &#34;https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->appserect}&#34;;
$row = C::t(PT_WXTOKEN)->getNew($addtime, $type);
if ($row) {
return $row[&#39;access_token&#39;];
} else {
$result = $this->curl->doGet($url);
if (!$result) {
$this->msg = &#34;无法获取令牌内容&#34;;
return false;
}
$result = json_decode($result, true);
if (!$result) {
$this->msg = &#34;解析令牌内容失败&#34;;
return false;
}
if ($result[&#39;access_token&#39;]) {
C::t(PT_WXTOKEN)->addToken($result[&#39;access_token&#39;], $type);
return $result[&#39;access_token&#39;];
} else {
$this->msg = &#34;获取令牌失败&#34;;
return false;
}
}
}
// 获取js票据 需要在公众号设置 - 功能设置 - JS接口安全域名设置
public function getJsTicket() {
$addtime = TIMESTAMP - 7200;
$row = C::t(PT_WXTICKET)->getNew($addtime);
if ($row) {
return $row[&#39;ticket&#39;];
} else {
$token = $this->getAccessToken();
if (!$token) {
return false;
}
$url = &#34;https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$token}&type=jsapi&#34;;
$result = $this->curl->doGet($url);
if (!$result) {
$this->msg = &#34;无法获取js票据&#34;;
return false;
}
$result = json_decode($result, true);
if (!$result) {
$this->msg = &#34;解析js票据内容失败&#34;;
return false;
}
if ($result[&#39;ticket&#39;]) {
C::t(PT_WXTICKET)->addTicket($result[&#39;ticket&#39;]);
return $result[&#39;ticket&#39;];
} else {
$this->msg = &#34;获取js票据失败&#34;;
return false;
}
}
}
// js sdk 票据签名 当前网页的URL,不包含#及其后面部分
public function jsSign($data) {
// 1.所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)
ksort($data);
// 2.URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1 采用原始值,不进行URL 转义
$string1 = $this->ToUrlParams($data);
// echo &#34;string1:{$string1}<br/>&#34;;
// 3.对string1做sha1加密
$sign = sha1($string1);
// echo &#34;signature:{$sign}<br/>&#34;;
return $sign;
}
// 获取消息内容
public function getMsg() {
return $this->msg;
}
/**
* 格式化参数格式化成url参数
*/
public function ToUrlParams($data) {
$buff = &#34;&#34;;
foreach ($data as $k => $v) {
if ($k != &#34;sign&#34; && $v != &#34;&#34; && !is_array($v)) {
$buff .= $k . &#34;=&#34; . $v . &#34;&&#34;;
}
}
$buff = trim($buff, &#34;&&#34;);
return $buff;
}
}
?>业务代码:
// 微信登录
function wxlogin() {
global $_G,$identifier,$config,$wx;
if (!$_G[&#39;uid&#39;]) {
if ($_GET[&#39;state&#39;]) {
//回调
$member = $wx->getUserInfo($_GET[&#39;code&#39;]);
if (!$member) {
exit($wx->getMsg());
}
if (!function_exists(&#34;setloginstatus&#34;)) {
include_once libfile(&#39;function/member&#39;);
}
// 设置登录状态$wx
setloginstatus($member, 2592000);
checkfollowfeed();
$_G[&#39;uid&#39;] = $member[&#39;uid&#39;];
$_G[&#39;member&#39;] = $member;
} else {
//请求授权 对参数编码
$redirect = urlencode(getProtocol() . $_SERVER[&#39;HTTP_HOST&#39;] . $_SERVER[&#39;REQUEST_URI&#39;]);
$wx->getCode($redirect, &#39;snsapi_base&#39;);
}
}
}
function getProtocol() {
return is_HTTPS() ? &#39;https://&#39; : &#39;http://&#39;;
}
function is_HTTPS() { if ($_SERVER[&#39;HTTPS&#39;] === 1 || $_SERVER[&#39;HTTPS&#39;] === &#39;on&#39; || $_SERVER[&#39;SERVER_PORT&#39;] == 443) {
return true;
}
return false;
} |
|