前言:承接上文 redis的详解和项目应用之PHP操作总结
三、项目应用
3.1 项目介绍
此项目为了实现单点登录(Single Sign On),简称为 SSO,是比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
单点登录使用的是session共享技术,然而session是不能跨服务器的,但是将session存储在一台 redis/memcache 缓存服务器上就可以实现跨服务器session信息共享,实现过程如下图所示:
过程说明:
需要准备一台web服务器用来做一个授权登录系统,然后再准备几台其他站点的web服务器。实现是通过登录后的session信息存储到redis中,并且通过跳转将SESSIONID传递到相关的各个系统站点的cookie中来记录登录状态,通过cookie中的SESSIONID请求登录服务器来获取存储到redis中的用户信息,即实现单点登录。
3.2 前期配置
1、准备一台登录服务器,安装 redis-server,在ubuntu服务器执行
apt install redis-server
安装需要配置远程连接,建议可看我之前发布的文章 redis的详解和项目应用之数据类型_m0_68949064的博客-CSDN博客
2、 将登录服务器和其他web服务器上的PHP都安装上 php-redis 扩展,在ubuntu服务器上执行
# 依照个人的php版本进行安装redis扩展
apt install php7.2-redis
3、修改php.ini来配置将session存储到远程redis中
# 修改php.ini文件中 [Session] 下的配置
session.save_handler = redis
session.save_path = "tcp://ip:6379?auth=YOURPASSPHRASE"
3.3 实现结果
代码:
登录服务器的 config.php 文件
<?php
/**
* 配置文件,安装个人项目的配置来
*/
//同步登录web域名数组
$synUrlAll = [
web1,
web2,
];
//认证中心的web域名
$centerUrl = centerUrl;
//私钥
$privateKey = 'redisKey';
//redis配置
$redisConfig = [
'host' => centerUrl,
'port' => '6379',
'timeout' => '604800',
'auth_pass' => YOURPASSPHRASE,
];
登录服务器的 center.php 文件
<?php
header('Content-Type:application/json; charset=utf-8');
error_reporting(E_ERROR | E_CORE_ERROR | E_PARSE);
include_once "./config.php";
/**
* 登录认账中心
*/
class center
{
protected $synUrlAll;
protected $centerUrl;
protected $privateKey;
protected $redisConfig;
function __construct($synUrlAll, $centerUrl, $privateKey, $redisConfig)
{
$this->synUrlAll = $synUrlAll;
$this->centerUrl = $centerUrl;
$this->privateKey = $privateKey;
$this->redisConfig = $redisConfig;
}
public function toLogin()
{
$data = $_POST;
if(!isset($data['username']) || !$data['username']){
return $this->returnJosn(500, '缺少username参数');
}
if(!isset($data['passwd']) || !$data['passwd']){
return $this->returnJosn(500, '缺少passwd参数');
}
// 此处登录验证省略,并假设用户ID为1
$userInfo = [
'uid' => 1,
'username' => $data['username'],
'passwd' => $data['passwd'],
];
$login_key = 'PHPREDIS_SESSION:'.session_id();
$_SESSION['uid'] = 1;
$_SESSION['userInfo'] = $userInfo;
setcookie('login_key', $login_key, time()+3600*24, '/');
return $this->synAllWeb($login_key);
//return $this->returnJosn(200, '登录成功');
}
//同步登录
public function synLogin()
{
$data = $_GET;
if(!isset($data['login_key']) || !$data['login_key']){
return $this->returnJosn(500, '缺少login_key参数');
}
$login_key = base64_decode($data['login_key']);
$login_key = substr($login_key, 0, strlen($login_key)-strlen($this->privateKey));
setcookie('login_key', $login_key, time()+3600*24, '/');
header("Location:".base64_decode($data['returnUrl']).$data['urlAll']);
exit;
}
//将登录的sessionid同步至其他网站
public function synAllWeb($login_key = '')
{
if(!$login_key){
$login_key = $_COOKIE['login_key'];
}
if(isset($_GET['urlAll'])){
$urlAll = isset($_GET['urlAll']) ? trim(base64_decode($_GET['urlAll'])) : '';
$urlAll = json_decode($urlAll, true);
}else{
$urlAll = $this->synUrlAll;
}
if(is_array($urlAll) && !empty($urlAll)){
sort($urlAll);
$url = $urlAll[0];
unset($urlAll[0]);
header("Location:http://".$url."/web.php?act=synLogin&login_key=".base64_encode($login_key.$this->privateKey)."&urlAll=".base64_encode(json_encode($urlAll))."&returnUrl=".base64_encode("http://".$this->centerUrl."/redisObj/center.php?act=synAllWeb&urlAll="));
exit;
}else{
return $this->isLogin();
}
}
public function isLogin()
{
if(isset($_COOKIE['login_key']) && $_COOKIE['login_key']){
$redis = $this->redis();
$session_str = stripslashes($redis->get($_COOKIE['login_key']));
if($session_str){
$index = strpos($session_str, ';');
$str = substr($session_str, ($index+1));
$userInfo = unserialize(str_replace('userInfo|', '', $str));
return $this->returnJosn(200, '登录中', json_encode(['userInfo'=>$userInfo]));
}else{
unset($_COOKIE['login_key']);
return $this->returnJosn(500, '登录失效');
}
}else{
return $this->returnJosn(500, '登录失效');
}
}
public function loginOut()
{
unset($_COOKIE['login_key']);
unset($_SESSION['uid']);
unset($_SESSION['userInfo']);
session_destroy();
return $this->returnJosn(200, '已退出登录');
}
protected function redis()
{
$redisConfig = $this->redisConfig;
$redis = new Redis();
$redis->pconnect($redisConfig['host'], $redisConfig['port'], $redisConfig['timeout']);
$redis->auth($redisConfig['auth_pass']);
return $redis;
}
protected function returnJosn($code, $msg = '', $data = array())
{
echo json_encode([$code, $msg, $data], JSON_UNESCAPED_UNICODE);
exit;
}
}
session_start();
$act = isset($_GET['act']) ? trim($_GET['act']) : 'isLogin';
if($act){
$center = new center($synUrlAll, $centerUrl, $privateKey, $redisConfig);
$center->$act();
}
登录服务器的 login.html 文件(统一登录页面)
<!DOCTYPE html>
<html>
<head>
<title>登录页面</title>
</head>
<body>
<meta charset="utf-8">
<h3>登录页面</h3>
<form action="./center.php?act=toLogin" method="post">
<input type="text" name="username" value="username11"><br/>
<input type="password" name="passwd" value="pwd11"><br/>
<input type="submit" value="登录">
</form>
</body>
</html>
其他web服务器的 web.php 文件
<?php
header('Content-Type:application/json; charset=utf-8');
error_reporting(E_ERROR | E_CORE_ERROR | E_PARSE);
/**
* 站点1,使用此方法登录依次其他站点
*/
class web1
{
protected $privateKey;
protected $redisConfig;
function __construct($privateKey, $redisConfig)
{
$this->privateKey = $privateKey;
$this->redisConfig = $redisConfig;
}
//同步登录
public function synLogin()
{
$data = $_GET;
if(!isset($data['login_key']) || !$data['login_key']){
return $this->returnJosn(500, '缺少login_key参数');
}
$login_key = base64_decode($data['login_key']);
$login_key = substr($login_key, 0, strlen($login_key)-strlen($this->privateKey));
setcookie('login_key', $login_key, time()+3600*24, '/');
header("Location:".base64_decode($data['returnUrl']).$data['urlAll']);
exit;
}
public function isLogin()
{
if(isset($_COOKIE['login_key']) && $_COOKIE['login_key']){
$redis = $this->redis();
$session_str = stripslashes($redis->get($_COOKIE['login_key']));
if($session_str){
$index = strpos($session_str, ';');
$str = substr($session_str, ($index+1));
$userInfo = unserialize(str_replace('userInfo|', '', $str));
return $this->returnJosn(200, '登录中', json_encode(['userInfo'=>$userInfo]));
}else{
unset($_COOKIE['login_key']);
return $this->returnJosn(500, '登录失效');
}
}else{
return $this->returnJosn(500, '登录失效');
}
}
public function loginOut()
{
unset($_COOKIE['login_key']);
return $this->returnJosn(200, '已退出登录');
}
protected function redis()
{
$redisConfig = $this->redisConfig;
$redis = new Redis();
$redis->pconnect($redisConfig['host'], $redisConfig['port'], $redisConfig['timeout']);
$redis->auth($redisConfig['auth_pass']);
return $redis;
}
protected function returnJosn($code, $msg = '', $data = array())
{
echo json_encode([$code, $msg, $data], JSON_UNESCAPED_UNICODE);
exit;
}
}
$act = isset($_GET['act']) ? trim($_GET['act']) : 'isLogin';
//私钥
$privateKey = 'redisKey';
//redis配置
$redisConfig = [
'host' => '106.12.155.232',
'port' => '6379',
'timeout' => '604800',
'auth_pass' => 'redispwd11',
];
if($act){
$web1 = new web1($privateKey, $redisConfig);
$web1->$act();
}