周梦康 发表于 2014-06-12 3149 次浏览 标签 : SSO

工作笔记 - 简单的单点登陆实现

A站的登陆或者注册都在Modules\Member\Action\PublicAction.class.php里面doRegisterdoLogin两个方法里面都调用了下面这个函数

function sso_sync($email,$password){
    $flag = md5('test');//防止别人通过接口恶意注册

    $username = M('member')->where(array('email'=>$email))->field('username')->find();

    $tmp_left = "";
    $tmp_right = "";
    for ($i = 0; $i < 6; $i++){
        $tmp_left .= chr(rand(97, 122));
        $tmp_right .= chr(rand(97, 122));
    }
    $name = $tmp_left.$username['username'].$tmp_right;
    
    $cookie = base64_encode(serialize($name.md5(time())));
    //得到的cookie类似于
    //czo1MzoiY3hndHB65bq35qKm5ZGoY3J2Y3pnMDgzNmI2OWFjMzc0ODcxODg1NDgwZTRkOTFhNThmYTIiOw
    //这样暴力破解不是那么简单吧?我们小站应该没人会来研究,感觉应该OK了
    setcookie("sso_sync",$cookie, time()+3600*24,"/",".quankr2.com");

    //通过问答那边提供的api来对那边进行注册
    $ch = curl_init();

    $data = array(
        'username' => $username['username'],
        'email' => $email,
        'password'=> $password,
        'cookie'=>$cookie,
        'flag'=>$flag
    );

    curl_setopt($ch, CURLOPT_URL, 'http://wenda.quankr2.com/?/account/ajax/register_sync_check/');
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

    curl_exec($ch);
    curl_close($ch);
}

这样访问了B站的同步登陆api:http://wenda.quankr2.com/?/account/ajax/register_sync_check/,访问授权这里用简单的$_POST['flag']作为验证,实际应该更加复杂。

/**
 * 主站注册或者登陆,问答同步注册检测
 * 暂时不考虑用户已经存在的情况,因为是两边相互同步的
 */
public function register_sync_check_action(){
    if($_POST['flag'] != md5('test')){
        exit;
    }
    if($uid = $this->model('account')->check_username($_POST['username'])){}else{
        //插入用户,并更新
        $uid = $this->model('account')->user_register($_POST['username'], $_POST['password'], $_POST['email']);
    }
    //更新用户表里面的cookie字段
    $this->model('account')->update_user_cookie($_POST['cookie'], $uid);
}

这样就触发了对B站用户表的检测,在客户端存储的cookie,并且存入B站数据库。

B站打开的时候如果有sso_sync,则自动发送ajax请求登陆。

//发送自动登陆请求
var loginFlag = "<?php echo $_SESSION['jsn__Anwsion']['client_info']['__CLIENT_UID']; ?>";
if($.cookie('sso_sync') && !parseInt(loginFlag)){
    var url = "/?/account/ajax/sso_auto_login/";
    $.post(url,{cookie:$.cookie('sso_sync')},function(data){
        if(data.rsm!= null && data.rsm.url){
            window.location = decodeURIComponent(data.rsm.url);
        }else{
            window.location.reload();
        }
    },'json');
}

然后在B站打开的时候根据cookie去判断这个cookie对应的用户在数据库里存储的cookie信息是否一致,如果一致,就自动登录。

public function sso_auto_login_action(){

    //根据cookie判断是否已经在主站登陆过
    $username = substr(unserialize(base64_decode($_POST['cookie'])),6,-38);

    //查询是否与存储的信息相符
    $user_info = $this->model('account')->get_user_info_by_cookie($username, $_POST['cookie']);

    if (! $user_info){
        H::ajax_json_output(AWS_APP::RSM(null, -1, AWS_APP::lang()->_t('请输入正确的帐号或密码')));
    }else{
    	//服务器登陆操作...
    }
}

由于这里是一级域名是一样的,所以cookie好保存,如果是不通域名的情况,需要存储B站点的cookie,可以参考这篇文章http://blog.163.com/niuzai369@126/blog/static/3743091520122733733736/ 通过P3P协议来添加。

评论列表