后文涉及的跳转逻辑所需,示例站点说明
https://ssl.hlzblog.top/login // 单点登录站点,登录页
https://ssl.hlzblog.top/submit // 单点登录站点,登录表单页
https://ssl.hlzblog.top/redirect // 单点登录站点,重定向页
https://zone.yuntianhe.com/star/articles // 某个应用站点 - 正打算访问的某个地址
https://zone.yuntianhe.com/setcookie // 某个应用站点 - 设置 cookie
单点登录站点的凭证 与 应用站的凭证 保持一致即可
后文我们都以cookie
中的 Auth-Token
作为凭证
callback
对应应用站点设置cookie的地址:redirect
正打算访问的某个地址注册、登录、注销
都在这完成)
Auth-Token
Auth-Token
,进入应用站点
Auth-Token
执行本次跳转Redis
,获取到用户的 Auth-Token
存储的身份信息,进行操作一般通过这个 Auth-Token
凭证,去请求开发的RPC接口
以实现用户相关数据拉取,以此分离不同服务
<?php
use App\Helpers\Token;
use App\Services\User\LogService;
... // 根据正确的用户登录表单数据 搜索到用户信息
// 删除上一次的登录信息
Token::delete($user->remember_token);
// 写入用户 mobile、token、user_id 到缓存信息
$token = Token::rand_token();
$token_info = [
'email' => $user->email,
'token' => $user,
'user_id' => $user->id,
];
Token::set($token, $token_info);
// 写入登录日志
LogService::user_log($user->id);
// 返回 token 给客户端
return [
'token' => $token,
];
如果是后台系统的单点登录,还应该把权限控制在这部分完成,如Auth
节点级控制
本次单点登录传输 Auth-Token 回源站的安全性问题
<?php
namespace App\Helpers;
// -----------------------
// Link: www.hlzblog.top
// -----------------------
use Illuminate\Support\Facades\Redis;
class Token
{
/**
* 随机生成固定长度的随机数
* @param String len 截取长度,默认八位
* @param Int type 返回类型
* @return String
*/
public static function rand_str($len = 8, $type = 'mix')
{
switch ($type) {
case 'mix':
$str = "0123456789qwertyuiopasdfghjklzxcvbnm~!@#$%^&*()_+";
break;
case 'number':
$str = "0123456789";
break;
case 'alpha':
$str = "qwertyuiopasdfghjklzxcvbnm";
break;
default:
$str = "0123456789qwertyuiopasdfghjklzxcvbnm";
}
return substr(str_shuffle($str), 0, $len);
}
/**
* 依据微秒来生成不同字符串
* @return String 生成的 token
*/
public static function rand_token()
{
return md5(microtime(true) . self::rand_str());
}
const TOKEN_PRE = 'tk:'; // TOKEN 名称 前缀
const TOKEN_EXPIRE = 2592000; // Token 有效时长,默认,3个月
public static $token = null; // http请求携带的token
/**
* CURD token中的数据
* 为空时,返回token中的数据,否则更新数据到 token
* @param Array data
*/
public static function token_info($data = [])
{
if ([] == $data) {
return self::get(self::$token);
} else {
return self::set(self::$token, $data);
}
}
/**
* 检测AuthToken是否正确
* @param String token 具体值
* @return int 用户id
*/
public static function token_check($token)
{
self::$token = $token;
$arr = self::get($token);
if (count($arr)) {
return $arr['user_id'];
} else {
return 0;
}
}
// ------------------------------------
// 内部逻辑
// ------------------------------------
/**
* 获取 token内容
* @param String token 具体值
* @return Array
*/
public static function get($token)
{
$token_name = self::TOKEN_PRE . $token;
$token_val = Redis::get($token_name);
if (!$token_val) {
return []; // 查询不到时
}
return json_decode($token_val, true);
}
/**
* 合并内容到 token
* @param String token 具体值
* @param Array data 数组数据
* @return Void
*/
public static function set($token, $data)
{
$token_name = self::TOKEN_PRE . $token;
$before = self::get($token_name);
$token_life = time() + self::TOKEN_EXPIRE;
if ($before == null) {
$before = [];
}
$arr = array_merge($before, $data);
Redis::set($token_name, json_encode($arr));
Redis::expireAt($token_name, $token_life);
}
/**
* 删除 过期 token
* @param String token 具体值
* @return Void
*/
public static function delete($token)
{
$token_name = self::TOKEN_PRE . $token;
Redis::del($token_name);
}
}
评论列表点此评论