柔晶美网络工作室

柔晶美网络工作室,倾心于web技术的博客站点

关注我 微信公众号

您现在的位置是: 首页 > 博客日记

Dcat Admin添加关注公众号后微信扫码自动登录功能

2021-03-05 admin laravel  1092

Dcat Admin默认使用账号密码登录,但我们可以很方便添加微信扫码登录,用户关注公众号后,在登录界面点击微信登录,弹出二维码,用微信扫一扫,如果用户已绑定过账号就自动登录进后台,如果没有绑定账号,则进入绑定账号界面。

原理:前台发起请求=>后台生成带参数二维码并将缓存参数=>前台展示二维码=>用户扫码获取openid并判断是否在数据库中=》前台轮询判断登录成功。

首先安装laravel-wechat

composer require "overtrue/laravel-wechat:^5.1"

生成配置文件:

php artisan vendor:publish --provider="Overtrue\LaravelWeChat\ServiceProvider"

编辑config/wechat.php文件

    'official_account' => [
        'default' => [
            'app_id'  => env('WEIXIN_KEY', 'your-app-id'),  // AppID
            'secret'  => env('WEIXIN_SECRET', 'your-app-secret'),  // AppSecret
            'token'   => env('WECHAT_ACCOUNT_TOKEN', 'my_weixin'), // Token
            'aes_key' => env('WECHAT_ACCOUNT_AES_KEY', ''), // EncodingAESKey

编辑.env文件,添加微信服务号相关的key

WEIXIN_KEY=wxaf8f
WEIXIN_SECRET=7a00bd3
WEIXIN_REDIRECT_URI="${APP_URL}/weixin/callback"
WECHAT_ACCOUNT_TOKEN=e35bx
WECHAT_ACCOUNT_AES_KEY=CYAOCD

我们为了让前后台用户都能扫码登录,将二维码等路由放前台,在前台路由中添加:

//easywechat
Route::get('/weixin', [WeixinController::class,'weixin'])->name('weixin');
Route::any('/wechat', [WeixinController::class,'serve'])->name('serve');
Route::get('/getwxpic', [WeixinController::class,'getWxPic'])->name('wx.pic');
Route::get('/loginCheck', [WeixinController::class,'loginCheck'])->name('home.login.check');

app/Http/Controllers创建控制器WeixinController:

class WeixinController extends Controller
{
    protected $app;
    protected $successStatus = 200;
    
    public function __construct()
    {
        $this->app = app('wechat.official_account');
    }
    
    //获取二维码图片
    public function getWxPic(Request $request)
    {
        // 查询 cookie,如果没有就重新生成一次
        if (!$cache_key = $request->cookie('wxcachekey')) {
            $cache_key = 'wechat'.Str::random(10);//生成一个不重复的key作为cache标识
        }
       // 缓存微信带参二维码
        if (!$url = Cache::get($cache_key)) {
            // 有效期 1 天的二维码
            $qrCode = $this->app->qrcode;
            $result = $qrCode->temporary($cache_key, 3600 * 24);
            $url    = $qrCode->url($result['ticket']);
            Cache::put($cache_key,$url,now()->addDay());
        }
        // 自定义参数返回给前端,前端轮询,并缓存cookie
        return response()->json(['wxcachekey'=>$cache_key,'url'=>$url,'code' => 200], $this->successStatus)->cookie('wxcachekey', $cache_key, 24 * 60);
    }
    
    //前台微信用户登录检查
    public function loginCheck(Request $request)
    {
        $wxcachekey = $request->wxcachekey.'_openid';
        if(!$openid = Cache::get($wxcachekey)){
            return response()->json(['code' => 500], 500);
        }
        if(!$user = User::where('openid',$openid)->first()) {
            return response()->json(['code' => 500], 500);
        }
        // 登录用户、并清空缓存
        Auth::login($user);
        Cache::forget($wxcachekey);
        return response()->json(['code' => 200], $this->successStatus);
    }
    
    //处理微信的请求消息,用户扫描二维码后处理
    public function serve()
    {
        $app = $this->app;
        $app->server->push(function ($message) {
            if ($message) {
                switch ($message['MsgType']) {
                    case 'event':
                        $func = strtolower($message['Event']);
                        return $this->$func($message);
                        break;
                    case 'text':
                        return '收到文字消息';
                        break;
                    case 'image':
                        return '收到图片消息';
                        break;
                    case 'voice':
                        return '收到语音消息';
                        break;
                    case 'video':
                        return '收到视频消息';
                        break;
                    case 'location':
                        return '收到坐标消息';
                        break;
                    case 'link':
                        return '收到链接消息';
                        break;
                    case 'file':
                        return '收到文件消息';
                    default:
                        return '收到其它消息';
                        break;
                }
                Log::info('无此处理方法:' . $method);
            }
        });
        return $app->server->serve();
    }
    
    //关注事件
    private function subscribe($message)
    {
        $openid = $message['FromUserName'];
        if(!$user = User::where('openid',$openid)->first()){
            //注册新用户
            $customer = User::create([
                'openid' => $openid,
                'name' => '微信用户'.time(),
                'phone' => '1'.time(),//手机号
                'sex' => 1,//性别
                'email' => time().mt_rand(1,100).'@qq.com',
                'password' => Hash::make('123456'),//默认密码
                'yhz' => Userzb::first()->id,//用户组
                'email_verified_at' => now(),//默认激活
            ]);
        }
        //将二维码唯一标识和用户唯一openid对应,存入cache
        $event_key = str_replace('qrscene_','',$message['EventKey']).'_openid';
        if(!Cache::get($event_key)){
            Cache::put($event_key, $openid, 4);
        }
        return '关注并登录成功';
    }

    //已关注扫码
    private function scan($message)
    {
        $event_key = $message['EventKey'].'_openid';
        $openid = $message['FromUserName'];
        if(!Cache::get($event_key)){
            Cache::put($event_key, $openid, 4);
        }
        return '扫码登录成功';
    }

然后在后台路由中添加:

$router->get('/wxlogin', 'AuthController@loginCheck');//微信登录

并将此路由排除在授权外,config/admin.php文件:

        'except' => [
            'auth/login',
            'auth/logout',
            'wxlogin',
        ],

将原来后台的登录模板复制一份,放在前台resources/views下,并添加以下内容:

    let timer = null;// 方便清除轮询
    let key = null;
    $('#wxlogin').on('click',function(){
        // 请求登录二维码
        $.ajax({
            url:"{{ route('wx.pic') }}",
            dataType:"json",
            success:function(res){
                console.log(res);
                if (res.code !== 200)return;
                var wxcachekey = res.wxcachekey;
                $('.wechat-url').attr('src', res.url);//显示二维码图片
                // 轮询登录状态
                timer = setInterval(() => {
                    var islogin = "{{Admin::user() ? 1 : 0}}";
                    if(islogin == 1)location.href = '/admin';
                    // 请求参数是二维码中的场景值
                    $.ajax({
                        url: "/admin/wxlogin",
                        dataType: "json",
                        data: {wxcachekey: wxcachekey},
                        success: function(res){
                            console.log(res);
                            if(res.code == 200){
                                Dcat.success('登录成功');
                                location.href = res.url;
                            }else if(res.code == 300){
                                location.reload();//刷新页面绑定openid
                            }
                            clearInterval(timer);//清除轮询
                        }
                    });
                }, 2000);
            }
        })
    })

修改app/Admin/Controllers文件夹的AuthController,里面有借权中转微信登录的内容,主要是有些客户没有服务号,内容如下 :

namespace App\Admin\Controllers;

use Dcat\Admin\Models\Administrator as AdminUserModel;//引用model登录
use Dcat\Admin\Admin;
use Dcat\Admin\Form;
use Dcat\Admin\Layout\Content;
use Dcat\Admin\Models\Repositories\Administrator;
use Dcat\Admin\Traits\HasFormResponse;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Validator;
use Dcat\Admin\Controllers\AuthController as BaseAuthController;
use App\Kzh\Curl_k;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Cookie;
use Log;
use Cache;

class AuthController extends BaseAuthController
{
    use HasFormResponse;
    
    protected $view = 'htlogin';
    
    //后台微信用户登录检查
    public function loginCheck(Request $request)
    {
        $wxcachekey = $request->wxcachekey.'_openid';
        if(!$openid = Cache::get($wxcachekey)){
            return response()->json(['code' => 500], 200);
        }
        if(!$check = AdminUserModel::whereJsonContains('openid', $openid)->first()) {
            return response()->json(['code' => 500], 200);
        }
        // 登录用户、并清空缓存
        session(['openid' => $openid]);//微信openid如果已存在用户中,则自动登录
        if(!empty($check)){
            $this->guard()->login($check);
            session()->regenerate();
            $url = $this->getRedirectPath();
            Cache::forget($wxcachekey);
            return response()->json(['code'=>200,'url'=>$url], 200);
        }else{
            //没有绑定账号,跳转绑定
            return response()->json(['code'=>300], 200);
        }
    }
    
    //重写登录逻辑
    public function getLogin(Content $content){
        if ($this->guard()->check()) {
            return redirect($this->getRedirectPath());
        }
        $curl = new Curl_k();
        $openid = session('openid',null);
		if($curl->isWxClient() && empty($openid)){
            $appid = env('WEIXIN_KEY');
            $appKey = env('WEIXIN_SECRET');
		    $code = $_GET['code'] ?? null;
            if(env('WEXIN_ON') == 'borrow'){
                //没自己服务号,需要借权
                if (empty($code)){
                    $bzurl = env('APP_URL').'/admin/auth/login';//本站url
                    $url = 'https://www.roujingmei.com/wxgetcode?url='.$bzurl;
                    Header("Location: $url");//跳转获取
                }else{
            		$urlObj["appid"] = $appid;
            		$urlObj["secret"] = $appKey;
            		$urlObj["code"] = $code;
            		$urlObj["grant_type"] = "authorization_code";
            		$buff = "";
            		foreach ($urlObj as $k => $v){
            			if($k != "sign") $buff .= $k . "=" . $v . "&";
            		}
            		$bizString = trim($buff, "&");
            		$url = "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString; 
            		$options = array();
            		$ch = curl_init($url);
            		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            		curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            		if (!empty($options)) {
            			curl_setopt_array($ch, $options);
            		}
            		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            		$res = curl_exec($ch);
            		curl_close($ch);
            		$data = json_decode($res,true);
            		$openid = $data['openid'] ?? null;//取出openid
                }
            }else{
                //有自己的服务号
                if (empty($code)){
                    $scheme = $_SERVER['HTTPS']=='on' ? 'https://' : 'http://';
                    $uri = $_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING'];
                    if($_SERVER['REQUEST_URI']) $uri = $_SERVER['REQUEST_URI'];
                    $baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$uri);
                    $urlObj["appid"] = $appid;
                    $urlObj["redirect_uri"] = "$baseUrl";
                    $urlObj["response_type"] = "code";
                    $urlObj["scope"] = "snsapi_base";
                    $urlObj["state"] = "STATE"."#wechat_redirect";
                    $buff = "";
                    foreach ($urlObj as $k => $v){
                        if($k != "sign") $buff .= $k . "=" . $v . "&";
                    }
                    $bizString = trim($buff, "&");
                    $url = "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString; 
                    Header("Location: $url");
                    exit();
                }else{
                    $appid = env('WEIXIN_KEY');
                    $appKey = env('WEIXIN_SECRET');
            		$urlObj["appid"] = $appid;
            		$urlObj["secret"] = $appKey;
            		$urlObj["code"] = $code;
            		$urlObj["grant_type"] = "authorization_code";
            		$buff = "";
            		foreach ($urlObj as $k => $v){
            			if($k != "sign") $buff .= $k . "=" . $v . "&";
            		}
            		$bizString = trim($buff, "&");
            		$url = "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString; 
            		$options = array();
            		$ch = curl_init($url);
            		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            		curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            		if (!empty($options)) {
            			curl_setopt_array($ch, $options);
            		}
            		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            		$res = curl_exec($ch);
            		curl_close($ch);
            		$data = json_decode($res,true);
            		$openid = $data['openid'] ?? null;
                }
            }
            if(!empty($openid)){
                session(['openid' => $openid]);//微信openid如果已存在用户中,则自动登录
                $check = AdminUserModel::whereJsonContains('openid', $openid)->first();//微信openid已在用户中自动登录
                if(!empty($check)){
                    $this->guard()->login($check);
                    session()->regenerate();
                    return redirect($this->getRedirectPath());
                }
            }
		}
        //如果openid没有绑定,则跳转绑定页面
        return $content->full()->body(view($this->view,compact('openid')));
    }
    
    //登录认证
    public function postLogin(Request $request)
    {
        $openid = session('openid',null);
        $credentials = $request->only([$this->username(), 'password']);
        $remember = (bool) $request->input('remember', false);

        /** @var \Illuminate\Validation\Validator $validator */
        $validator = Validator::make($credentials, [
            $this->username()   => 'required',
            'password'          => 'required',
        ]);

        if ($validator->fails()) {
            return $this->validationErrorsResponse($validator);
        }

        if ($this->guard()->attempt($credentials, $remember)) {
            $username = $credentials['username'];
            if(!empty($openid)){
                //先获取用户openid,再追加更新Openid,实现多微信共账号登录
                $old = AdminUserModel::where('username',$username)->first()->openid;
                if(empty($old) or !in_array($openid,$old)){
                    $old[] = $openid;
                    AdminUserModel::where('username',$username)->update(['openid'=>$old]);
                }
            }
            return $this->sendLoginResponse($request);
        }

        return $this->validationErrorsResponse([
            $this->username() => $this->getFailedLoginMessage(),
        ]);
    }
    
    protected function getRedirectPath()
    {
        return $this->redirectTo ?: admin_url('/');
    }

这样就实现了微信扫码自动登录功能了。

文章评论


需要 登录 才能发表评论
热门评论
0条评论

暂时没有评论!