前言:
上一篇我们介绍过了如何使用 PHP+jwt. 讲述如何制作一个一站式登录平台. 打通各个业务方之间的账号壁垒. 那么现在我就来介绍下. 从业务方的角度, 如何去接入一个登录中心
流程
用户访问业务方地址
接入登录中心
首先我介绍下域名:
登录中心的 url 是: tp.login.com:81
接入方的域名是: tp.admin.com:81
两个完全不一样的域名, 也不存在 session 共享什么的. 那么, 现在我要把我的 tp.admin.com:81 接入到 tp.login.com:81 的账号体系中去.
一: 设计回调地址的路由
由于我这次写的一个 demo. 就是直接在 index 模块下 login 控制器里写了个 callback 方法. 如果在生产环境下的话还是推荐强制路由. 这样 url 看上去美观点. 好那么我们回调地址就是 tp.admin.com:81/index.PHP/index/login/callback(这里有个细节, 这个回调的地址, 是不需要做登录验证的, 因为刚回调回来的时候, cookie 中还没有密文. 所以这个控制器不继承有登录验证的那个控制器)
二: 申请应用
好了, 既然都是我写的 demo. 就不存在申请不申请了. 不过正规的流程应该是接入方会向登录中心的管理员, 申请. 告知回调地址. 登录中心的管理员在 App 中添加回调地址, 随机字符串作为秘钥, 并获得 appid.
三: 开始开发
首先呢, 我们将登录中心的地址, 和分配给我们的 appid 和 app_key 存到配置文件中方便使用
配置文件
随后, 我们既然是接入别人的登录中心, 那就不用自己写登录了. 直接写登录验证.
登录验证一般来说都会是写到一个控制器基类的__construct() 方法里面
- class Base extends Controller
- {
- public $uid;
- public $name;
- public function __construct()
- {
- /**
- * 1. 判断是否有 cookie
- * 2. 没有 cookie, 拼接回调地址和 appid
- * 3. 如果有 cookie, 解密 code. 验签
- * 4. 延签失败删除 cookie
- * 5. 验签成功, 初始化 uid,name
- */
- try{
- $request=request();
- $authCode= cookie('authCode');
- if (empty($authCode)) {
- // 拼接回调地址和 appid
- $appid=config('sso.appid');
- // 获取当前请求地址
- $callback_url=$request->url(true);
- // 去配置文件中获取登录中心地址
- $login_url=config('sso.login_url');
- // 拼接跳转地址
- $url=$login_url."?appid={$appid}&callback_url=".urlencode($callback_url);
- $this->redirect($url);
- }else{
- // 如果有 cookie
- // 解密验签并取得 cookie 中的 uid
- $uid=Auth::getUidByJwt($authCode);
- $this->uid=$uid;
- }
- }catch (Exception $e){
- $this->error($e->getMessage());
- }
- }
- }
重点看看验签获取 uid 的方法. 其实这个方法是一个基础方法扩展而成的. 直接来看代码
- /**
- * 从 jwt 中获取某个字段
- * @param $token
- * @param $key
- * @return bool
- * @throws Exception
- */
- public static function getDataByJwt($token, $key)
- {
- // 将 token 字符串初始化 token 对象
- $tokenObj = (new Parser())->parse((string)$token);
- // 实例化加密对象
- $signer = new Sha256();
- // 使用配置中的秘钥验签
- $result = $tokenObj->verify($signer, config('sso.app_key'));
- if (!$result) {
- // 验签失败
- // 删除 cookie, 避免无限重定向
- cookie('authCode');
- throw new Exception('验签失败');
- }
- // 使用检查过期时间的方法判断登录是否过期
- if ($tokenObj->isExpired()) {
- cookie('authCode');
- throw new Exception('登录过期');
- }
- // 获取想要的数据
- $data = $tokenObj->getClaim($key);
- if ($data) {
- return $data;
- } else {
- return false;
- }
- }
- /**
- * 使用基础方法, 获取 uid
- * @param $token
- * @return bool
- * @throws Exception
- */
- public static function getUidByJwt($token)
- {
- return self::getDataByJwt($token, 'uid');
- }
其实 jwt 的包提供了非常丰富的方法, 基本对密文没有太多的处理, 验签之类的方法都准备好了. 使用成本相当的低
上面介绍了验证登录的构造函数, 还有一个地方别忘了. 那就是我们最开始申请应用的那个回调地址. 那个方法需要负责以下几个动作
获取登录中心传递过来的参数
验证 code 签名
将 code 写入 cookie
从定向到用户最初访问的地址
下面来看代码
- class Login extends Controller
- {
- public function callback($code,$redirect)
- {
- try{
- // 验签并取出 uid
- $uid= Auth::getUidByJwt($code);
- //uid 存在且回调地址存在
- if($uid&&$redirect){
- // 写入 cookie
- cookie('authCode',$code);
- // 重定向到用户最初访问的地址
- $this->redirect($redirect);
- }else{
- // 避免重定向
- cookie('authCode');
- $this->error('登录过期');
- }
- }catch (Exception $e){
- // 避免重定向
- cookie('authCode');
- $this->error('登录过期');
- }
- }
- }
当这一次重定向到用户访问的页面时, 又会触发登录验证. 这个时候, 我们已经写好了 cookie.cookie 也是合法的. 就会成功的取出用户的 uid. 下次用户访问这个应用的别的方法时, 也携带这个 cookie 就可以正常使用了. 更方便的是, 我们在登录中心这个域名下还保存了一份 cookie, 当去访问别的应用时, 别人的应用认为你没有登录重定向到登录中心后, 就会检测到有 cookie 值并带上值重定向回别的应用. 实现免登录
下面简单展示下效果
l 浏览器进入我们写的 tp.admin 这个应用
检测没有登录, 重定向到登录中心
登录中心
随后输入密码账号, 点击登录
点击登录
然后又经历了两次重定向
一次是重定向到注册的回调地址, 带上 code
一次是写好 cookie 之后就重定向到最初访问的地址, 我们从浏览器上也观察出回到了我们最初输入的那个地址
回到最初访问的地址
并且我在首页输出了用户的 id 1 这是通过解密 jwt 得到的 uid
现在我们在刷新一下页面, 由于已经有 cookie 保存了. 所以不会再做重定向了.
登录后再访问首页
我们还可以试试把 tp.admin.com 这个域名下的 cookie 删除, 然后在访问首页. 看会发生什么
删除 cookie
我们刷新下 tp.admin.com 的主页
又是一波疯狂的重定向
我们一步一步分析
重定向到了登录中心
登录中心发现有 cookie 存在就重定向到回调地址, 带上 cookie 和 redirect
回调地址, 验签通过, 初始化 uid 并且重定向到最初访问的地址, 也就是首页
虽然做了几次重定向, 但是对于用户来说是察觉不出来的. 他体验到的是不需要登录的方便与快捷
好了今天的介绍接入登录中心就写到这里了. 虽然还有些不完善的地方, 不过这个机制是非常值得学习和可靠的. 如果有不对的地方, 望大神指正. 感谢!
以上
来源: http://www.jianshu.com/p/25cb93e157bf