登录一个Facebook用户到CakePHP应用程序,如果身份验证和用户存在于数据库


Logging a Facebook user into CakePHP app if authenticated and user exists in DB

我已经构建了一个非常简单的CakePHP应用程序来测试Facebook PHP SDK的可能实现,其中用户可以使用应用程序或Facebook注册,也可以使用应用程序或Facebook登录。但是因为用户可以在应用程序上做事情,所以即使他们通过Facebook帐户登录/注册,他们也会在应用程序上拥有一个用户帐户。

到目前为止,我已经创建了两个方法来处理授权和获取用户信息:
// Asks user to authenticate
public function facebook()
{
    $params = array(
      'redirect_uri' => 'http://domain.com/users/signupfacebook',
      'scope'=> 'email',
      'display' => 'popup'
    );
    $loginUrl = $this->Facebook->Sdk->getLoginUrl($params);
    // Need to know if user is already a user on OUR app
    // If true log them in. If false redirect to signup form like below
    $this->autoRender = false;
    $this->response->header('Location', $loginUrl);
}
// Handle the return
public function signupfacebook()
{
    $user = $this->Facebook->Sdk->getUser();
    if($user)
    {
        try
        {
            $facebookuser = $this->Facebook->Sdk->api('/me');
            $this->set('facebookuser', $facebookuser);
        }
        catch(FacebookApiException $e)
        {
            error_log($e);
            $user = NULL;
        }
   }
   else
   {
        $this->Session->setFlash('An error has occurred! Please try again.');
        $this->redirect(array('action'=>'index'));
   }
}

用户说我想使用Facebook进行身份验证,这使用我的控制器的Facebook()方法,然后它会请求他们的许可,并根据这个操作重定向他们。目前,它只会将他们发送到一个注册页面,其中一些字段是根据他们传递的用户信息预先填充的,如下所示:

<?php echo $this->Form->create('User'); ?>
    <?php echo $this->Form->input('User.email', array('type' => 'text', 'default'=> $facebookuser['email'], 'label' => array('class' => 'placeholder', 'text' => 'Email address') )); ?>
        <?php echo $this->Form->input('User.firstname', array('type' => 'text', 'default'=> $facebookuser['first_name'], 'label' => array('class' => 'placeholder', 'text' => 'Firstname') )); ?>
        <?php echo $this->Form->input('User.lastname', array('type' => 'text', 'default'=> $facebookuser['last_name'], 'label' => array('class' => 'placeholder', 'text' => 'Lastname') )); ?>
        <?php echo $this->Form->input('User.username', array('type' => 'text', 'default'=> $facebookuser['username'], 'label' => array('class' => 'placeholder', 'text' => 'Username') )); ?>
        <?php echo $this->Form->input('User.password', array('value' => '','type' => 'password', 'label' => array('class' => 'placeholder', 'text' => 'Password') )); ?>
    <button type="submit">Create</button>
<?php echo $this->Form->end(); ?>

但是我这里有几个问题:

问题1)如果用户已经在系统中有一个帐户,他们刚刚输入的Facebook凭据,那么它需要处理这个并登录用户。这需要在Facebook端使用什么特殊的东西吗?因为我正在考虑只是搜索Users表中的匹配帐户,然后自动登录,但我没有发现任何在线的例子,一旦FB身份验证已经发生,就验证您自己的用户表。

问题2)可能与上一个问题相关,但是当用户注册一个帐户时,与他们用来登录的Facebook帐户没有关联被保存,所以如果和当他们尝试使用Facebook再次登录时,该关联将不会被看到,除非我搜索DB的类似细节,但再次这似乎非常古怪,而不是正确使用API。从本质上讲,用户只是使用FB来填充表单,而不是实际使用他们的Facebook帐户进行注册。

我可以检查是否相同的电子邮件地址存在于数据库作为一个通过Facebook,但如果他们已经改变了它吗?我考虑过将Facebook用户id存储在一个表中,并将用户id存储在应用程序中,以便存储连接。这是一个解决方案吗?或者是错误的做法?

。一个新的表来存储FB Users和CakePHP App Users之间的链接:

id
user_id (this is the relationship to your USER table)
facebook_user_id

但即使在这样做的时候,我不确定如何利用这一点,以一种有用的方式正确地链接帐户,并通过Facebook正确地处理身份验证,而不是仅仅自动填充表单或登录用户,如果某些详细信息匹配(我确信有潜在的安全风险)。

问题3)当用户通过Facebook被发送回注册表单时,他们会得到以下URL(出于安全原因,注释位已被更改):

http://domain.com/users/signupfacebook?state=dbf2f6dds322b7cb90d71b6c958a001bf4a3ba&code=AQa7-GDsesM0ZfgswjgGF-mMKwjcoboLl19WKprIA2-f4iZzUdsd8o8z-NweYYODzySS3h9GksBw0G9WCXj79Pp-0ldyeIRCOLSt9gfgfsmEFEfOrEO6gm7a0jmDuQvchdsaHaw1QpI8RdGTCrqAPlLzpHGcqiCtBvXjiQtBhQP--tMr8CIlvRwbrJRwiZwF6xo30howlRkVTLddsdDCDt60_KGznPVmEe-T4Qbu9gdkv9v#_=_

根据:https://developers.facebook.com/docs/howtos/login/server-side-login/,我应该在上面发布的两个方法之间创建一个额外的步骤来处理代码并获得访问令牌。

但是我已经访问了填写注册表单所需的所有必要信息,而无需执行OAuth过程的第二阶段并获得访问令牌!所以不确定我是否需要它在这种情况下…还是我?如果是这样,它将如何帮助解决前两个问题?

允许用户通过facebook注册的步骤如下:

完成此任务所使用的表结构如下:

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL,
  `email` varchar(255) NOT NULL,
  `password` varchar(200) NOT NULL,
  `profile_name` varchar(100) NOT NULL,
  `dob` date NOT NULL,
  `phone_number` bigint(20) NOT NULL,
  `location` varchar(200) NOT NULL,
  `user_picture` varchar(200) NOT NULL,
  `profile_view` bigint(20) NOT NULL,
  `facebook_id` bigint(11) NOT NULL,
  `facebook_status` tinyint(4) NOT NULL,
  `login_status` tinyint(4) NOT NULL,
  `status` tinyint(4) NOT NULL,
  `activation_link` varchar(200) NOT NULL,
  `password_link` varchar(200) NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;

首先用下面的代码创建视图文件。我把它放在views/users/signup.ctp

<div class="grid_7 alpha">
    <a href="Javascript:void(0);" onclick="connect_facebook(); return false;">
      Signup with facebook
    </a>
</div>

应用一些CSS使它看起来更漂亮。现在,在相同的视图文件中,在页面底部添加以下代码:

<script type="text/javascript">
    window.fbAsyncInit = function()
    {
        FB.init(
        {
            appId: FACEBOOK_API,
            cookie: true,
            xfbml: true,
            oauth: true
        });
    };
    function connect_facebook()
    {
        FB.login(function(response)
        {
            if (response.authResponse)
            {
                $.ajax(
                {
                    url         : HOST+'users/facebook_signup/'+response.authResponse.userID,
                    dataType    : 'json',
                    beforeSend  : function()
                    {
                        $.blockUI({
                            message: '<img src="'+HOST+'img/popup/pre_code_bg.gif" alt="" />'
                        });
                    },
                    success     : function(data)
                    {
                        $.unblockUI();
                        if(data.status == "false")
                        {
                            FB.logout(function(response)
                            {
                                window.location = HOST+'users/logout';
                            });
                            return false;
                        }
                        else
                        {
                            document.location.reload(true);
                        }
                    }
                });
            }
            else
            {
                FB.logout(function(response)
                {
                    window.location = HOST+'users/logout';
                    alert('User cancelled login or did not fully authorize.');
                });
            }
        });
    }
    (function()
    {
        var e = document.createElement('script'); e.async = true;
        e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
        document.getElementById('fb-signup-root').appendChild(e);
    }());
</script>

只需替换FACEBOOK_API即可。

现在在users_controller文件粘贴下面的代码,

<?php
class UsersController extends AppController
{
    var $name = 'Users';
    function beforeFilter()
    {
        parent::beforeFilter();
        $this->Auth->allow( 'login','signup''facebook_signup' );
    }
    function facebook_signup($facebook_id)
    {
        $this->autoRender = false;
        $this->layout = "ajax";
        App::import('Vendor', 'Facebook', array('file' => 'facebook'.DS.'facebook.php'));
        $response = array();
        $facebook = new Facebook(array
        (
            'appId'  => FACEBOOK_API,
            'secret' => FACEBOOK_SKEY,
        ));
        $facebook_user = $facebook->getUser();
        if ($facebook_user)
        {
            try
            {
                $user_profile = $facebook->api('/me');
            }
            catch (FacebookApiException $e)
            {
                echo '<pre>'.htmlspecialchars(print_r($e, true)).'</pre>';
                $facebook_user = null;
            }
        }
        $find_facebookemail = $this->User->find('first',array
        (
            'conditions' => array
            (
                'User.email' => $user_profile['email'],
                'OR' => array
                (
                    'User.facebook_id' => 0,
                    'User.facebook_id' => $facebook_id
                )
            ),
            'recursive' => -1
        ));
        if(!empty($find_facebookemail))
        {
            $this->data['User']['id'] = $find_facebookemail['User']['id'];
            $this->data['User']['email'] = $find_facebookemail['User']['email'];
            $this->data['User']['name'] = $find_facebookemail['User']['name'];
            $this->data['User']['facebook_id'] = $find_facebookemail['User']['facebook_id'];
            $this->data['User']['location'] = $find_facebookemail['User']['location'];
            $this->data['User']['dob'] = $find_facebookemail['User']['dob'];
            $this->Session->write("Auth.User", $find_facebookemail['User']);
            $this->Session->write("Auth.User.facebook_user", true);
            $response['status'] = "true";
            echo json_encode($response);
            exit;
        }
        else
        {
            $this->data['User']['email'] = $user_profile['email'];
            $this->data['User']['name'] = $user_profile['name'];
            $this->data['User']['facebook_id'] = $user_profile['id'];
            $this->data['User']['location'] = $user_profile['location']['name'];
            $this->data['User']['facebook_status'] = 1;
            $this->data['User']['status'] = 1;
            $dob = explode("/",$user_profile['birthday']);
            $this->data['User']['dob'] = $dob[2].'-'.$dob[0].'-'.$dob[1];
            if($this->User->save($this->data['User']))
            {
                $this->Session->write("Auth.User.id", $this->User->getLastInsertID());
                $this->Session->write("Auth.User.name", $this->data['User']['name']);
                $this->Session->write("Auth.User.email", $this->data['User']['email']);
                $this->Session->write("Auth.User.facebook_id", $this->data['User']['facebook_id']);
                $this->Session->write("Auth.User.location", $this->data['User']['location']);
                $this->Session->write("Auth.User.dob", $this->data['User']['dob']);
                $this->Session->write("Auth.User.facebook_user", true);
                $img = file_get_contents('https://graph.facebook.com/'.$find_facebookemail['User']['facebook_id'].'/picture?width=500&height=500');
                $img_thumbs = file_get_contents('https://graph.facebook.com/'.$find_facebookemail['User']['facebook_id'].'/picture?width=150&height=150');
                $file = "img".DS."user_picture".DS."".$find_facebookemail['User']['facebook_id'].".jpg";
                $file_thumbs = "img".DS."user_picture".DS."thumbs".DS."".$find_facebookemail['User']['facebook_id'].".jpg";
                file_put_contents($file, $img);
                file_put_contents($file_thumbs, $img_thumbs);
                $this->data['User']['id'] = $this->Session->read('Auth.User.id');
                $this->data['User']['user_picture'] = $find_facebookemail['User']['facebook_id'].".jpg";
                $this->User->save($this->data['User']);
                $response['message'] = "You have successfully registered with us.";
                $response['status'] = "true";
                echo json_encode($response);
                exit;
            }
            else
            {
                $response['message'] = "Something is wrong during process.Please try again later.";
                $response['status'] = "false";
                echo json_encode($response);
                exit;
            }
        }
        exit;
    }
}

控制器代码所做的是检查facebook用户的张贴id是否已经注册,然后允许他到login站点并设置cakephp认证。用户会话,如果没有,然后添加用户和他/她的照片与所有其他数据到mysql数据库

现在你有用户进入你的数据库,所以你可以要求使用更改他的密码一旦他/她更改密码,然后他们可以login到你的应用程序没有loginfacebook帐户。

以上代码经过多次全面测试,并正常运行。

数据库表结构:通常你有你的普通用户表的姓名,电子邮件,生日等和一个额外的字段,包含Facebook用户ID。我称之为"facebook_uid"。这将为您提供与Facebook API相关的识别目的。如果您需要在用户注销应用程序时执行某些操作,那么您将需要一个名为"access_token"的附加字段。此访问令牌以及正确的权限将允许您在用户离线时代表用户执行操作,例如在他的墙上发布内容等。

Answer 1.)如果用户已经在你的应用程序上注册了Facebook,你只需要检查Facebook API调用中出现的Facebook用户ID,并将其与你的用户表相匹配。如果找到了,就像平常一样用CakePHP处理本地会话。如果没有找到,这意味着一个新用户,他们将不得不通过注册过程。

答案2.)将手动注册用户和Facebook注册用户相关联的唯一数据是他们的电子邮件。如果本地电子邮件与Facebook电子邮件匹配,这意味着它是同一个用户,您只需要更新数据库上的"facebook_uid"字段,这样下次他登录时,您就可以识别他了。如果Facebook的电子邮件与他在你的网站注册时使用的电子邮件不同,那么你就无法知道这是同一个人。你还可以检查其他信息,如姓名,生日等,但这一点都不可靠,你可能会弄乱用户数据。此外,您不需要2个表。您的原始用户表已经有"facebook_uid"字段。

答案3.)就像我之前解释的那样,只有当你需要在用户的Facebook账户上做一些事情时,你才需要访问令牌,而他已经注销了,比如在他的墙上发帖或点赞。为了获得他的访问令牌,您只需要从您的Facebook API类调用getAccessToken()方法:

$this->Facebook->Sdk->getAccessToken();

这将返回一个长字符串,您可以将其存储在用户表中,然后检索它以在他的Facebook帐户上执行任务。下面是一个基于CodeIgniter的Wall post示例:

$facebook = new Facebook(array(
  'appId'  => 'XXXXXXXXXXXXXXXXXXXX',
  'secret' => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
));
$facebook->setAccessToken($row->access_token); //this access token was previously stored in the database
$args = array(
    'message'   => 'Ya estoy participando en el Concurso - Se la Estrella',
    'link'      => $this->config->item('tabUrl'),
    'name'      => 'Concurso - Se la Estrella de Kreisel',
    'caption'   => '¡Vota por mi video! Tu también puedes participar.',
    'picture'   => site_url().'/assets/main/uploads/captures/'.$row->facebook_id.'.jpg'
);
$post_id = $facebook->api("/me/feed", "post", $args);

另外,我个人对你的注册表单的建议是:不要让用户修改或看到他在表单上的Facebook信息,只需询问他任何你可能需要的Facebook不会提供的额外信息,比如他的电话,公司等。像姓名、姓氏、电子邮件、生日等信息已经在Facebook上了,显示一个已经填满这些数据的表单会让用户感到困惑。如果用户注册了Facebook,你不需要问他很多其他问题,只需要检查用户是否已经存在于你的本地数据库中,如果存在,你就登录他,如果不存在,就保存他的数据。

是的,您将被要求将帐户链接在一起。

我通常为用户添加facebook_id列。所以当我的用户想要使用FB登录或获得正认证响应时

function connectWithFacebook() {
    FB.getLoginStatus(function(response) {
    var uid = '';
    var accessToken = '';
    if (response.status === 'connected') {
        uid = response.authResponse.userID;
        accessToken = response.authResponse.accessToken;
        FB.api('/me', function(response) {
            $.post("http://" + AppSettings.baseURL + "/Users/checkState", 
            {data : response}, 
            function(postResponse){
                if(postResponse == 'registered') {
                    // do after registration stuff
                } else if( postResponse == 'authenticated') {
                        // do after login stuff
                    }
            });
        });
    }
 });
}
FB.Event.subscribe('auth.login', function () {
    connectWithFacebook();
});

控制器侧。

public function checkState() {
    $userExists = $this->User->userExists($this->request->data['id']);
    if (!empty($userExists)) {         
        $this->authorizeUser($userExists);  // Already exisits, login user to our system with id
        echo "authorized";
        return;
    }
    if ($this->request->is('post')) {
        $this->User->create();
        $this->request->data['User']['role_id'] = 4;
        if ($this->User->save($this->request->data, array('validate' => false))) {
            echo "registered";
        }
        return;
    }
}

对于不使用Facebook注册的用户,facebook_id列保持0。

希望对大家有所帮助

相关文章: