彻底跨域单点登陆解决方案[php+redis+p3p协议]

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

cookie跨域:让不一样域下的session cookie有着一样的session idjavascript

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

彻底跨域的网站架构和业务分配在平常生活中并很多见,你天天上淘宝去支付宝付款,还有我常常上的大B站,出来个biligame的业务,将原来B站的跨二级域单点登陆提高到了彻底跨域的高度html

=====================================================================java

一、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域对外域请求作合法性检查的事情了redis

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

    

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

    

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

=====================================================================cookie

二、session共享

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

    二级跨域只须要把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,但会选取最长匹配域做为最后值 )

    

    能够看出相同键值下会作域的最长匹配,这其实也是二级跨域时为何要把服务器的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模式,很简单,没什么可讲的

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

三、代码实现

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>

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

效果

 

能够看到asite和bsite的session cookie的session id是相同的,因此两者会读取服务器上的同一session数据

转载请注明来源,谢谢!

相关文章
相关标签/搜索