php多站点共用seeion实现单点登录

最近闲来无事,总结整理下单点登录的问题。

单点登录的基本原理为:客户端共享sesionid,服务器端共享session信息。通过共同的sessionid在服务器端获得相同session信息,即可达到单点登录(即多站点共享用户信息,一处登录,处处可用)的目的。

单点登录分为两种情况:

一、站点部署在同一个服务器,且使用同一个二级域名

这种情况下,比较好解决。

1、首先解决站点在客户端sessionid(存在cookie中)的共享问题。使用ini_set()函数即可指定cookie的域,如下: ini_set('session.cookie_domain', '.xxxx.com');//设置服务器cookie的域,xxxx为公用二级域名
2、其次解决站点在服务端的session信息的共享。因为站点在同一个服务器,所以生成的session文件是可以公用的,可以直接使用sessionid获取对应的session信息。

二、站点部署在不同的服务器,使用不同域名(多iFrame跳转模式)

这种情况比较复杂,因为站点在不同服务器使用不同域名,在客户端不能使用ini_set设置cookie的作用域,在服务器端也是各自生成自己的session文件,不能共用,但还是用解决的办法。

1、首先解决客户端sessionid同步问题。

假设我们有三个站点,域名分别是aa.com,bb.com,cc.com。我们在aa.com上建立一个共用的登陆入口login.php,三个网站的登陆请求全部跳转到该页面。代码流程如下:

	$back = login($name,$pwd);//执行登陆操作,成功就写入session
    //如果登录成功,进行以下操作流程
    if($back){
        $sessionid = session_id();
        $key = encode($session,$keyword);//生成安全码
        //输出一个登陆成功提示页,并跳转到请求登陆的站点
    }

在登陆成功html提示页面中添加如下代码,利用iframe标签请求需要同步登陆的站点
这里写图片描述
aa.com和cc.com站点的set_cookie.php文件如下

//解密$key
decode($key);
//把当前站点的sessionid设置为传递的sessionid
session_id($_GET['sessionid']);
session_start();
2、解决三个站点服务器端共享session的问题。

前面已经说过,因为三个站点不在同一个服务器,因此会生成各自的session文件,如果想要共享这些文件,又面临跨域等一系列问题。所以我们转化思路,不使用文件保存session信息,而是把session信息保存到数据库中。这样,只要获得session信息的sessionid,任何站点都可以访问相同的session信息。

我们创建一个mysql_session.php文件,用于存储session信息到数据库,代码如下

$gb_DBname="test";                        //数据库名称 
$gb_DBuser="root";                        //数据库用户名称 
$gb_DBpass="";                            //数据库密码 
$gb_DBHOSTname="127.0.0.1";               //主机的名称或是IP地址 
$SESS_DBH="";                           //数据库对象
session_module_name("User");            //定义session存储按用户定义的方式
$SESS_LIFE=get_cfg_var("session.gc_maxlifetime");//得到session的最大有效期,也可以自定义
 
function sess_open($save_path,$session_name)
{ 
    global $gb_DBHOSTname,$gb_DBname,$gb_DBuser,$gb_DBpass,$SESS_DBH; 
    if(!$SESS_DBH=mysql_pconnect($gb_DBHOSTname,$gb_DBuser,$gb_DBpass)){ 
    echo "MySql Error:".mysql_error().""; 
    die(); 
    } 
    if(!mysql_select_db($gb_DBname,$SESS_DBH)){ 
    echo "MySql Error:".mysql_error().""; 
    die(); 
    } 
    return true; 
} 
function sess_close(){ 
return true; 
} 
function sess_read($key)
{ 
    global $SESS_DBH,$SESS_LIFE; 
    $qry="select value from db_session where sesskey = '$key' and expiry > ".time(); 
    $qid=mysql_query($qry,$SESS_DBH); 
    if(list($value)=mysql_fetch_row($qid)){ 
    return $value; 
    } 
    return false; 
}
 
//写入session信息。保存session信息的数据表名为:db_session
//除了主键自增id,需要的字段如下
//sesskey   sessionid
//values    session值
//expiry    session的到期日期
 
function sess_write($key,$val)
{ 
    global $SESS_DBH,$SESS_LIFE; 
    $expiry=time()+$SESS_LIFE; 
    $value=$val; 
    $qry="insert into db_session values('$key',$expiry,'$value')"; 
    $qid=mysql_query($qry,$SESS_DBH); 
    if(!$qid){ 
    $qry="update db_session set expiry=$expiry, value='$value' where sesskey='$key' and expiry >".time(); 
    $qid=mysql_query($qry,$SESS_DBH); 
    } 
    return $qid; 
} 
function sess_destroy($key)
{ 
    global $SESS_DBH; 
    $qry="delete from db_session where sesskey = '$key'"; 
    $qid=mysql_query($qry,$SESS_DBH); 
    return $qid; 
} 
function sess_gc($maxlifetime)
{ 
    global $SESS_DBH; 
    $qry="delete from db_session where expiry < ".time(); 
    $qid=mysql_query($qry,$SESS_DBH); 
    return mysql_affected_rows($SESS_DBH); 
} 
 
session_set_save_handler("sess_open","sess_close","sess_read","sess_write","sess_destroy","sess_gc");

之后在需要使用session的页面中,在session_start()之前引入该文件,其他的跟平时使用seesion一样就可以了。你会发现你赋值的session已经被存进了数据库中。

三、站点部署在不同的服务器,使用不同域名(CAS模式)

方法二中,在一个站点登录的时候,我们通过iframe来设置了其他几个需要同步登陆的站点的cookie为同一个值,可以从数据库中查询到同一个sessionid值,这就实现了单点登录。
弊端是,当站点较多时,要一个个添加iframe和其他代码,扩展不方便。
此时我们可以使用CAS模式,基本流程如下:

1、用户访问app系统,app系统是需要登录的,但用户现在没有登录。
2、跳转到CAS server(带上app系统的网址作为sso回跳地址),即SSO登录系统,以后图中的CAS Server我们统一叫做SSO系统。 SSO系统也没有登录,弹出用户登录页。
3、用户填写用户名、密码,SSO系统进行认证后,将登录状态写入SSO的session,浏览器(Browser)中写入SSO域下的Cookie。
4、SSO系统登录完成后会生成一个ST(Service Ticket),然后跳转到app系统,同时将ST作为参数传递给app系统。
5、app系统拿到ST后,从后台向SSO发送请求,验证ST是否有效。
6、验证通过后,app系统将登录状态写入session并设置app域下的Cookie。

至此,跨域单点登录就完成了。以后我们再访问app系统时,app就是登录的。接下来,我们再看看访问app2系统时的流程。

1、用户访问app2系统,app2系统没有登录,跳转到SSO。
2、由于app系统请求SSO登录系统后,浏览器已经记录SSO系统的Cookie值,此时SSO系统已经登录了,不需要重新登录认证。
3、SSO生成ST,浏览器跳转到app2系统,并将ST作为参数传递给app2。
4、app2拿到ST,后台访问SSO,验证ST是否有效。
5、验证成功后,app2将登录状态写入session,并在app2域下写入Cookie。

这样,app2系统不需要走登录流程,就已经是登录了。SSO,app和app2在不同的域,它们之间的session不共享也是没问题的。

有的同学问我,SSO系统登录后,跳回原业务系统时,带了个参数ST,业务系统还要拿ST再次访问SSO进行验证,觉得这个步骤有点多余。他想SSO登录认证通过后,通过回调地址将用户信息返回给原业务系统,原业务系统直接设置登录状态,这样流程简单,也完成了登录,不是很好吗?

其实这样问题时很严重的,如果我在SSO没有登录,而是直接在浏览器中敲入回调的地址,并带上伪造的用户信息,是不是业务系统也认为登录了呢?这是很可怕的。

猜你喜欢

转载自blog.csdn.net/u012830303/article/details/82252708
今日推荐