fecshop google 登陆原理

技术分享 · Fecmall · 于 5年前 发布 · 2513 次阅读

关于google登陆参看:Fecshop Facebook Google登录

http://fecshop.appfront.fancyecommerce.com/customer/account/login 页面可以看到

<img onclick="googlelogin()" src="//img.fancyecommerce.com/images/google.jpg">

<script type="text/javascript">
    var newwindow;
    var intId;
   
    function googlelogin(){
        var  screenX    = typeof window.screenX != 'undefined' ? window.screenX : window.screenLeft,
             screenY    = typeof window.screenY != 'undefined' ? window.screenY : window.screenTop,
             outerWidth = typeof window.outerWidth != 'undefined' ? window.outerWidth : document.body.clientWidth,
             outerHeight = typeof window.outerHeight != 'undefined' ? window.outerHeight : (document.body.clientHeight - 22),
             width    = 800,
             height   = 650,
             left     = parseInt(screenX + ((outerWidth - width) / 2), 10),
             top      = parseInt(screenY + ((outerHeight - height) / 2.5), 10),
             features = (
                'width=' + width +
                ',height=' + height +
                ',left=' + left +
                ',top=' + top
              );

        newwindow=window.open('https://accounts.google.com/o/oauth2/auth?response_type=code&redirect_uri=http%3A%2F%2Ffecshop.appfront.fancyecommerce.com%2Fcustomer%2Fgoogle%2Floginv&client_id=380372364773-qdj1seag9bh2n0pgrhcv2r5uoc58ltp3.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&access_type=offline&approval_prompt=auto   ','Login_by_facebook',features);

       if (window.focus) {newwindow.focus()}
      return false;
    }
 </script>


也就是点击后,弹出来一个框,跳转到google的登陆页面进行授权,可以看到传递的参数有授权成功后的url: redirect_uri=http%3A%2F%2Ffecshop.appfront.fancyecommerce.com%2Fcustomer%2Fgoogle%2Floginv

也就是/customer/google/loginv

就是fecshop的

@fecshop/app/appfront/modules/Customer/controllers/GoogleController.php

/**
     * google登录确认成功后,返回的url
     * 通过下面,得到用户的email,first_name,last_name
     * 然后登录。
     * 由于阿里云是国内服务器,暂时还没有具体测试,这个需要
     * 用国外的服务器才可以。因为需要服务器方面访问google的接口。国内服务器会被墙的。
     */
    public function actionLoginv()
    {
        Yii::$service->session->set('logintype', 'google');
        $thirdLogin = Yii::$service->store->thirdLogin;
        global $googleapiinfo;
        $googleapiinfo['GOOGLE_CLIENT_ID'] = isset($thirdLogin['google']['CLIENT_ID']) ? $thirdLogin['google']['CLIENT_ID'] : '';
        $googleapiinfo['GOOGLE_CLIENT_SECRET'] = isset($thirdLogin['google']['CLIENT_SECRET']) ? $thirdLogin['google']['CLIENT_SECRET'] : '';
        $lib_google_base = Yii::getAlias('@fecshop/lib/google');
        include $lib_google_base.'/Social.php';
        $urlKey = 'customer/google/loginv';
        $redirectUrl = Yii::$service->url->getUrl($urlKey);
        $Social_obj = new \Social($redirectUrl);
        $user = $Social_obj->google();
        // 服务器放到国外才行。不然上面无法返回数据。
        if (is_array($user) && !empty($user)) {
            $fullname = $user['name'];
            $email = $user['email'];
            if ($email) {
                $this->accountLogin($fullname, $email);
            }
        }
    }

/customer/google/loginv 部分,获取email 和user name,这里使用的是google的php sdk。

细节需要自己研究

共收到 15 条回复
xuhuaiqu#15年前 0 个赞

这里accountLogin后,并没能拿到access_token呢。前端(vue)是如何获取身份认证令牌access token的呢

Fecmall#25年前 0 个赞

@xuhuaiqu #1楼 代码位置都告诉你了,自己研究

xuhuaiqu#35年前 0 个赞

@Fecshop [#2楼](#comment2) 查看前端代码 可以看到请求/customer/google/loginv 之后 即保存了返回header中的access-token


var root = process.env.API_ROOT;

export default {
    data () {
        return {
            pageInitUrl: root + '/customer/google/loginv' ,
            errormsg:'',
            refer_url: '',
            correctmsg:''
        }
    },
    created: function(){
        this.pageInit();
    },
    beforeRouteEnter (to, from, next) {
        var website_root = process.env.WEBSITE_ROOT
        var fullPath = from.fullPath
        var name = from.name
        console.log(fullPath);
        console.log(from);  
        if (fullPath !== '/' || typeof(name) === 'undefined' ) {
            var referUrl = website_root + "/#" + fullPath
            console.log(referUrl)
            
        } else {
            referUrl = ''
        }
        next( vm => {
            vm.refer_url = referUrl;
        });  
    },
    methods: {
        pageInit: function(){
            var self = this;
            self.errormsg = '';
            self.correctmsg = '';
            var requestParams = this.$route.query;
            var ajaxData = {};
            for(var x in requestParams){
                ajaxData[x] = requestParams[x];
            }
            $.showIndicator();
            var cookies = self.getTraceAllCookie();
            ajaxData["cookies"] = cookies;
            $.ajax({
                url: self.pageInitUrl,
                async: true,
                timeout: 120000,
                type: 'get',
                headers: self.getRequestHeader(),
                data:ajaxData,
                success:function(reponseData, textStatus,request){
                    $.hideIndicator();
                    if(reponseData.code == 200){
                        self.reloadPage();
                        var traceData = {"refer_url": self.refer_url};
                        var routerQ = self.$route.query
                        for (var k in routerQ) {
                            traceData[k] = routerQ[k]
                        }
                        self.reloadTraceJs(traceData);
                        self.saveReponseHeader(request); 
                    }
                },
                error:function(){
                    $.toast('system error');
                    $.hideIndicator();
                    console.log('get address list page init error');
                }
            });
            
        },
        reloadPage: function(){
            window.close();
			window.opener.location.reload();
        }
        
    
    }
    
    
    
}

Vue.prototype.saveReponseHeader = function (response){
    // fecshop-uuid
    var fecshop_uuid = response.getResponseHeader('fecshop-uuid');
    if(fecshop_uuid){
        var local_fecshop_uuid = window.localStorage.getItem("fecshop-uuid");
        if(local_fecshop_uuid != fecshop_uuid){
            window.localStorage.setItem("fecshop-uuid",fecshop_uuid);
            console.log('save header [fecshop-uuid] ######' + fecshop_uuid);
        }
    }
    
    var access_token = response.getResponseHeader('access-token');
    if(access_token){
        console.log('save header [access-token1]' );
        var local_access_token = window.localStorage.getItem("access-token");
        console.log('save header [access-token2]' );
        if(local_access_token != access_token){
            console.log('save header [access-token3] ######' + access_token);
            window.localStorage.setItem("access-token",access_token);
            
        }
    }
    
    
    
}

xuhuaiqu#45年前 0 个赞

以 google为例 可以看到调用了三方注册的函数


    /**
     * google账户登录.
     */
    public function accountLogin($full_name, $email)
    {
        $name_arr = explode(' ', $full_name);
        $first_name = $name_arr[0];
        $last_name = $name_arr[1];
        $user = [
            'first_name'    =>$first_name,
            'last_name'    =>$last_name,
            'email'        =>$email,
        ];
        Yii::$service->customer->registerThirdPartyAccountAndLogin($user, 'google');
        $code = Yii::$service->helper->appserver->status_success;
        $data = [];
        $responseData = Yii::$service->helper->appserver->getResponseData($code, $data);
        
        return $responseData;
        //echo '<script>
		//			window.close();
		//			window.opener.location.reload();
		//		</script>';
        //exit;
    }
xuhuaiqu#55年前 0 个赞

三方注册调用了两个函数来进行登录

    /**
     * @property  $user | Array ,example:
     * ['first_name' => $first_name,'last_name' => $last_name,'email' => $email,]
     * @property  $type | String 代表第三方登录的名称,譬如google,facebook
     * @return bool
     * 如果用户emai存在,则直接登录,成功后返回true
     * 如果用户不存在,则注册用户,然后直接登录,成功后返回true
     */
    protected function actionRegisterThirdPartyAccountAndLogin($user, $type)
    {
        
        // 查看邮箱是否存在
        $email = $user['email'];
        $customer_one = Yii::$service->customer->getUserIdentityByEmail($email);
        if ($customer_one) {
            $loginStatus = \Yii::$app->user->login($customer_one);
            if ($loginStatus) {
                return true;
            }
            // 不存在,注册。
        } else {
            if (!(isset($user['password']) && $user['password'])) {
                $user['password'] = $this->getRandomPassword();
            }
            $registerData = [
                'email'       => $email,
                'firstname'   => $user['first_name'],
                'lastname'    => $user['last_name'],
                'password'    => $user['password'],
                'type'        => $type,
            ];
            $registerStatus = Yii::$service->customer->register($registerData);
            if ($registerStatus) {
                $loginStatus = Yii::$service->customer->login($registerData);
                if ($loginStatus) {
                    return true;
                }
            }
        }

        return false;
    }
xuhuaiqu#65年前 0 个赞

一个是YII2.0的原生登录方式

    public function login(IdentityInterface $identity, $duration = 0)
    {
        if ($this->beforeLogin($identity, false, $duration)) {
            $this->switchIdentity($identity, $duration);
            $id = $identity->getId();
            $ip = Yii::$app->getRequest()->getUserIP();
            if ($this->enableSession) {
                $log = "User '$id' logged in from $ip with duration $duration.";
            } else {
                $log = "User '$id' logged in from $ip. Session not enabled.";
            }

            $this->regenerateCsrfToken();

            Yii::info($log, __METHOD__);
            $this->afterLogin($identity, false, $duration);
        }

        return !$this->getIsGuest();
    }
xuhuaiqu#75年前 0 个赞

一个是customer service的方式

    protected function actionLogin($data)
    {
        $model = new $this->_customerLoginModelName();
        $model->email       = $data['email'];
        $model->password    = $data['password'];
        $loginStatus        = $model->login();
        $errors             = $model->errors;
        if (empty($errors)) {
            // 合并购物车数据
            Yii::$service->cart->mergeCartAfterUserLogin();
            // 发送登录信息到trace系统
            Yii::$service->page->trace->sendTraceLoginInfoByApi($data['email']);
        } else {
            Yii::$service->helper->errors->addByModelErrors($errors);
        }

        return $loginStatus;
    }
xuhuaiqu#85年前 0 个赞

问题在于 这两种方式 都是仅仅返回的 我登录成功了 这种状态。 并没有像邮件登录时 进行header头的设置。我也直接使用的actionRegisterThirdPartyAccountAndLogin 来完成的三方登录。这个流程我是理解的。我已经完成了回调客户端,以及客户端请求/customer/google/loginv 这一步,也成功的获取了三方的username等信息。问题是这个新形成的用户的accss token 在这个registerThirdPartyAccountAndLogin 并未设置。所以困惑在这个点。

    protected function actionLoginAndGetAccessToken($email, $password)
    {
        $header = Yii::$app->request->getHeaders();
        if (isset($header['access-token']) && $header['access-token']) {
            $accessToken = $header['access-token'];
        }
        // 如果request header中有access-token,则查看这个 access-token 是否有效
        if ($accessToken) {
            $identity = Yii::$app->user->loginByAccessToken($accessToken);
            if ($identity !== null) {
                $access_token_created_at = $identity->access_token_created_at;
                $timeout = Yii::$service->session->timeout;
                if ($access_token_created_at + $timeout > time()) {
                    return $accessToken;
                }
            }
        }
        // 如果上面access-token不存在
        $data = [
            'email'     => $email,
            'password'  => $password,
        ];
        
        if (Yii::$service->customer->login($data)) {
            $identity = Yii::$app->user->identity;
            $identity->generateAccessToken();
            $identity->access_token_created_at = time();
            $identity->save();
            // 执行购物车合并等操作。
            Yii::$service->cart->mergeCartAfterUserLogin();
            $this->setHeaderAccessToken($identity->access_token);
            return $identity->access_token;
        }
    }
Fecmall#95年前 0 个赞

不需要参考google的思路做,按照微信的登陆方式做就好

仔细看去微信的文档步骤

xuhuaiqu#105年前 0 个赞

@Fecshop #9楼 ok 我重载registerThirdPartyAccountAndLogin吧 谢谢

alibo1016#115年前 0 个赞

@xuhuaiqu #10楼 我也有相同的疑问,你后来是自己加access_token的header的吗。

xuhuaiqu#125年前 1 个赞

@alibo1016 [#11楼](#comment11) 重写的函数

    /**
     * @property  $user | Array ,example:
     * ['open_id' => $open_id,'nickname' => $nickname,'birthday' => $birthday]
     * @property  $type | String 代表第三方登录的名称,譬如google,facebook, wechat
     * @return bool
     * 如果用户open_id存在,则直接登录,成功后返回true
     * 如果用户不存在,则注册用户,然后直接登录,成功后返回true
     */
    protected function actionRegisterThirdPartyAccountAndLogin($user, $type)
    {
        // 查看OpenId是否存在
        $open_id = $user['open_id'];
        $customer_one = Yii::$service->customer->getUserIdentityByOpenId($open_id);
        if ($customer_one) {
            $loginStatus = \Yii::$app->user->login($customer_one);
            if ($loginStatus) {
                $customer_one->generateAccessToken();
                $customer_one->access_token_created_at = time();
                $customer_one->save();
                
                return $this->setHeaderAccessToken($customer_one->access_token);
            }
        } else {
            Yii::info('三方不存在,注册', 'fecshop_debug');
            // 不存在,注册。
            if (!(isset($user['password']) && $user['password'])) {
                $user['password'] = $this->getRandomPassword();
            }

            $registerData = [
                'nickname'    => $user['nickname'],
                'firstname'   => '',
                'lastname'    => '',
                'email'       => 'yaji' . time() . rand(1000, 9999) . '@sns.user',
                'password'    => $user['password'],
                'open_id'     => $user['open_id'],
                'avatar'      => $user['avatar'],
                'type'        => $type,
            ];

            $registerStatus = Yii::$service->customer->register($registerData);
            Yii::info('三方注册结果' . json_encode($registerStatus), 'fecshop_debug');
            if ($registerStatus) {
                Yii::info('三方成功后使用注册用户进行登录', 'fecshop_debug');
                return Yii::$service->customer->loginAndGetAccessToken($registerData['email'], $registerData['password']);
            }
        }

        return false;
    }

alibo1016#135年前 0 个赞

@xuhuaiqu #12楼 谢谢 我后面也是这么处理的

Fecmall#145年前 0 个赞

@all 各位不好意思,我今天有时间,仔细看了您发的帖子,的确存在这个问题,我把代码修正一下

多谢您提的bug

(不好意思,论坛的无脑帖子太多,回复这类帖子多了,我有时候也会先入为主的回复帖子,致歉!)

Fecmall#155年前 1 个赞

代码已经提交:https://github.com/fecshop/yii2_fecshop/commit/9606f675101cbacef62db31c92ff34122bf07bca

@xuhuaiqu #12楼 您看看是否有问题,多谢您的bug提交,再次感谢。

添加回复 (需要登录)
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册
Your Site Analytics