逻辑总结
以fastadmin后台会员为例,缺少字段请自行在数据表中添加。
1.在后台添加会员,并修改user表,新增如下字段
字段名 类型 长度 小数点 不是null 键 注释
verification varchar 255 0 0 0 验证
wx_openid varchar 255 0 0 0 微信用户openid
wx_nickname varchar 255 0 0 0 微信用户名
wx_headimgurl varchar 255 0 0 0 微信用户头像
wx_state varchar 255 0 0 0 自定义微信标识
power_gzh mediumtext 0 0 0 0 公众号权限
2.添加会员
3.对指定账号进行微信扫码绑定用户openid,一定得是完整授权的opendi
4.扫描登录二维码获取用户信息修改登录状态
第一步,微信授权url生成二维码扫码获取用户code
/** * 登录-微信扫码 * @ApiReturn ({ "code": 0, "msg": "请求成功", "time": "1684717960", "data": "<img src=\"/static/login/qrcode/1684717960.8303.png\" alt=\"扫码登录\" style=\"width:240px\">" * }) */ public function ewmlogin(){ $state = md5(uniqid(rand(), true)); //用于前端登录状态判断 Session::set('state',$state); //构造微信授权登录的URL $authURL = 'https://open.weixin.qq.com/connect/oauth2/authorize'; $authURL .= '?appid=' . $this->wechatAppID; $authURL .= '&redirect_uri='.$this->wechatRedirectURL; $authURL .= '&response_type=code'; $authURL .= '&scope=snsapi_base'; $authURL .= '&state='.$state; $authURL .= '#wechat_redirect'; // return($authURL); //https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxe772ecaab712dd7f&redirect_uri=https://wechat.xuekao123.com/api/Wx_login/redirect.html&response_type=code&scope=snsapi_base&state=5#wechat_redirect //用url生成二维码保存至服务器,并将图片地址返回给前端进行渲染 $erweimaImg = $this->erweima($authURL); // $this->success("请求成功", $erweimaImg, 0); $this->success("请求成功", ['state'=>$state,"erweima"=>$erweimaImg] , 0); }
/** * 生成二维码 * @ApiInternal * @param string access_token * @param string openid * @return array */ function erweima($url = '') { require_once '../vendor/phpqrcode/phpqrcode.php'; $qrcode = new \QRcode(); $value = $url; //二维码内容 $errorCorrectionLevel = 'H'; //容错级别 $matrixPointSize = 6; //生成图片大小 //生成二维码图片 $filename = 'static/login/qrcode/' . microtime(true) . '.png'; $qrcode->png($value, $filename, $errorCorrectionLevel, $matrixPointSize, 2); $logo = 'static/image/logo.jpg'; //准备好的logo图片 $QR = $filename; //已经生成的原始二维码图 if (file_exists($logo)) { $QR = imagecreatefromstring(file_get_contents($QR)); //目标图象连接资源。 $logo = imagecreatefromstring(file_get_contents($logo)); //源图象连接资源。 $QR_width = imagesx($QR); //二维码图片宽度 $QR_height = imagesy($QR); //二维码图片高度 $logo_width = imagesx($logo); //logo图片宽度 $logo_height = imagesy($logo); //logo图片高度 $logo_qr_width = $QR_width / 4; //组合之后logo的宽度(占二维码的1/5) $scale = $logo_width / $logo_qr_width; //logo的宽度缩放比(本身宽度/组合后的宽度) $logo_qr_height = $logo_height / $scale; //组合之后logo的高度 $from_width = ($QR_width - $logo_qr_width) / 2; //组合之后logo左上角所在坐标点 //重新组合图片并调整大小 /* * imagecopyresampled() 将一幅图像(源图象)中的一块正方形区域拷贝到另一个图像中 */ imagecopyresampled($QR, $logo, $from_width, $from_width, 0, 0, $logo_qr_width, $logo_qr_height, $logo_width, $logo_height); } //输出图片 // echo $QR; $im = imagecreatefrompng($QR); // imagepng($im); imagedestroy($im); // return '<img src="/' . $filename . '" alt="扫码登录" style="width:240px">'; return '/' . $filename; }
第二步,回调使用用户code获取用户access_token
/** * 微信扫码回调 * @ApiInternal */ public function redirect() { //获取到code和state $param = $this->request->param(); //获取到access_token、expires_in、refresh_token、openid、scope $accessToken = $this->getAccessToken($param['code']); //未授权完整信息则提示用户授权 if(isset($accessToken['is_snapshotuser'])) return ("<div><h1>请点击下方授权完整信息!</h1></div>"); //获取用户信息 $wxuserinfo = $this->getWxUserInfo($accessToken['access_token'],$accessToken['openid']); if(strlen($param['state'])<16) { //账号绑定 $id = $param['state']; $data = User::get($id); if(!$data) $this->error("参数错误 (invalid parameter)", null, ApiErrorCode::$ParameterError); if($data->wx_openid) $this->error("绑定失败,该账号已被绑定!", null, 1); if(User::get(["wx_openid"=>$wxuserinfo['openid']])) $this->error("绑定失败,该微信已绑定其他账号!", null, 1); $data->wx_openid = $wxuserinfo['openid']; $data->wx_nickname = $wxuserinfo['nickname']; $data->wx_headimgurl = $wxuserinfo['headimgurl']; $result = $data->save(); if(!$result) $this->error("账号绑定失败", null, 1); return ("<div><h1>扫码成功!</h1></div>"); // $this->success("请求成功", '', 0); }else{ //扫码登录(即存入state值) if($wxuserinfo){ $user = User::get(["wx_openid" => $accessToken['openid']]); if(!$user) $this->error("扫码失败,请联系技术管理人员绑定账号!", null, 1); $user->wx_nickname = $wxuserinfo['nickname']; $user->wx_headimgurl = $wxuserinfo['headimgurl']; $user->wx_state = $param["state"]; $user->save(); } return ("<div><h1>扫码成功!</h1></div>"); // $this->success("扫码成功!", '', 0); } }
/** * 获取access_token * @ApiInternal */ public function getAccessToken($code = '') { if (!$code) { return []; } $queryarr = array( "appid" => $this->wechatAppID, "secret" => $this->wechatAppSecret, "code" => $code, "grant_type" => "authorization_code", ); $response = Http::get('https://api.weixin.qq.com/sns/oauth2/access_token', $queryarr); $ret = (array)json_decode($response, true); return $ret ? $ret : []; }
/** * 获取微信用户信息 * @ApiInternal * @param string access_token * @param string openid * @return array */ public function getWxUserInfo($access_token = '' , $openid = '') { $queryarr = [ "access_token" => $access_token, "openid" => $openid, "lang" => 'zh_CN' ]; $ret = Http::get('https://api.weixin.qq.com/sns/userinfo', $queryarr); $ret = (array)json_decode($ret, true); return $ret ? $ret : []; }
第三步,回调中使用用户access_token获取用户信息
/** * 获取微信用户信息 * @ApiInternal * @param string access_token * @param string openid * @return array */ public function getWxUserInfo($access_token = '' , $openid = '') { $queryarr = [ "access_token" => $access_token, "openid" => $openid, "lang" => 'zh_CN' ]; $ret = Http::get('https://api.weixin.qq.com/sns/userinfo', $queryarr); $ret = (array)json_decode($ret, true); return $ret ? $ret : []; }
第四步,回调中获取用户完整授权的openid进行账号绑定
/** * 账号绑定 * @ApiParams (name="id", type="int", required=true, description="会员id") * @ApiReturn ({ 图片html标签 * }) */ public function bind(){ $id = $this->request->param("id"); if(!$id) $this->error("参数错误 (invalid parameter)", null, ApiErrorCode::$ParameterError); //构造微信授权登录的URL $authURL = 'https://open.weixin.qq.com/connect/oauth2/authorize'; $authURL .= '?appid=' . $this->wechatAppID; $authURL .= '&redirect_uri='.$this->wechatRedirectURL; $authURL .= '&response_type=code'; $authURL .= '&scope=snsapi_base'; $authURL .= '&state='.$id; $authURL .= '#wechat_redirect'; $erweimaImg = $this->erweima($authURL); echo $erweimaImg; }
第五步,前端接口登录判定
/** * 扫码判断及登录 * @ApiMethod (POST) * @ApiParams (name="state", type="int", required=true, description="登录状态码") * @ApiReturn ({ "code": 0, "msg": "已登录", "time": "1684484807", "data": { "token": "962c5546-e344-4a2d-be5e-79186338678d" } }) */ public function isLogin() { header('Content-type:text/json; charset=utf-8'); //需post请求 if(!Request::instance()->isPost()) $this->error("需要 POST 请求", null, 43002); $state = $this->request->param('state'); if(!$state) $this->error("参数错误 (invalid parameter)", null, c); $result = User::where("wx_state",$state)->find(); if($result) { if(!Cookie::get('token')) { $id = openssl_encrypt($result->id, 'AES-128-ECB', '1236768886123456', OPENSSL_RAW_DATA); if ($this->auth->login('','',$id)) { $userInfo = $this->auth->getUserinfo(); Cookie::set("token",$userInfo['token'],0); $this->success("已登录", ['token'=>$userInfo['token']], 0); } else { $this->error(__('Please login first'), null, 401); } } $data = $this->auth->getUserinfo(); $this->success("已登录", ['token'=>$data['token']], 0); } $this->error('请先扫码', null, 1); }