Single Sign On 单点登录解决方案


Single Sign On 单点登录解决方案

文章插图
 
需求多个应用系统中 , 用户只需要登录一次就可以访问所有相互信任的应用系统 。
A 网站和 B 网站是同一家公司的关联服务 。现在要求 , 用户只要在其中一个网站登录 , 再访问另一个网站就会自动登录 , 请问怎么实现?
涉及到的关键点:
这里就涉及到了 跨域认证 以及 前端页面 JAVAScript 跨域 问题 。
一、跨域认证问题互联网服务离不开用户认证 。一般流程是下面这样 。
  • 用户向服务器发送账户和密码
  • 服务器验证通过后 , 在当前会话 (session)里保存相关数据 , 如用户角色、用户ID等
  • 服务器向用户返回一个 session_id , 写入用户 cookie
  • 用户之后的每一次请求 , 都会通过 Cookie 将 session_id 传回服务器
  • 服务器收到 session_id  , 找到之前存储的数据 , 由此得知用户身份
下面以 登录 A 站点 访问 B 站点 为例
方案一: session 持久化
? 将 session 数据持久化 , 写入数据库或别的持久层 。各种服务受到请求后 , 都向持久层请求数据 。
这种方案 , 对于各种系统的代码改动量少 , 只要在权限验证的地方进行判断即可 。
  • A 登录成功代码
  • 登录成功后 , 将 session 存储到 redis 持久化存储 , 注意设置有效期
  • tip:
Redis 以层级形式、目录形式保存数据 , 最大四级 , 格式如下:
set('dir:dir:dir:key', 'value');
/** * 将 Session 存储到 Redis * @param boolean $logout 是否退出登陆 默认否 * @return array */public function sessionToRedis($logout = false) { try { if ($logout) { (new Redis())->del('Admin:session:' . session_id()); }else{ $userInfo = session('CH_ADMIN_LOGIN_STATUS'); $res = (new Redis())->setex('Admin:session:' . session_id(), 28800, json_encode($userInfo, JSON_UNESCAPED_UNICODE)); if ($res === false) throw new Exception('Redis 存储失败'); return true; } } catch (Exception $e) { return false; } } </pre>
  • 登录成功后 访问 B, B 进行用户身份认证
  • 注意 此时 访问 B的链接URL 要携带参数 sessionid!
  • B 在处理请求- 身份验证时 , 先解析是否携带了sessionid参数 , 携带了则向 redis 中查询相关数据 , 并将数据保存到当前会话中 。此时就成功 登录 B 了 。
/** * 通过其他后台单点登陆 * 传递session_id(用于session共享) */$params = $request->param();if (!empty($params) && isset($params['session_id'])) { session_id($params['session_id']); // 设置会话id $userInfo = Session::get('userinfo', 'admin'); if (empty($userInfo)) { $userInfo = json_decode((new Redis())->get('Admin:session:'.$params['session_id']), true); Session::set('userinfo', $userInfo, 'admin'); }} else { $userInfo = Session::get('userinfo', 'admin');}</pre>方案二:JWT(JSON Web Token)
服务器索性不保存 session 数据了 , 「用户数据」加密保存到「客户端」 , 每次请求都将数据发回服务器 , 服务器再进行解析得到用户身份信息数据 。JWT 就是这种方案的一个代表 。
【Single Sign On 单点登录解决方案】JWT 原理


    推荐阅读