精华内容
下载资源
问答
  • 微信扫码 - 关注公众号后网站自动注册并登录的实现 需求描述 在自己网站上点击微信登录,网站自己弹出一个二维码、扫描二维码后弹出公众号的关注界面、只要一关注公众号网站自动登录、第二次扫描登录的时候网站...

    微信扫码 - 关注公众号后网站自动注册并登录的实现


    需求描述

    在自己网站上点击微信登录,网站自己弹出一个二维码、扫描二维码后弹出公众号的关注界面、只要一关注公众号网站自动登录、第二次扫描登录的时候网站直接登录。

    大家可以体验一下 「随便找的一个网站

    前期准备

    一个公众号(必须认证,配置服务器)

    微信开发文档

    实现原理

    公众平台提供了生成带参二维码的接口。使用该接口可以生成带不同场景值的二维码,用户扫描后,公众号可以接收到扫码/关注事件推送,在细分如下:

    • 扫描二维码,如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值(自定义值)关注事件推送给开发者。
    • 扫描二维码,如果用户已经关注公众号,在用户扫描后会自动进入会话,微信也会将带场景值(自定义值)扫码事件推送给开发者。

    设计如下流程:

    1. 生成二维码的时候你自定义一个参数到二维码中,顺便把这个参数传到前端页面中。
    2. 用户扫码关注后微信服务器发送一个关注事件或扫码事件消息到自己服务端,消息参数中包括了自定义参数和扫码用户openid等参数。
    3. 根据openid用微信公众号接口去获取用户信息,拿到用户信息之后就是实现注册逻辑,用自定义参数标记作为缓存key标记可以登录。
    4. 前端轮询查询定义参数为key的缓存是否标记可登录时,就开始实现登录逻辑,重载页面,流程完毕。

    实战

    1.微信公众号部署服务器和Token认证

    由于我们自己服务,需要接管微信服务器的推送,所以需要在微信公众号后台配置服务器通知地址,公众号测试号的配置大同小异这里不做累述,如图所示:

    注:这个配置启用后,微信服务器会把相关的事件推送都转发到用户服务器上,同时微信后台的自定义菜单和回复也失效,需要用户在自己服务器上通过微信接口来接管

    Token验证示例代码: 

    <?php
    // 微信token认证
    $signature = $_GET["signature"];
    $timestamp = $_GET["timestamp"];
    $nonce = $_GET["nonce"];
    $echostr = $_GET["echostr"];
    // 你在微信公众号后台的设置的Token
      $token = "";
    
    // 1)将token、timestamp、nonce三个参数进行字典序排序
    $tmpArr = array($nonce,$token,$timestamp);
    sort($tmpArr,SORT_STRING);
    
    // 2)将三个参数字符串拼接成一个字符串进行sha1加密
    $str = implode($tmpArr);
    $sign = sha1($str);
    
    // 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
    if ($sign == $signature) {
      echo $echostr;
    }

    2.服务端生成带参二维码

    先了解下,公众号的带参二维码生成 接口文档 ,我们选择临时二维码,步骤如下:

    第一步:通过AppID和AppSecret获取access_token

    这里的access_token和上边的token不是一回事,不要混淆。获取access_token接口文档 ,这一步建议抽离封装,以便其他接口复用。

    第二步:通过access_token创建二维码ticket

    每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),分别介绍临时二维码和永久二维码的创建二维码ticket过程。

    第三步:通过ticket换取二维码

    这一步其实就是拼接成一个二维码图片地址:https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET 提醒:TICKET记得进行UrlEncode,然后把这个二维码图片地址,返回给前端直接显示

    示例代码:

        /**
         * 获取微信场景二维码
         * @param $sceneValue 场景值
         * @param int $type 1:临时二维码  2:永久二维码
         * @return string
         */
        public static function getWeChatUrl($sceneValue,$type = 1){
            $accessToken = WxToken::getToken();
            $url = 'https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token='.$accessToken;
            $actionName = 'QR_STR_SCENE';
            if($type==2){
                $actionName = 'QR_LIMIT_STR_SCENE';
            }
    
            $res = self::post($url,json_encode([
                'expire_seconds'=>604800,
                'action_name'=>$actionName,
                'action_info'=>[
                    'scene'=>[
                        'scene_id'=>$sceneValue,
                        'scene_str'=>$sceneValue
                    ]
                ]
            ]));
            if(isset($res['errcode']) && $res['errcode']){
                echo $res['errmsg'];die();
            }
            return 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket='.$res['ticket'];
        }

    3.前端请求二维码,轮询状态(也可使用websocket)

        // 方便清除轮询
        var timer = null;
        var flag = '';
    
        $(document).on('click', '.wechat-login', function () {
            // 请求登录二维码
            $.get('/admin/index/weChatQrCode', function (data, status) {
                $('.wechat-url').attr('src', data.data.qr_code_url);
    
                // 显示二维码
                flag = data.data.scene_value;
    
                // 轮询登录状态
                timer = setInterval(function () {
                    // 对话框关闭时,清除轮询
                    if ($('#wx-login-pan').attr('class').indexOf('open') == -1) {
                        clearInterval(timer);
                        return;
                    }
                    // 请求参数是二维码中的场景值
    
                    $.get('/admin/index/loginCheck?wechat_flag='+flag, function (data, status) {
                        if(data.code==0){
                           window.location.href = '/home/serve/apply';
                        }
                    });
                }, 2000);
            });
        });
    
        // 返回时清除轮询
        $('.wechat-back').click(function () {
            clearInterval(timer)
        });
    
        function closeLoginPan() {
            clearInterval(timer);
        }

    4.后端扫码事件接受处理

    // 微信事件接收类
    class WxPush
    {
        /**
         * 获得请求时POST:XML字符串
         * 不能用$_POST获取,因为没有key
         */
        public static function getResponse()
        {
            $xmlStr = $GLOBALS['HTTP_RAW_POST_DATA'];
            Log::record($xmlStr, 'wx_push');
            if (empty($xmlStr)) {
                return false;
            }
            // 解析该xml字符串,利用simpleXML
            libxml_disable_entity_loader(true);
            //禁止xml实体解析,防止xml注入
            $xml = simplexml_load_string($xmlStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $data = json_decode(json_encode($xml),true);
            //判断该消息的类型,通过元素MsgType
            Log::write($data, 'wx_push');
            switch ($data['MsgType']) {
                case 'event': // 事件处理
                    self::handleEvent($data);
                    break;
                case 'text'://文本消息
    
                    break;
                case 'image'://图片消息
    
                    break;
                case 'voice'://语音消息
    
                    break;
                case 'video'://视频消息
    
                    break;
                case 'shortvideo'://短视频消息
    
                    break;
                case 'location'://位置消息
    
                    break;
                case 'link'://链接消息
    
                    break;
            }
        }
    
        /**
         * 事件引导处理方法(事件有许多,拆分处理)
         *
         * @param $data
         * @return mixed
         * @internal param $request
         * @internal param $event
         */
        static function handleEvent($data)
        {
            $method = strtolower($data['Event']);
            $event = new Event();
            if (method_exists($event, $method)) {
                return call_user_func_array([$event, $method], [$data]);
            }
        }
    
    }
    // 事件类
    class Event
    {
        /**
         * 扫描带参二维码事件
         */
        public function scan($data)
        {
            // 标记前端可登陆
            Admin::markLogin($data['EventKey'],$data['FromUserName']);
        }
    
        /**
         * 关注订阅
         */
        public function subscribe($data)
        {
            // 关注事件的场景值会带一个前缀需要去掉
            $data['EventKey'] = str_replace('qrscene_','',$data['EventKey']);
    
            // 标记前端可登陆
            Admin::markLogin($data['EventKey'],$data['FromUserName']);
        }
    
        /**
         * 取消订阅
         */
        public function unsubscribe($data)
        {
            $adminName = $data['FromUserName'];
            $admin = Admin::get(['wx_openid'=>$data['FromUserName']]);
            if($admin && $admin['name']){
                $adminName = $admin['name'];
            }
        }
    
        /**
         *  菜单点击事件
         */
        public function click(){
    
        }
    
        /**
         * 连接跳转事件
         */
        public function view(){
    
        }
    }
        /**
         * 标记可登录
         */
        public static function markLogin($key,$val)
        {
            $admin = self::get(['wx_openid'=>$val]);
            $time = time();
            if(empty($admin)){
                $wxUser = WxUser::getCgiUserInfo($val);
                $admin = self::create([
                    'wx_openid'=>$val,
                    'name'=>Emoji::encode($wxUser['nickname']),
                    'head_img_url'=>$wxUser['headimgurl'],
                    'sex'=>$wxUser['sex'],
                    'province'=>$wxUser['province'],
                    'city'=>$wxUser['city'],
                    'type'=>3,// 客户
                    'create_time'=>$time,
                    'update_time'=>$time
                ]);
            }
            // 写入缓存
            Cache::set($key, $val,60*60);
        }

    5.登录检查

    在前端轮询请求检查登录状态接口时,把自定义参数传给后端,后端以该自定义参数为key去缓存查询可登录状态,查到可以登录了则去授权登录,返回登录成功。

        /**
         * 微信用户登录检查
         *
         */
        public function loginCheck()
        {
            // 判断请求是否有微信登录标识
            if (!$flag = $this->request->get('wechat_flag')) {
                $this->apiError(-1,'标识不能为空');
            }
    
            // 根据微信标识在缓存中获取需要登录用户的 UID
            $uid  = Cache::get($flag);
            if(empty($uid)){
                $this->apiError(-1,'void');
            }
            $admin = Admin::get(['wx_openid'=>$uid]);
            if(empty($admin)){
                $this->apiError(-1,'用户未注册');
            }
            Admin::doLogin($admin);
            $this->apiSuccess($flag);
    
        }

    展开全文
  • 1、扫码关注公众号,这个公众号有什么办法可以用订阅号,不想用服务号。 2、用订阅号有办法实现吗?
  • 「实用」微信扫码关注公众号号后自动登录 利于推广方式 另外一种扫码登录方式只需要一个微信服务号就行,大概流程是:点击微信登录,网站自己弹出一个二维码、扫描二维码后弹出公众号的关注...

    常见方式

    平常大家见到过最多的扫码登录应该是 开放平台网页登录 大概形式就是:点击微信登录后会出现一个黑页面,页面中有一个二维码,扫码后可以自动获取用户信息然后登录,但是这种方式需要申请开放平台比较麻烦。如图

    「实用」微信扫码关注公众号号后自动登录

    利于推广方式

    另外一种扫码登录方式只需要一个微信服务号就行,大概流程是:点击微信登录,网站自己弹出一个二维码、扫描二维码后弹出公众号的关注界面、只要一关注公众号网站自动登录、第二次扫描登录的时候网站直接登录,大家可以体验一下 「随便找的一个网站」,这种扫码登录的方式个人觉得非常利于推广公众号

    前期准备

    • 服务号(或者微信测试账号)

    • EasyWeChat 扩展包

    梳理

    其实第二种扫码登录的原理很简单,核心就是依靠 微信带参二维码、EasyWeChat 二维码文档

    简单的解释一下扫描这个带参二维码有什么不同:

    • 扫描二维码,如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值(自定义值)关注事件推送给开发者。

    • 扫描二维码,如果用户已经关注公众号,在用户扫描后会自动进入会话,微信也会将带场景值(自定义值)扫码事件推送给开发者。

    看到这里相信你已经明白了,梳理一下:

    • 生成二维码的时候你自定义一个参数到二维码中,顺便把这个参数传到前端页面中。

    • 前端页面根据这个参数轮询用户登录状态(也可使用 socket)。

    • 用户扫码关注后会推送一个关注事件到服务端,也会把自定义参数带入到事件中。

    • 根据 openid 创建用户后,然后在 Redis 中存储 Key 为场景值(自定义参数) Value 为用户创建后的 id。

    • 前端轮询方法中如果在 Redis 中获取到 Id 后,Auth 登陆,页面再重载一下,流程完毕。

    实战

    请求登录二维码

    前端通过一个点击事件请求微信登录二维码

    // 方便清除轮询
    let timer = null
    
    $(document).on('click', '.wechat-login', function () {
        // 请求登录二维码
        axios.get('{{ route('wx.pic') }}').then(response => {
          let result = response.data
          if (result.status_code !== 200) {
            return
          }
          // 显示二维码图片
          $('.wechat-url').attr('src', result.data.url)
          // 轮询登录状态
          timer = setInterval(() => {
            // 请求参数是二维码中的场景值
            axios.get('{{ route('home.login.check') }}', {params: {wechat_flag: result.data.weChatFlag}}).then(response => {
              let result = response.data
              if (result.data) {
                window.location.href = '/'
              }
            })
          }, 2000)
        })
      })
    
      // 返回时清除轮询
      $('.wechat-back').click(function () {
      clearInterval(timer)
     })
    

    后端生成带参二维码逻辑,EasyWeChat 配置请自行查阅 文档

    protected $app;
    
        /**
         * Construct
         *
         * WeChatController constructor.
         */
        public function __construct() {
            $this->app = app('wechat.official_account');
        }
    
      /**
         * 获取二维码图片
         *
         * @param Request $request
         *
         * @return \Illuminate\Http\JsonResponse
         * @throws \Exception
         */
        public function getWxPic(Request $request) {
            // 查询 cookie,如果没有就重新生成一次
            if (!$weChatFlag = $request->cookie(WxUser::WECHAT_FLAG)) {
                $weChatFlag = Uuid::uuid4()->getHex();
            }
    
           // 缓存微信带参二维码
            if (!$url = Cache::get(WxUser::QR_URL . $weChatFlag)) {
                // 有效期 1 天的二维码
                $qrCode = $this->app->qrcode;
                $result = $qrCode->temporary($weChatFlag, 3600 * 24);
                $url    = $qrCode->url($result["ticket"]);
    
                Cache::put(WxUser::QR_URL . $weChatFlag, $url, now()->addDay());
            }
    
            // 自定义参数返回给前端,前端轮询
            return $this->ajaxSuccess(compact('url', 'weChatFlag'))
                ->cookie(WxUser::WECHAT_FLAG, $weChatFlag, 24 * 60);
        }
    

    用户扫描二维码后处理

    /**
         * 微信消息接入(这里拆分函数处理)
         *
         * @return \Symfony\Component\HttpFoundation\Response
         * @throws \EasyWeChat\Kernel\Exceptions\BadRequestException
         * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
         * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
         * @throws \ReflectionException
         */
        public function serve() {
            $app = $this->app;
    
            $app->server->push(function ($message) {
                if ($message) {
                    $method = camel_case('handle_' . $message['MsgType']);
    
                    if (method_exists($this, $method)) {
                        $this->openid = $message['FromUserName'];
    
                        return call_user_func_array([$this, $method], [$message]);
                    }
    
                    Log::info('无此处理方法:' . $method);
                }
            });
    
            return $app->server->serve();
        }
    
        /**
         * 事件引导处理方法(事件有许多,拆分处理)
         *
         * @param $event
         *
         * @return mixed
         */
        protected function handleEvent($event) {
            Log::info('事件参数:', [$event]);
    
            $method = camel_case('event_' . $event['Event']);
            Log::info('处理方法:' . $method);
    
            if (method_exists($this, $method)) {
                return call_user_func_array([$this, $method], [$event]);
            }
    
            Log::info('无此事件处理方法:' . $method);
        }
    
        /**
         * 取消订阅
         *
         * @param $event
         */
        protected function eventUnsubscribe($event) {
            $wxUser                 = WxUser::whereOpenid($this->openid)->first();
            $wxUser->subscribe      = 0;
            $wxUser->subscribe_time = null;
            $wxUser->save();
        }
    
        /**
         * 扫描带参二维码事件
         *
         * @param $event
         */
        public function eventSCAN($event) {
            if ($wxUser = WxUser::whereOpenid($this->openid)->first()) {
                // 标记前端可登陆
                $this->markTheLogin($event, $wxUser->uid);
    
                return;
            }
        }
    
        /**
         * 订阅
         *
         * @param $event
         *
         * @throws \Throwable
         */
        protected function eventSubscribe($event) {
            $openId = $this->openid;
    
            if ($wxUser = WxUser::whereOpenid($openId)->first()) {
                // 标记前端可登陆
                $this->markTheLogin($event, $wxUser->uid);
    
                return;
            }
    
            // 微信用户信息
            $wxUser = $this->app->user->get($openId);
            // 注册
            $nickname = $this->filterEmoji($wxUser['nickname']);
    
            $result = DB::transaction(function () use ($openId, $event, $nickname, $wxUser) {
                $uid  = Uuid::uuid4()->getHex();
                $time = time();
    
                // 用户
                $user = User::create([
                    'uid'        => $uid,
                    'created_at' => $time,
                ]);
                // 用户信息
                $user->user_info()->create([
                    'email'      => $user->email,
                    'nickname'   => $nickname,
                    'sex'        => $wxUser['sex'],
                    'address'    => $wxUser['country'] . ' ' . $wxUser['province'] . ' ' . $wxUser['city'],
                    'avatar'     => $wxUser['headimgurl'],
                    'code'       => app(UserRegisterController::class)->inviteCode(10),
                    'created_at' => $time,
                ]);
                // 用户账户
                $user->user_account()->create([
                    'gold'       => 200,
                    'created_at' => $time,
                ]);
    
                $wxUserModel = $user->wx_user()->create([
                    'subscribe'      => $wxUser['subscribe'],
                    'subscribe_time' => $wxUser['subscribe_time'],
                    'openid'         => $wxUser['openid'],
                    'created_at'     => $time,
                ]);
    
                Log::info('用户注册成功 openid:' . $openId);
    
                $this->markTheLogin($event, $wxUserModel->uid);
            });
    
            Log::debug('SQL 错误: ', [$result]);
        }
    
        /**
         * 标记可登录
         *
         * @param $event
         * @param $uid
         */
        public function markTheLogin($event, $uid) {
            if (empty($event['EventKey'])) {
                return;
            }
    
            $eventKey = $event['EventKey'];
    
            // 关注事件的场景值会带一个前缀需要去掉
            if ($event['Event'] == 'subscribe') {
                $eventKey = str_after($event['EventKey'], 'qrscene_');
            }
    
            Log::info('EventKey:' . $eventKey, [$event['EventKey']]);
    
            // 标记前端可登陆
            Cache::put(WxUser::LOGIN_WECHAT . $eventKey, $uid, now()->addMinute(30));
        }
    

    前端登录检查

    /**
         * 微信用户登录检查
         *
         * @param Request $request
         *
         * @return bool|\Illuminate\Http\JsonResponse
         */
        public function loginCheck(Request $request) {
            // 判断请求是否有微信登录标识
            if (!$flag = $request->wechat_flag) {
                return $this->ajaxSuccess(false);
            }
    
            // 根据微信标识在缓存中获取需要登录用户的 UID
            $uid  = Cache::get(WxUser::LOGIN_WECHAT . $flag);
            $user = User::whereUid($uid)->first();
    
            if (empty($user)) {
                return $this->ajaxSuccess(false);
            }
    
            // 登录用户、并清空缓存
            auth('web')->login($user);
            Cache::forget(WxUser::LOGIN_WECHAT . $flag);
            Cache::forget(WxUser::QR_URL . $flag);
    
            return $this->ajaxSuccess(true);
        }
    

    OK,很实用的一个功能吧,赶快加到你项目中吧!

    展开全文
  • 用户扫码关注后,微信服务器会通知 微信接口配置的url 服务器拿到openId和ticket 将 ticket作为key,openId作为value 存储至redis 当前端拿ticket 请求服务器,服务端拿ticket去redis中查询。如果存在即扫码关注成...

    流程如下

    1. 服务端获取accessToken
    2. 服务端请求微信接口生成带参数的二维码,微信会返回{二维码ticket,二维码有效时间,二维码图片地址}等信息。服务端将ticket响应给前端,前端拿ticket开始轮询请求服务器
    3. 用户扫码关注后,微信服务器会通知微信接口配置的url接口并携带{FromUserName:发送方的openid,Event:事件类型 subscribe(订阅)、unsubscribe(取消订阅),二维码的ticket}等信息
    4. 服务器拿到openId和ticket 将 ticket作为key,openId作为value 存储至redis。表明此用户已扫码关注。
    5. 当前端拿ticket 轮询请求服务器时,服务端拿ticket去redis中查询。如果存在即扫码或关注成功,查看openid是否已注册,如已注册登录成功,如未注册则去注册。业务处理完成后服务端删除redis中的数据。

    微信配置:
    基本配置–》服务器配置。服务器地址(URL)即事件触发时被通知的接口

    1. 服务端获取accessToken

    1. accessToken 不建议使用时去获取
    2. accessToken 失效性是两个小时,所以我们在redis存储1小时时间
        @Override
        public String getPubAccessToken() {
            String pubAccessToken;
            Object obj = redisTemplate.opsForValue().get("pubAccessToken");
            if (obj != null) {
                pubAccessToken = (String)obj;
                return pubAccessToken;
            } else {
                log.info("开始获取pubAccessToken");
                try {
                    String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + wxPubPayProperties.getAppId() + "&secret=" + wxPubPayProperties.getAppSecret();
                    ResponseEntity<Map> entity =  restTemplate.exchange(url,HttpMethod.GET, null,Map.class);
                    Map<String,Object> map = entity.getBody();
                    pubAccessToken = map.get("access_token") != null ? String.valueOf(map.get("access_token")) : null;
                    if (StringUtils.isNotEmpty(pubAccessToken)) {
                        redisTemplate.opsForValue().set("pubAccessToken", pubAccessToken, 3600, TimeUnit.SECONDS);
                        return pubAccessToken;
                    } else {
                        return null;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }
        }
    

    2. 服务端生成带参数的二维码

        @Override
        public AppResult createQrCode(String sceneStr) {
            String pubAccessToken = getPubAccessToken();
            String url ="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + pubAccessToken;
    
            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.add("Content-type", "application/json; charset=utf-8");
            HttpEntity<String> requestEntity = new HttpEntity<>("{\"expire_seconds\": 3600, \"action_name\": \"QR_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": " + sceneStr +" }}}", requestHeaders);
            Map<String,Object> map  =  restTemplate.postForObject(url,requestEntity,Map.class);
            // map为响应参数 其中有ticket expire_seconds url 
            return AppResult.successReturnDate(map);
        }
    

    3. 被微信服务器通知的服务端接口

    1. 将token、timestamp、nonce三个参数进行字典序排序
    2. 将三个参数字符串拼接成一个字符串进行sha1加密
    3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
    4. 接收事件推送 查看xmlData里的参数
        @PostMapping("weChatNotice")
        public String weChatNotice(@RequestBody String xmlData,String signature,Long timestamp,String nonce, String echostr) {
            wxService.weChatNotice(xmlData);
            return echostr;
        }
    

    4. 绑定ticket和openid至redis

        @Override
        public void weChatNotice(String xmlData) {
            // xml转map
            Map<String, String> resultMap = WxPayKit.xmlToMap(xmlData);
            String ticket = resultMap.get("Ticket");
            if (StringUtils.isNotEmpty(ticket)) {
                String openId = resultMap.get("FromUserName");
                // 将ticket 和 openId 存储进redis
                redisTemplate.opsForValue().set(ticket, openId, 3600, TimeUnit.SECONDS);
            }
        }
    

    5. 被前端轮询的接口

        @Override
        public AppResult wxLogin(String ticket) {
            // 判断 用户是否已扫码关注
            Map<String,Object> map = new HashMap<>();
            Object obj = redisTemplate.opsForValue().get(ticket);
            if (obj == null) {
                return AppResult.errorReturn(10024, "用户还未扫码关注");
            } else {
                String openId = (String) obj; 
                // 用户已扫码登录,删除redis
                redisTemplate.delete(ticket);
                // 查询openId是否已绑定手机号注册
                map.put("openId",openId);
                Member member = memberMapper.getMemberOpenId(openId);
                if (member == null) {
                    map.put("state",1);
                    return  AppResult.successReturnDate(map, "用户已扫码,但未注册");
                } else {
                    map.put("state",0);
                    return  AppResult.successReturnDate(map, "用户已扫码,已注册");
                }
            }
        }
    

    6. 根据openId获取微信用户信息

        public Map<String,Object> getWxUserInfo(String openId) {
            String pubAccessToken = getPubAccessToken();
            String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + pubAccessToken + "&openid=" + openId + "&lang=zh_CN";
            ResponseEntity<Map> entity =  restTemplate.exchange(url,HttpMethod.GET, null,Map.class);
            Map<String,Object> map = entity.getBody();
            return map;
        }
    
    
    展开全文
  • 点击上方好好学java,选择星标公众号重磅资讯、干货,第一时间送达 今日推荐:用好Java中的枚举,真的没有那么简单!个人原创+1博客:点击前往,查看更多 作者:destin...

      点击上方 好好学java ,选择 星标 公众号

    重磅资讯、干货,第一时间送达
    今日推荐:用好Java中的枚举,真的没有那么简单!个人原创+1博客:点击前往,查看更多
    
    作者:destiny
    链接:https://segmentfault.com/a/1190000022188562
    

    序言

    常见方式

    平常大家见到过最多的扫码登录应该是 开放平台网页登录 大概形式就是:点击微信登录后会出现一个黑页面,页面中有一个二维码,扫码后可以自动获取用户信息然后登录,但是这种方式需要申请开放平台比较麻烦。如图

    「实用」微信扫码关注公众号号后自动登录

    利于推广方式

    另外一种扫码登录方式只需要一个微信服务号就行,大概流程是:点击微信登录,网站自己弹出一个二维码、扫描二维码后弹出公众号的关注界面、只要一关注公众号网站自动登录、第二次扫描登录的时候网站直接登录,大家可以体验一下 「随便找的一个网站」,这种扫码登录的方式个人觉得非常利于推广公众号

    前期准备

    • 服务号(或者微信测试账号)

    • EasyWeChat 扩展包

    梳理

    其实第二种扫码登录的原理很简单,核心就是依靠 微信带参二维码、EasyWeChat 二维码文档

    简单的解释一下扫描这个带参二维码有什么不同:

    • 扫描二维码,如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值(自定义值)关注事件推送给开发者。

    • 扫描二维码,如果用户已经关注公众号,在用户扫描后会自动进入会话,微信也会将带场景值(自定义值)扫码事件推送给开发者。

    看到这里相信你已经明白了,梳理一下:

    • 生成二维码的时候你自定义一个参数到二维码中,顺便把这个参数传到前端页面中。

    • 前端页面根据这个参数轮询用户登录状态(也可使用 socket)。

    • 用户扫码关注后会推送一个关注事件到服务端,也会把自定义参数带入到事件中。

    • 根据 openid 创建用户后,然后在 Redis 中存储 Key 为场景值(自定义参数) Value 为用户创建后的 id。

    • 前端轮询方法中如果在 Redis 中获取到 Id 后,Auth 登陆,页面再重载一下,流程完毕。

    实战

    请求登录二维码

    前端通过一个点击事件请求微信登录二维码

    // 方便清除轮询
    let timer = null
    
    $(document).on('click', '.wechat-login', function () {
        // 请求登录二维码
        axios.get('{{ route('wx.pic') }}').then(response => {
          let result = response.data
          if (result.status_code !== 200) {
            return
          }
          // 显示二维码图片
          $('.wechat-url').attr('src', result.data.url)
          // 轮询登录状态
          timer = setInterval(() => {
            // 请求参数是二维码中的场景值
            axios.get('{{ route('home.login.check') }}', {params: {wechat_flag: result.data.weChatFlag}}).then(response => {
              let result = response.data
              if (result.data) {
                window.location.href = '/'
              }
            })
          }, 2000)
        })
      })
    
      // 返回时清除轮询
      $('.wechat-back').click(function () {
      clearInterval(timer)
     })
    

    后端生成带参二维码逻辑,EasyWeChat 配置请自行查阅 文档

    protected $app;
    
        /**
         * Construct
         *
         * WeChatController constructor.
         */
        public function __construct() {
            $this->app = app('wechat.official_account');
        }
    
      /**
         * 获取二维码图片
         *
         * @param Request $request
         *
         * @return \Illuminate\Http\JsonResponse
         * @throws \Exception
         */
        public function getWxPic(Request $request) {
            // 查询 cookie,如果没有就重新生成一次
            if (!$weChatFlag = $request->cookie(WxUser::WECHAT_FLAG)) {
                $weChatFlag = Uuid::uuid4()->getHex();
            }
    
           // 缓存微信带参二维码
            if (!$url = Cache::get(WxUser::QR_URL . $weChatFlag)) {
                // 有效期 1 天的二维码
                $qrCode = $this->app->qrcode;
                $result = $qrCode->temporary($weChatFlag, 3600 * 24);
                $url    = $qrCode->url($result['ticket']);
    
                Cache::put(WxUser::QR_URL . $weChatFlag, $url, now()->addDay());
            }
    
            // 自定义参数返回给前端,前端轮询
            return $this->ajaxSuccess(compact('url', 'weChatFlag'))
                ->cookie(WxUser::WECHAT_FLAG, $weChatFlag, 24 * 60);
        }
    

    用户扫描二维码后处理

    /**
         * 微信消息接入(这里拆分函数处理)
         *
         * @return \Symfony\Component\HttpFoundation\Response
         * @throws \EasyWeChat\Kernel\Exceptions\BadRequestException
         * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
         * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
         * @throws \ReflectionException
         */
        public function serve() {
            $app = $this->app;
    
            $app->server->push(function ($message) {
                if ($message) {
                    $method = camel_case('handle_' . $message['MsgType']);
    
                    if (method_exists($this, $method)) {
                        $this->openid = $message['FromUserName'];
    
                        return call_user_func_array([$this, $method], [$message]);
                    }
    
                    Log::info('无此处理方法:' . $method);
                }
            });
    
            return $app->server->serve();
        }
    
        /**
         * 事件引导处理方法(事件有许多,拆分处理)
         *
         * @param $event
         *
         * @return mixed
         */
        protected function handleEvent($event) {
            Log::info('事件参数:', [$event]);
    
            $method = camel_case('event_' . $event['Event']);
            Log::info('处理方法:' . $method);
    
            if (method_exists($this, $method)) {
                return call_user_func_array([$this, $method], [$event]);
            }
    
            Log::info('无此事件处理方法:' . $method);
        }
    
        /**
         * 取消订阅
         *
         * @param $event
         */
        protected function eventUnsubscribe($event) {
            $wxUser                 = WxUser::whereOpenid($this->openid)->first();
            $wxUser->subscribe      = 0;
            $wxUser->subscribe_time = null;
            $wxUser->save();
        }
    
        /**
         * 扫描带参二维码事件
         *
         * @param $event
         */
        public function eventSCAN($event) {
            if ($wxUser = WxUser::whereOpenid($this->openid)->first()) {
                // 标记前端可登陆
                $this->markTheLogin($event, $wxUser->uid);
    
                return;
            }
        }
    
        /**
         * 订阅
         *
         * @param $event
         *
         * @throws \Throwable
         */
        protected function eventSubscribe($event) {
            $openId = $this->openid;
    
            if ($wxUser = WxUser::whereOpenid($openId)->first()) {
                // 标记前端可登陆
                $this->markTheLogin($event, $wxUser->uid);
    
                return;
            }
    
            // 微信用户信息
            $wxUser = $this->app->user->get($openId);
            // 注册
            $nickname = $this->filterEmoji($wxUser['nickname']);
    
            $result = DB::transaction(function () use ($openId, $event, $nickname, $wxUser) {
                $uid  = Uuid::uuid4()->getHex();
                $time = time();
    
                // 用户
                $user = User::create([
                    'uid'        => $uid,
                    'created_at' => $time,
                ]);
                // 用户信息
                $user->user_info()->create([
                    'email'      => $user->email,
                    'nickname'   => $nickname,
                    'sex'        => $wxUser['sex'],
                    'address'    => $wxUser['country'] . ' ' . $wxUser['province'] . ' ' . $wxUser['city'],
                    'avatar'     => $wxUser['headimgurl'],
                    'code'       => app(UserRegisterController::class)->inviteCode(10),
                    'created_at' => $time,
                ]);
                // 用户账户
                $user->user_account()->create([
                    'gold'       => 200,
                    'created_at' => $time,
                ]);
    
                $wxUserModel = $user->wx_user()->create([
                    'subscribe'      => $wxUser['subscribe'],
                    'subscribe_time' => $wxUser['subscribe_time'],
                    'openid'         => $wxUser['openid'],
                    'created_at'     => $time,
                ]);
    
                Log::info('用户注册成功 openid:' . $openId);
    
                $this->markTheLogin($event, $wxUserModel->uid);
            });
    
            Log::debug('SQL 错误: ', [$result]);
        }
    
        /**
         * 标记可登录
         *
         * @param $event
         * @param $uid
         */
        public function markTheLogin($event, $uid) {
            if (empty($event['EventKey'])) {
                return;
            }
    
            $eventKey = $event['EventKey'];
    
            // 关注事件的场景值会带一个前缀需要去掉
            if ($event['Event'] == 'subscribe') {
                $eventKey = str_after($event['EventKey'], 'qrscene_');
            }
    
            Log::info('EventKey:' . $eventKey, [$event['EventKey']]);
    
            // 标记前端可登陆
            Cache::put(WxUser::LOGIN_WECHAT . $eventKey, $uid, now()->addMinute(30));
        }
    

    前端登录检查

    /**
         * 微信用户登录检查
         *
         * @param Request $request
         *
         * @return bool|\Illuminate\Http\JsonResponse
         */
        public function loginCheck(Request $request) {
            // 判断请求是否有微信登录标识
            if (!$flag = $request->wechat_flag) {
                return $this->ajaxSuccess(false);
            }
    
            // 根据微信标识在缓存中获取需要登录用户的 UID
            $uid  = Cache::get(WxUser::LOGIN_WECHAT . $flag);
            $user = User::whereUid($uid)->first();
    
            if (empty($user)) {
                return $this->ajaxSuccess(false);
            }
    
            // 登录用户、并清空缓存
            auth('web')->login($user);
            Cache::forget(WxUser::LOGIN_WECHAT . $flag);
            Cache::forget(WxUser::QR_URL . $flag);
    
            return $this->ajaxSuccess(true);
        }
    

    OK,很实用的一个功能吧,赶快加到你项目中吧!

    展开全文
  • 点击上方Java后端,选择设为星标优质文章,及时送达作者:destiny链接:segmentfault.com/a/1190000022188562序言常见方式平常大家见到过最多的...
  • 本程序使用php语言实现了通过扫描网站声称的二维码,验证用户是否关注公众号,如关注了则自动登陆网站,否则会先进入公众号关注页面,关注后再登陆网站。简单修改源码中的公众号信息即可接入自己的系统内。
  • 需求 解决方案 步骤 这里我们选择永久二维码 代码见第二章需求 用户通过微信扫码关注公众号获取...扫码是否关注公众号已关注,则微信会将带场景值扫描事件推送给开发者,根据场景判断当前二维码是否失效失效,公众号
  • php语言实现了通过扫描网站的二维码,验证用户是否关注公众号,如关注了则自动登陆网站,否则会先进入公众号关注页面,关注后再登陆网站。
  • 微信公众号开发(1)-实现关注公众号自动登录网站微信公众号初识一、[微信官方文档](https://developers.weixin.qq.com/doc/)**二、公众号申请流程三、公众号管理页面四、必备开发者工具的使用五、细读开发者文档六...
  • PHP原生代码实现微信扫码关注公众号并同时登录,适用于PC段进行扫描登陆。 大家可以下载使用。PHP原生代码实现微信扫码关注公众号并同时登录,适用于PC段进行扫描登陆。 大家可以下载使用。
  • PHP微信扫码登录看起来简单,但做起来有点复杂,微信接口的坑有点多,稍不注意,就费很多时间。PHP判断是否首次关注公众号,获取微信用户头像、openid和省市等信息
  • 碰到这样一个需求,PC端生成公众号二维码,用户用手机微信扫描,如果用户未关注公众号,则关注公众号自动登录,如果用户已经关注过公众号,则直接登录。 前端Vue,后端Java实现 分3步来完成: 1.生成带参数的...
  • PHP判断是否首次关注公众号扫码关注公众号获取微信用户头像、openid和省市等信息源码。 演示体验地址: https://www.skpan.cn/user/login.html 网盘下载地址: https://www.skpan.cn/EA3tZACFyO7 部分截图如下:...
  • 完整封装微信服务号扫码登陆代码,复制修改配置即可使用,带文档说明,流程介绍
  • 本篇介绍的二维码登录不是微信开发平台的二维码登录,而是利用微信公众号临时二维码扫码事件关注公众号进行登录注册, 浏览器判断扫码状态有两种方式, 第一种是ajax每隔一秒进行轮询,如果用户扫码了则后台给个...
  • Java实现微信扫码关注公众号登录网站 准备工作: 申请一个微信公众号并登录,需要用到里面的两个功能—基本配置和开发者工具 核心思想: 一:在微信公众号后台的基本配置里配置好服务器配置,如图: 需要注意的是...
  • 一键关注公众号的代码,原来通过获取 获取自定义菜单配置接口取出当前公众号的唯一标识__biz;(看到网上都没有这方面的资料,大家记得点关注或赞,谢谢) /* * 获取一键关注授权标识 * */ public functi
  •   上周二开晨会分配任务的时候,分配到了一个微信扫码关注公众号的需求。刚开始以为只要截个公众号二维码的图,然后按照UI出的设计稿把二维码放到指定位置,再加上一波加边框加阴影的操作提交就完事了。所以当部门...
  • php微信关注公众号扫码实现获取用户信息(内含简易框架搭建,可复用)php微信关注公众号扫码实现获取用户信息(内含简易框架搭建,可复用)概述》详细流程准备工作准备公众号及配置相关接口文档获取access_token...
  • 二维码关注公众号

    2018-12-03 10:25:09
     网站实现扫描二维码关注微信公众号,如果已经关注公众号自动登陆网站并获取其微信昵称,头像等信息,如果用户未关注就等用户关注公众号自动登陆网站 –如果用户已关注公众号,网站端直接自动登陆,如果没有...
  • 1.__开发前先从公众号查出APPID和APPSECRE 2.注册url的域名: 公众号-设置-公众号设置-功能设置 3.注册的url是你的项目根路径,不是回调路径 4.一般将MP_*****.txt放在web或webapp中,微信会从注册的url中找到这个文件 ...
  • 扫码关注,自动回复文字,合成海报,关键字回复,图文回复等,适用于新手哦

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,820
精华内容 6,328
关键字:

如何扫码自动关注公众号