简述

本文章是我对Yii2如何实现SSO登录做一个全面的逻辑解析。事实上,在此之前我也写过两篇文章关于SSO登录的实现方式以及进一步优化,包括这篇文章也都是介绍Yii2的SSO登录,逐步优化不断总结与分享,目的就是要把Yii2的SSO登录功能尽可能的做到极致,从程序开发的灵活性角度去思考问题,把一切潜在的局限扼杀在摇篮中。

实现步骤

1、在common\config\main.php文件配置如下:

<?php  
use kartik\mpdf\Pdf;
//动态获取无www的域名
$host_array = explode('.', $_SERVER["HTTP_HOST"]); 
if (count($host_array) == 3) {    
     if(strpos($_SERVER["HTTP_HOST"],':')){  //判断域名是否存在端口号
         $domain_array=explode(':', $host_array[2]);
         define('DOMAIN', $host_array[1] . '.' . $domain_array[0]);//去掉端口号,避免域名带端口号无法退出
     }else{
        define('DOMAIN', $host_array[1] . '.' . $host_array[2]);
     }  
}
//针对com.cn域名
elseif (count($host_array) == 4) {   
    if(strpos($_SERVER["HTTP_HOST"],':')){
         $domain_array=explode(':', $host_array[3]);
         define('DOMAIN', $host_array[1] . '.' . $host_array[2]. '.' . $domain_array[0]);
    }else{
        define('DOMAIN', $host_array[1] . '.' . $host_array[2]. '.' . $host_array[3]);
    }  
}else{
    //echo "本系统不支持本地访问,请配置域名";exit;
}

//将动态获取到的无www域名,配置上www、crm...
define('DOMAIN_HOME', 'www.' . DOMAIN);
define('DOMAIN_CRM', 'crm.' . DOMAIN);
define('DOMAIN_HR', 'hr.' . DOMAIN);
define('DOMAIN_WEIXIN', 'weixin.' . DOMAIN);
define('DOMAIN_ADMIN', 'admin.' . DOMAIN);
define('DOMAIN_OA', 'oa.' . DOMAIN);
define('DOMAIN_FRONTEND', 'frontend.' . DOMAIN);
define('DOMAIN_BACKEND', 'backend.' . DOMAIN);

define('DOMAIN_API', 'api.' . DOMAIN);
define('DOMAIN_LOGIN', 'login.' . DOMAIN);

配置User 和 Session:
'components' => [
        'user' => [            
            'identityClass' => 'login\models\User',
            'enableAutoLogin' => true,
            'identityCookie' => ['name' => '_identity', 'httpOnly' => true,'domain' => '.' . DOMAIN],
            // 'returnUrl'=>'//' . DOMAIN_HOME,
        ],        
        'session' => [           
            'cookieParams' => ['domain' => '.' . DOMAIN, 'lifetime' => 0],            
            'timeout' => 24*3600*30,
        ],

2、新建一个login模块,然后打开common\config\bootstrap.php加下这么一段代码:

Yii::setAlias('login', dirname(dirname(__DIR__)) . '/login'); //增加自定义目录结构

3、在login\config\main.php里修改 urlManager,改成下面这样子:


        'urlManager' => [
            'class' => 'common\components\MutilpleDomainUrlManager',
            'domains' => [
                'crm' => '//' . DOMAIN_CRM,
                'admin' => '//' . DOMAIN_ADMIN,
                'hr' => '//' . DOMAIN_HR,
                'oa' => '//' . DOMAIN_OA,
                'frontend' => '//' . DOMAIN_FRONTEND,
                'backend' => '//' . DOMAIN_BACKEND,
            //     'img' => '//' . DOMAIN_IMG,
                'api' => '//' . DOMAIN_API,
                'login' => '//' . DOMAIN_LOGIN,
            ],

            //'baseUrl' => 'http://'.DOMAIN_LOGIN.'?redirectURL=http://'.DOMAIN_HOME,
            'showScriptName' => false,
            'enablePrettyUrl' => true,  //美化URL
            'enableStrictParsing' => true, //设置有无‘s’;  
            // 'suffix' => ".php",  
            'rules' => [ '' => 'site/login',
                        // 如果没有这里,则访问域名不能直接打开默认Action (去除URL的“site/login”) 
            ]   
        ],

4、补充第3步骤缺少的MutilpleDomainUrlManager.php文件
MutilpleDomainUrlManager.php,这个文件按照我给你们的命名空间存放。

<?php
namespace common\components;
 
use Yii;
 
class MutilpleDomainUrlManager extends \yii\web\UrlManager
{
    public $domains = array();
 
    public function createUrl($domain, $params = array()) {
        if (func_num_args() === 1) {
            $params = $domain;
            $domain = false;
        }
        $bak = $this->getBaseUrl();
        if ($domain) {
            if (!isset($this->domains[$domain])) {
                throw new \yii\base\InvalidConfigException('Please configure UrlManager of domain "' . $domain . '".');
            }
            $this->setBaseUrl($this->domains[$domain]);
        }
        $url = parent::createUrl($params);
        $this->setBaseUrl($bak);
        return $url;
    }
}

注释:用于获取domain url。

5、修改login模块下的SiteController.php Login方法

    //登录
    public function actionLogin()
    {    
        //获取当前的URL
        $URL=Yii::$app->request->getHostInfo().Yii::$app->request->url;
        $URL1='http://'.DOMAIN_CRM; 
        $redirectURL=Yii::$app->request->get('redirectURL'); 
        $redirectURL1='http://'.DOMAIN_LOGIN; 

        $model = new LoginForm();
        TagDependency::invalidate(Yii::$app->cache, ['Session:'.Yii::$app->session->id]);
 
        //验证是否已登录,非为登录
        if (!\Yii::$app->user->isGuest) {  
           if(!empty($redirectURL)){
                $this->actionLogout();//强制性退出登录
               
                return $this->redirect($URL);

           }else{ 
                //redirectURL不存在时,提交表单判断
                if($this->siteLogin){     
                   if ($model->load(Yii::$app->request->post()) && $model->login()) { 
                       //判断该账号是否禁止登录 
                       if(empty($t_status=$model->user->attributes['t_status']) && $t_status==0){ 
                           return $this->error($redirectURL1,[Yii::t('yii','The account is prohibited from logging in, please contact the administrator!')]);
                       }else{ 
                            if(empty($redirectURL)) return $this->redirect($URL1,301); 

                            return $this->redirect($redirectURL,301);
                        }          
                    } else { 
                        return $this->renderPartial('login', [
                            'model' => $model,
                        ]);
                    }
                }else{ 
                    return $this->goHome();
                }  
           }  
        } else {

            //redirectURL存在时,提交表单判断
            if ($model->load(Yii::$app->request->post()) && $model->login()) {     
                 //判断该账号是否禁止登录
                 if(empty($t_status=$model->user->attributes['t_status']) && $t_status==0){ 
                    if(empty($redirectURL)){
                        return $this->error($redirectURL1,[Yii::t('yii','The account is prohibited from logging in, please contact the administrator!')]);
                    }
                    return $this->error($URL,[Yii::t('yii','The account is prohibited from logging in, please contact the administrator!')]);
                 }else{ 
                    if(empty($redirectURL)) return $this->redirect($URL1,301); 
               
                    return $this->redirect($redirectURL,301);
                 }        
            } else {   
                return $this->renderPartial('login', [
                    'model' => $model,
                ]);
            }
        }
    }

6、修改frontend模块下的SiteController.php Login方法

 public function actionLogin()
    {  
        //获取上一个URL
        $URL=Yii::$app->request->getHostInfo().Yii::$app->user->getReturnUrl();  
        if (!\Yii::$app->user->isGuest) { 
            return $this->redirect('http://'.DOMAIN_LOGIN.'?redirectURL='.$URL);
        }
        $model = new LoginForm();
        if ($model->load(Yii::$app->request->post()) && $model->login()) { 
            return $this->goBack();
        } else { 
            if(!empty($URL)){ 
                return $this->redirect('http://'.DOMAIN_LOGIN.'?redirectURL='.$URL);
            }else{ 
                return $this->renderPartial('login', [
                                'model' => $model,
                            ]);
            }
        }
    }

7、在frontendviewsdefaultlayoutsmain.php的顶部加入下面代码

$redirectURL='http://'.DOMAIN_LOGIN.'?redirectURL='.Yii::$app->request->getHostInfo().Yii::$app->request->url;

8、最后在退出的a标签这么输出<?php echo $redirectURL; ?>。

注:在其它模块如:backend、crm等等当中仿造我这frontend的实现思路改改,即可实现整个项目的SSO登录机制。

提醒注意

1、在第1步骤中,动态获取无www的域名,此步骤必须做域名的判断处理,比如:www.xxx.com,www.xxx.com.cn,www.xxx.com:8099等这些可能出现的域名,以保证域名都能使用实现登录退出的机制。
2、在第5步骤和第7步骤中,使用Yii2自带的方法Yii::$app->request->getHostInfo().Yii::$app->request->url获取当前的url,是比较方便且高效的一种做法,能降低代码的冗余。
3、在第6步骤中的frontend模块下的SiteController.php Login方法里,用Yii2自带的方法Yii::$app->request->getHostInfo().Yii::$app->user->getReturnUrl()获取上一个url,这里必须特别注意是获取“上一个url”而不是当前的url,获取当前的url就变成了login.xxx.com了,这是不对的。

相关资料

Yii: 获取URL的一些方法:http://blog.csdn.net/iefreer/...


zacklee
704 声望56 粉丝