完全跨域单点登录解决方案[php+redis+p3p协议]

技术要点:COOKIE跨域 + SESSION共享

cookie跨域:让不同域下的session cookie有着同样的session id

session共享:同一会话系统,客户端不同域下的session id相同故可访问相同的会话状态

完全跨域的网站架构和业务分配在日常生活中并不少见,你每天上淘宝去支付宝付款,还有我经常上的大B站,出来个biligame的业务,将原来B站的跨二级域单点登录提升到了完全跨域的高度

=====================================================================

1、p3p协议(cookie跨域的根基)

此协议可以完成cookie的跨域设置,即我在A域下请求B域的某个方法,B域上加载此协议后可以实现通过A域的请求完成cookie设置【当然此cookie的域肯定是B域的】,说的简单些就是A域的cookie只能是在A域下设置,再怎样你也做不到在A域下设置一个B域的cookie,但你可以通过A域向B域发送一个跨域请求,B域响应此请求,虽然此请求来至A域,在某些情况下(IE大法)依旧无法完成cookie的设置,但在P3P协议的帮助下,即B域上运行着P3P协议,就可以完美的接收来至任何他域的请求来设置cookie了【再次强调,B域的cookie只能在B域下设置,若A域下就能设置B域的cookie先不说SOP协议当然无存,我们还没事折腾什么跨域】,门已开,肯定得需要安检,这就涉及到B域对外域请求做合法性检查的事情了

在A域下不能设置域为B域的cookie,域为X的cookie只能在X域下被设置,但此请求可以通过外域发起,A向B发起,B验证接收相关数据设置域为B的cookie,虽然用户目前还没有浏览B,但本地已经生成了B域的cookie

    154130_GkBf_252076.png

    可以看到在此域下设置的外域的cookie没有"生效",可能有人说当前域只能访问当前域下的cookie,很好,我们继续看下面的与此cookie对应的域是否能访问到,再没有那就可以证明了,而且此域下通过script标签发起跨域请求

    154758_X0oV_252076.png

    此域运行了p3p协议,响应了来至www.sallency.com的请求,根据传递的参数设置cookie,这样就完成了cookie跨域设置,即我并没有访问www.yii.com,但我客户端已经生成一个此域的cookie,是我从www.sallency.com跨域完成生成的

=====================================================================

2、session共享

    session共享的根本所在就是要让每个域下的session_id相同

    二级跨域只需要把session cookie domain设定为一级域,这样每个二级域都使用此一级域的session cookie作为session id去读取服务器端的会话状态,就实现了单点登录

    完全跨域则需要跨域设置相同的session cookie

    session是存储在服务器端与用户本地的session cookie挂钩的用户对话数据,当我们每次向服务器发送请求时都会携带本地的cookie,服务端session_start后会检查请求中有无session cookie的键值,有则根据此键值去session系统读取对应的数据载入内存,就形成了$_SESSION超全局变量,若没有则在客户端本地生成域为当前域的session cookie(注意cookie的匹配策略同样为最长匹配,www.sallency.com可访问域为www.sallency.com和.sallency.com的cookie,但会选取最长匹配域作为最后值 )

    155527_5CH9_252076.png

    可以看出相同键值下会做域的最长匹配,这其实也是二级跨域时为什么要把服务器的session.cookie_domain设定为父域,否则每次请求服务器session_start时服务器会按session.cookie_domain的域去检测有没有此域的session cookie的键值,有那就是session_id,没有服务器会设置一个当前二级域的session cookie,每次请求的session id就成为此二级域自己重新生成的,那就达不到我们目的了,我们的目的是服务器将一级域作为session cookie的域,这样不论哪个子域都会把域为一级域的session cookie的session_id作为键值去读取服务器端的session

    session共享一般是使用数据库模式,共用一个数据库系统作为session系统,我为了方便使用的redis模式,很简单,没什么可讲的

=====================================================================

3、代码实现

www.asite.com

index.php 主界面

<?php
    error_reporting(E_ALL);
    //使用redis做session共享
    ini_set('session.save_handler', 'redis');
    ini_set('session.save_path', 'tcp://192.168.30.154:6379');
    session_start();
    //退出 销毁服务器端的session 状态完全同步 其他站点也会退出登录
    if (isset($_GET['act'])) {
        session_destroy();
        header("location:index.php");
    }

    //添加新的session 用以验证session完全同步
    if (isset($_POST['add'])) {
        $_SESSION[$_POST['key']] = $_POST['value'];
        header("location:index.php");
    }

?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>www.asite.com</title>
    </head>
    <body>
    <?php
        if (isset($_SESSION['user'])) {
    ?>
    
        <div>welcome!<?php echo $_SESSION['user']?></div>
        <div>
            <h4>session完全同步验证</h4>
            <?php foreach ($_SESSION as $key => $value): ?>
                <p><?php echo $key . '--' . $value?></p>
            <?php endforeach ?>
        </div>
        <div>
            <h4>添加新的session键值</h4>
             <form action="" method="post">
                <input type="text" name="key" placehodler="session key">
                <input type="text" name="value" placehodler="session value">
                <input type="submit" value="Add" name="add">
            </form>
        </div>
        <a href="index.php?act=logout">退出</a>
    <?php
        } elseif (isset($_POST['submit'])) {
            //用户登录
            $_SESSION['user'] = $_POST['user'];
            $_SESSION['passwd'] = $_POST['passwd'];
            //同时触发cookie跨域设置的请求
            header("location:p3p.php");
        } else {
    ?>
        <div>
            <h4>登录<h4>
            <form action="" method="post">
                <input type="text" name="user" placehodler="username">
                <input type="text" name="passwd" placehodler="userpasswd">
                <input type="submit" value="Login" name="submit">
            </form>
        </div>
    <?php
        }
    ?>
    </body>
</html>

p3p.php 完成跨域cookie设置请求

asite肯定是请求bsite设置好和自己session name session id相同的session cookie

<?php
    header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
    if (isset($_GET['sessname']) && isset($_GET['sessid'])) {
        // cross domain request from www.bsite.com
        setcookie($_GET['sessname'], $_GET['sessid'], time() + 3600, '/', 'www.asite.com');
        header("location:index.php");
    }
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>asite cross domain set bsite cookie</title>
    <?php
        //在这里我没有做session redis共享的一些初始化设置,因为我不并不去读取当前session的数据内容
        //我只需把session name 和 session id 跨域请求并传递给bsite,其会根据我传递的参数在其域下设定相同的session cookie
        session_start();
        echo "<script src='http://www.bsite.com/p3p.php?sessid=" . session_id() . "&sessname=". session_name() ."'></script>";
    ?>
</head>
<body>
    <div style="text-align: center">
        <p>success login</p>
    </div>
</body>
<script type="text/javascript">
    window.onload = function() {
        setTimeout(function(){
            window.location.replace('index.php');
        }, 1000);
    }
</script>
</html>

=====================================================================

www.bsite.com

    index.php 与asite index.php不同之处就在于页面title不一样...方便识别嘛....

<?php
    error_reporting(E_ALL);
    //使用redis做session共享
    ini_set('session.save_handler', 'redis');
    ini_set('session.save_path', 'tcp://192.168.30.154:6379');
    session_start();
    //退出 销毁服务器端的session 状态完全同步 其他站点也会退出登录
    if (isset($_GET['act'])) {
        session_destroy();
        header("location:index.php");
    }

    //添加新的session 用以验证session完全同步
    if (isset($_POST['add'])) {
        $_SESSION[$_POST['key']] = $_POST['value'];
        header("location:index.php");
    }

?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>www.bsite.com</title>
    </head>
    <body>
    <?php
        if (isset($_SESSION['user'])) {
    ?>
    
        <div>welcome!<?php echo $_SESSION['user']?></div>
        <div>
            <h4>session完全同步验证</h4>
            <?php foreach ($_SESSION as $key => $value): ?>
                <p><?php echo $key . '--' . $value?></p>
            <?php endforeach ?>
        </div>
        <div>
            <h4>添加新的session键值</h4>
             <form action="" method="post">
                <input type="text" name="key" placehodler="session key">
                <input type="text" name="value" placehodler="session value">
                <input type="submit" value="Add" name="add">
            </form>
        </div>
        <a href="index.php?act=logout">退出</a>
    <?php
        } elseif (isset($_POST['submit'])) {
            //用户登录
            $_SESSION['user'] = $_POST['user'];
            $_SESSION['passwd'] = $_POST['passwd'];
            //同时触发cookie跨域设置的请求
            header("location:p3p.php");
        } else {
    ?>
        <div>
            <h4>登录<h4>
            <form action="" method="post">
                <input type="text" name="user" placehodler="username">
                <input type="text" name="passwd" placehodler="userpasswd">
                <input type="submit" value="Login" name="submit">
            </form>
        </div>
    <?php
        }
    ?>
    </body>
</html>

p3p.php 完成跨域cookie设置请求 bsite肯定是请求asite设置好和自己session name session id相同的session cookie

<?php
    header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
    if (isset($_GET['sessname']) && isset($_GET['sessid'])) {
        // cross domain request from www.asite.com
        setcookie($_GET['sessname'], $_GET['sessid'], time() + 3600, '/', 'www.bsite.com');
        header("location:index.php");
    }
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>bsite cross domain et asite cookie</title>
    <?php
        //在这里我没有做session redis共享的一些初始化设置,因为我不并不去读取当前session的数据内容
        //我只需把session name 和 session id 跨域请求并传递给asite,其会根据我传递的参数在其域下设定相同的session cookie
        session_start();
        echo "<script src='http://www.asite.com/p3p.php?sessid=" . session_id() . "&sessname=". session_name() ."'></script>";
    ?>
</head>
<body>
    <div style="text-align: center">
        <p>success login</p>
    </div>
</body>
<script type="text/javascript">
    window.onload = function() {
        setTimeout(function(){
            window.location.replace('index.php');
        }, 1000);
    }
</script>
</html>

=====================================================================

效果

170729_R7B1_252076.png

 

170747_gK5x_252076.png

170747_O5tO_252076.png

170747_pXcL_252076.png

170747_2gFs_252076.png

可以看到asite和bsite的session cookie的session id是相同的,所以二者会读取服务器上的同一session数据

170747_ao3h_252076.png

转载请注明来源,谢谢!

转载于:https://my.oschina.net/sallency/blog/529005

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值