摘要:第三方登陆的整体思路是获取第三方中的,然后与用户关联存到数据库,进行登陆。用于第三方应用防止攻击,成功授权后回调时会原样带回。
第三方登陆的整体思路是获取第三方中的openid,然后与用户关联(存到数据库),进行登陆。
现在需求是:两个(或多个)一级域名,如maxiye.cn和yexima.com,同时每个域名下有多个二级域名分布,如:app.maxiye.cn,new.maxiye.cn,old.maxiye.cn,app.yexima.com,new.yexima.com,old.yexima.com...等,但是这些域名下使用了同一份代码(对,就是马甲),共享数据库和session。同时旗下每个域名均可能包含pc,ios,Android端,如何全部接入第三方登陆?
qq,微信,微博接入要点:
1.申请入口:QQ是QQ互联,微信是微信开放平台,微博是微博开放平台;
2.回调域设置:QQ可以设置一级域名且可以有多个,必须http://开头,;结束,如http://maxiye.cn;http://yexima.com;;微信只能设置到二级域名且只能一个,格式为:app.maxiye.cn;微博也可以设置一级域名且是一个:maxiye.cn;
注:由于QQ互联开启了回调地址强校验,现在必须写完整的回调路径,多个以“;”分隔,如https://account.maxiye.cn/connect/callback/qq;https://account.yexima.com/connect/qc。(2018.3.26)
3.unionid获取:多个app(同一开发者账号下)共享数据库,故需要使用unionid确认身份。QQ获取unionid需要额外申请权限,具体参考相同开发者账号下的不同appid应用如何打通;微信已有unionid获取接口;微博中的uid即unionid;
4.本地也可以调试:比如要本地调试app.maxiye.cn的接入,只需要配置本地域名为local.maxiye.cn(local可以改,但一级域名不能动,适用于QQ,微博;微信则必须是回调设置好的二级域名,可以先把回调地址设置为local.maxiye.cn,本地调试好了,再改),就可以正常接收第三方的回调了;
坑:
1.QQ接口返回的结果好多是jsonp格式,需要手动剥离callback();
2.微博获取access_token竟然必须使用post,惊了;
3.微信不支持回调地址填写一级域名,所以需要使用统一域名(代理)作为回调地址,然后内部再重定向到发申请的域名;
下边是一个写好的工具类:
[ "qq" => [ "app_id" => "100000000", "app_secret" => "f9038c3d07c*******7884edf3e31708", ], "weixin" => [ "app_id" => "wxee7c90a7744c2002", "app_secret" => "13e649627894*******7a85a0e2f50e7", ], "weibo" => [ "app_id" => "1200000000", "app_secret" => "e074de8*******d3818d0df9ca28c459", ], ], "yexima.com" => [ "qq" => [ "app_id" => "101111244", "app_secret" => "6ca59c6a1b1*******77e636a10ac334", ], "weixin" => [ "app_id" => "wx0b822222ea9ee323", "app_secret" => "7f9cbd*******f37ce7b4c267bdde029", ], "weibo" => [ "app_id" => "911111998", "app_secret" => "5b21c452f88e2982*******1722d8fcd", ], ], ]; function __construct($params = []) { $this->type = $params["type"]; $this->server = $_SERVER["SERVER_NAME"]; foreach ($this->config as $k => $v) { if (stristr($this->server, $k) && isset($v[$this->type])) { $this->app_id = $v[$this->type]["app_id"]; $this->app_secret = $v[$this->type]["app_secret"]; } } if (isset($params["code"])) { $this->code = $params["code"]; } } /** * 获取用户授权验证的链接 * @return string */ public function getOauthUrl() { $this->state = md5(uniqid(rand(), TRUE)); Yii::$app->session->setFlash("oauth_state", $this->state); $redirect_uri = urlencode(Url::to(["login-by-openid", "type" => $this->type, true)); if ($this->type == "weixin" && $this->server != $this->proxy) {//微信回调多域名代理处理 $redirect_uri = str_replace($this->server, $this->proxy, $redirect_uri) . "%26redirect%3D" . $this->server; } $url = ""; switch ($this->type) { case "qq"://qq回调域填写一级域名并以“;”结束:http://maxiye.cn;http://yexima.com; $url = "https://graph.qq.com/oauth/show?which=Login&display=pc&response_type=code&client_id={$this->app_id}&state={$this->state}&display=web&redirect_uri={$redirect_uri}"; break; case "weixin"://app.maxiye.cn不支持只填写一级域名 $url = "https://open.weixin.qq.com/connect/qrconnect?response_type=code&appid={$this->app_id}&state={$this->state}&scope=snsapi_login&redirect_uri={$redirect_uri}#wechat_redirect"; break; case "weibo"://微博设置安全域名:maxiye.cn $url = "https://api.weibo.com/oauth2/authorize?response_type=code&client_id={$this->app_id}&state={$this->state}&display=web&redirect_uri={$redirect_uri}"; break; default: break; } return $url; } /** * 获取针对开发者账号的惟一uuid * @return string unionid或uid */ public function getUuid() { $openid = ""; if ($this->type == "qq") { $this->getAccessToken(); } else { $openid = $this->getOpenid(); } $access_token = $this->access_token; $uuid = ""; if ($access_token) { switch ($this->type) { case "qq": $url = "https://graph.qq.com/oauth2.0/me?access_token={$access_token}&unionid=1"; // 返回示例... /*callback({ "client_id":"YOUR_APPID", "openid":"YOUR_OPENID", "unionid":"YOUR_UNIONID" });*/ $result = $this->get_contents($url); if (strpos($result, "callback") !== false) { $lpos = strpos($result, "("); $rpos = strrpos($result, ")"); $result = json_decode(substr($result, $lpos + 1, $rpos - $lpos - 1), true); $uuid = isset($result["unionid"]) ? $result["unionid"] : ""; } return $uuid; // return $openid; break; case "weixin": $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$access_token}&openid={$openid}"; // 返回示例 /*{ "openid":"OPENID", "nickname":"NICKNAME", "sex":1, "province":"PROVINCE", "city":"CITY", "country":"COUNTRY", "headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0", "privilege":[ "PRIVILEGE1", "PRIVILEGE2" ], "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL" }*/ $result = json_decode($this->get_contents($url), true); return isset($result["unionid"]) ? $result["unionid"] : ""; break; case "weibo": return $openid; break; default: break; } } return $uuid; } /** * 获取access_token * @param boolean $true false表示获取原始结果,true获取真正的access_token * @return string json包|string */ public function getAccessToken($true = true) { //验证state if (Yii::$app->request->get("state", "") != Yii::$app->session->getFlash("oauth_state")) { return ""; } $redirect_uri = urlencode(Url::to(["login-by-openid", "type" => $this->type], true)); $url = ""; switch ($this->type) { case "qq": $url = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&code={$this->code}&client_id={$this->app_id}&client_secret={$this->app_secret}&redirect_uri={$redirect_uri}"; //返回示例... //access_token=15C0CE01C0311240F9091A7DB6828E62&expires_in=7776000&refresh_token=7BFCE2E5B773D4F5531561A10E1C2B2D break; case "weixin": $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->app_id}&secret={$this->app_secret}&code={$this->code}&grant_type=authorization_code"; //返回示例 /*{ "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE" }*/ break; case "weibo": $url = "https://api.weibo.com/oauth2/access_token?client_id={$this->app_id}&client_secret={$this->app_secret}&grant_type=authorization_code&redirect_uri={$redirect_uri}&code={$this->code}";//新浪微博 $post_data = []; return $this->post($url, $post_data);//撒币制杖 //返回示例 /*{ "access_token": "SlAV32hkKG", "remind_in": 3600, "expires_in": 3600, "uid":"12341234" }*/ break; default: break; } if ($true) { $res_access_token = $this->get_contents($url); if ($this->type == "qq" && strpos($res_access_token, "access_token") !== false) { $token_result = ["access_token" => explode("=", explode("&", $res_access_token)[0])[1]]; } else { $token_result = json_decode($res_access_token ?: "{}", true); } $access_token = !empty($token_result["access_token"]) ? $token_result["access_token"] : ""; $this->access_token = $access_token; return $access_token; } else { return $this->get_contents($url); } } /** * post * post方式请求资源 * @param string $url 基于的baseUrl * @param array $keysArr 请求的参数列表 * @param int $flag 标志位 * @return string 返回的资源内容 */ public function post($url, $keysArr, $flag = 0) { $ch = curl_init(); if (!$flag) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $keysArr); curl_setopt($ch, CURLOPT_URL, $url); $ret = curl_exec($ch); curl_close($ch); return $ret; } /** * get_contents * 服务器通过get请求获得内容 * @param string $url 请求的url,拼接后的 * @return string 请求返回的内容 */ public function get_contents($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($ch, CURLOPT_URL, $url); $response = curl_exec($ch); curl_close($ch); //-------请求为空 if (empty($response)) { return "{}"; } return $response; } /** * 获取openid * @return string openid */ public function getOpenid() { $res_access_token = $this->getAccessToken(false); if ($this->type == "qq" && strpos($res_access_token, "access_token") !== false) { $access_token = ["access_token" => explode("=", explode("&", $res_access_token)[0])[1]]; } else { $access_token = json_decode($res_access_token ?: "{}", true); } $openid = ""; if (isset($access_token["access_token"])) { $this->access_token = $access_token["access_token"]; switch ($this->type) { case "qq": $url = "https://graph.qq.com/oauth2.0/me?access_token={$access_token["access_token"]}"; // 返回示例... // callback( {"client_id":"101406183","openid":"6C611CBE0C72F765572AE2472C9B59A4"} ); $result = $this->get_contents($url); if (strpos($result, "callback") !== false) { $lpos = strpos($result, "("); $rpos = strrpos($result, ")"); $result = json_decode(substr($result, $lpos + 1, $rpos - $lpos - 1), true); $openid = isset($result["openid"]) ? $result["openid"] : ""; } break; case "weixin": return $access_token["openid"]; break; case "weibo": return $access_token["uid"]; /*$url = "https://api.weibo.com/oauth2/get_token_info?access_token={$access_token["access_token"]}"; //返回示例 { "uid": 1073880650, "appkey": 1352222456, "scope": null, "create_at": 1352267591, "expire_in": 157679471 } $result = $this->get_contents($url); $openid = isset($result["uid"])?$result["uid"]:"";*/ break; default: break; } } return $openid; } }
使用方法如下:
//第三方登录界面处理 public function actionLoginByOpenid(){ Yii::$app->response->format= Response::FORMAT_HTML; $params = Yii::$app->request->get(); $oauth = new OauthLogin($params); if(empty($params["code"])){ $url = $oauth->getOauthUrl(); return $this->redirect($url); }else{ //微信代理跳转处理 if(isset($params["redirect"]) && $params["redirect"] != $oauth->server){ $proxy = $oauth->proxy; $server = $params["redirect"]; return $this->redirect(str_replace($proxy, $server, Url::current(["redirect"=>null], "http"))); } $openid = $oauth->getUuid(); if (!$openid) { //失败处理TODO } else { //成功处理TODO } } }
具体流程(qq为例)如下:
1.用户点击QQ登陆图标,访问链接http://app.maxiye.cn/site/login-by-openid?type=qq;
2.服务器处理获取QQ的授权链接,重定向到https://graph.qq.com/oauth/show?which=Login&display=pc&response_type=code&client_id=1**83&state=acc19**b&display=web&redirect_uri=http%3A%2F%2Fapp.maxiye.cn%2Fsite%2Flogin-by-openid%3Ftype%3Dqq;
3.用户点击QQ头像或扫一扫授权通过,请求QQ服务器处理;
4.QQ在回调地址中添加code参数(Authorization Code),回调http://app.maxiye.cn/site/login-by-openid?type=qq&state=4a78***&code=1CA8DF***;
5.服务器验证state,根据code参数获取access_token,然后获取unionid(uid),处理结果。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/31983.html
摘要:近几日由于公司业务需要,要做一个网站的第三方登陆,并且登陆之后并进行验证,只有登陆过我们手机的第三方账号才可以登陆网站。只能百度,发现腾讯对于网站登陆和手机登陆拿到的是不一样的,如果要想一样得写申请信给,简直就是蛋疼。 近几日由于公司业务需要,要做一个网站的第三方登陆,并且登陆之后并进行验证,只有登陆过我们手机app的第三方账号才可以登陆网站。 这边先说下oauth2.0大概的流程吧...
摘要:微博登录是最常用的第三方账号登录之一。当前状态是未提交审核,待你开发完了上线后,提交微博审核,审核通过后即可正常使用。集成微博登录首先简介一下,第三方登录开发的流程。文件是首页,其实他的唯一作用就是产生一个微博登录跳转链接。 微博登录是最常用的第三方账号登录之一。由于其网站用户量大,可操作接口功能多,所以受到很多开发者的青睐。 既然是第三方,如果想使用它们的账号进行登录,那么第一步就应...
摘要:前言之前让网页公司制作新官网的时候规划有第三方账号的登录功能,但由于当时的一些开放平台申请步骤比较繁琐尤其是微信开放平台,所以一直拖延着,到了最近只能自己添加相关的功能。 前言 之前让网页公司制作新官网的时候规划有第三方账号的登录功能,但由于当时的一些开放平台申请步骤比较繁琐(尤其是微信开放平台),所以一直拖延着,到了最近只能自己添加相关的功能。 由于是刚接触Python和Django...
摘要:现在的网站基本都接入微信登陆了,就好像下面这样的。只需要用微信扫一下二维码,这个网站就可以展示你的微信昵称和头像,免去注册账号和输入密码登录的步骤,还免去设置头像和昵称的步骤,所以是挺方便的。 现在的网站基本都接入微信登陆了,就好像下面这样的。 showImg(https://segmentfault.com/img/bVbc4jV?w=523&h=508); 只需要用微信扫一下二维码...
阅读 3498·2023-04-25 15:52
阅读 580·2021-11-19 09:40
阅读 2570·2021-09-26 09:47
阅读 1022·2021-09-22 15:17
阅读 3547·2021-08-13 13:25
阅读 2198·2019-08-30 15:56
阅读 3459·2019-08-30 13:56
阅读 2094·2019-08-30 11:27