IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 基于laravel 5.7 集成 phpCAS 1.3.8 单点登出踩坑记。 -> 正文阅读

[PHP知识库]基于laravel 5.7 集成 phpCAS 1.3.8 单点登出踩坑记。

很神奇,宛宛转转我居然跑去帮忙解决PHP的项目了....一个基于国内的THINKPHP + 项目的二次开发项目。

Laravel 不知道那个版本开始,框架的作者认为php本身session机制太过于.. 唔. 所以自己写了一套session的处理机制,于是乎,项目组果断掉坑。

不做太多介绍,在读此文前,读者需要了解cas登出的标准流程。

1. 路由:这里使用的cas服务端是基于APACHE的CAS项目,版本为4.2.7,PHP注册到CAS的地址需要支持POST请求,因为CAS会使用POST登出请求。

2.权限:注册到CAS的地址需要没有权限限制。

3.处理流程:

a. 配置在登录CAS成功时候的回调函数,该回调函数将把票据保存在session

    	cas()->setPostAuthenticateCallback(function ($ticket){
    		//获取用户票据,并保存到缓存,对应到session;
            session()->put('casticket',$ticket);
            session()->save();
    	},array());

b.配置在接收到CAS登出请求时的回调函数,该函数将从CACHE中获取登出请求传过来的票据对应的sessionid,然后根据sessionid将用户踢掉。

踢用户要做三件事:把用户表里的remember_token置空;把laravel 的session重制;把casphp使用的session重置。

其中把phpcas使用的session重置只需要配置phpcas的 CAS_CONTROL_SESSIONS为true使其自动删除session即可。

cas()->setSingleSignoutCallback(function ($ticket){
    		$sessionId=Cache::pull('ticket:'.$ticket);
    		if($sessionId==null||$sessionId==""){
    			return;
    		}
    		$remember_token=Cache::pull('remember_token'.$sessionId);
    		if ($remember_token!=null&&$remember_token!=""){
    		    DB::update("UPDATE users set remember_token=null where remember_token=:t",['t'=>$remember_token]);
    		}
    		
    		Session::setId($sessionId);
			Session::start();
			
			$this->guard()->logout();

			Session::invalidate();
			
        	//本地登出
        	Auth::logout();
        	
			
    	},array());

c.在注册cas的地址写上如下方法,手动通知cas做登出处理

   	if (isset($_REQUEST['logoutRequest'])) {
    		$_POST['logoutRequest']=$_REQUEST['logoutRequest'];
    		cas()->handleLogoutRequests(false);
    		\Log::debug("退出登录");
    		exit;
    	}

d.在登录成功,并调用Laravel 需要进行的操作,主要目的就是做casticket和session的对应关系

            //CAS已登录,AUTH未登录,获取用户未登录前的casticket,
            $ticket=session()->get('casticket');
            
            Auth::login($user, true);

            Cache::forever('ticket:'. $ticket, session()->getId());
            //把用户的remember_token扔进session 登出的时候删除相关数据
            $user = User::where('casid', cas()->user())->first();
            Cache::forever('remember_token'.session()->getId(), $user->remember_token);

以下是增加单点登出前后代码的对比:

前:

<?php

namespace Zhiyi\Plus\Http\Controllers\Auth;

use Carbon\Carbon;
use DB;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Jenssegers\Agent\Agent;
use Zhiyi\Plus\Http\Controllers\Controller;
use Zhiyi\Plus\Models\Role;
use Zhiyi\Plus\Models\User;
use function Zhiyi\Component\ZhiyiPlus\PlusComponentPc\restful;
use function Zhiyi\Plus\username;


class CasController extends Controller
{
    //...
    use AuthenticatesUsers {
        login as authenticatesUsersLogin;
    }

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('cas.auth')->except(['logout']);
    }

    public function login(Request $request, Agent $agent)

    {
        if (cas()->isAuthenticated()) {
            //获取cas用户信息
            //...
            //根据casid查询用户
            $user = User::where('casid', cas()->user())->first();

            if ($user == null && !empty($casAttributes['email'])) {
                //根据email查询用户
                $user = User::where('email', $casAttributes['email'])->first();
            }
            if ($user == null && !empty($casAttributes['phone'])) {
                //根phone查询用户
                $user = User::where('phone', $casAttributes['phone'])->first();
            }

            date_default_timezone_set("PRC");
            //没有这个用户 要做注册用户处理
            if ($user == null) {
                $attributes['created_at'] = time();
                $attributes['updated_at'] = time();
                $user = new User($attributes);
                $user->save();
                //添加权限信息
                $this->addRes($user, $casAttributes);
            } else {
                //更新User信息
                $this->updateUserInfoWhenChange($user, $attributes);
                //更新权限信息
                $this->updateRes($user, $casAttributes);
            }
            //回调地址
            $redirect = $request->input("redirect");
            if ($redirect == null && isset($_SESSION['redirect'])) {
                $redirect = $_SESSION['redirect'];
                unset($_SESSION['redirect']);
            }
            $referer = $redirect == null ? '/' : $redirect;
            //登录
            $token = $request->session()->get('token');
            $jwt = app(\Tymon\JWTAuth\JWT::class);
            if (!$token || !$jwt->setToken($token)->check()) {
                $token = $jwt->fromUser($user);
                $request->session()->put('token', $token);
                $request->session()->save();
            }
            $config = config('http');
            $spa = $config['spa'];
            if ($agent->isMobile() && $spa['open']) {
                //$referer = "/#" . $referer;
                //获取?第一次出现的位置
                $check = strpos($referer, '?');
                //如果存在?
                if($check !== false){
                    //判断?后面有没有参数
                    if(substr($referer, $check+1) == ''){
                        $referer .= "token=" . $token;
                    }else{
                        $referer .= "&token=" . $token;
                    }
                }else{
                    $referer .= "?token=" . $token;
                }
            }
            Auth::login($user, true);
            //根据权限点进行跳转
            if (strstr($referer, 'auth/login') != false) {
                //登出账号密码再次验证
                Auth::logout();
                return redirect()->route('adminlogin');
            }
            return redirect()->route('redirect', ['target' => $referer]);
        }
        //未认证,本地登出,跳转认证
        Auth::logout();
        return cas()->authenticate();
    }


    private function addRes($user, $casAtt)
    {

        //...
    }

    private function updateRes($user, $casAtt)
    {   //  移除用户的所有角色...
        $user->roles()->detach();
        $this->addRes($user, $casAtt);
    }

    private function doCertification($user, $casRoles, $casAtt)
    {
        //...
    }

    private function updateUserInfoWhenChange($user, $attributes)
    {
        //...
        //本地用户信息和Cas信息差别更新
        $oddattributes = $user->getAttributes();
        $result = array_diff_assoc($attributes, $oddattributes);
        if ($result != null) {
            $attributes['updated_at'] = time();
            $user->update($attributes);
        }
    }


    public function logout(Request $request)
    {
        $this->guard()->logout();

        $request->session()->invalidate();

        //本地登出
        Auth::logout();
        //Cas登出
        cas()->logout();
    }


    /**
     * Get the login username to be used by the controller.
     *
     * @return string
     * @author Seven Du <shiweidu@outlook.com>
     */
    protected function username(): string
    {
        return username(
            cas()->user()
        );
    }

    /**
     * Get the post register / login redirect path.
     *
     * @return string
     * @author Seven Du <shiweidu@outlook.com>
     */
    protected function redirectTo(): string
    {
        return '/feeds';
    }

    /**
     * Get the guard to be used during authentication.
     *
     * @return \Illuminate\Contracts\Auth\StatefulGuard
     */
    protected function guard()
    {
        return Auth::guard();
    }
}

后:

<?php

namespace Zhiyi\Plus\Http\Controllers\Auth;

use Carbon\Carbon;
use DB;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Jenssegers\Agent\Agent;
use Zhiyi\Plus\Http\Controllers\Controller;
use Zhiyi\Plus\Models\Role;
use Zhiyi\Plus\Models\User;
use function Zhiyi\Component\ZhiyiPlus\PlusComponentPc\restful;
use function Zhiyi\Plus\username;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Session;


class CasController extends Controller
{
   //...
    use AuthenticatesUsers {
        login as authenticatesUsersLogin;
    }

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        //$this->middleware('cas.auth')->except(['logout']);
    }

    public function login(Request $request, Agent $agent)

    {
    	cas()->setPostAuthenticateCallback(function ($ticket){
    		//获取用户票据,并保存到缓存,对应到session;
            session()->put('casticket',$ticket);
            session()->save();
    	},array());
    	
    	cas()->setSingleSignoutCallback(function ($ticket){
    		$sessionId=Cache::pull('ticket:'.$ticket);
    		if($sessionId==null||$sessionId==""){
    			return;
    		}
    		$remember_token=Cache::pull('remember_token'.$sessionId);
    		if ($remember_token!=null&&$remember_token!=""){
    		    DB::update("UPDATE users set remember_token=null where remember_token=:t",['t'=>$remember_token]);
    		}
    		
    		Session::setId($sessionId);
			Session::start();
			

			Session::invalidate();
			
        	//本地登出
        	Auth::logout();
        	
			
    	},array());
    	
    	
    	if (isset($_REQUEST['logoutRequest'])) {
    		$_POST['logoutRequest']=$_REQUEST['logoutRequest'];
    		cas()->handleLogoutRequests(false);
    		\Log::debug("退出登录");
    		exit;
    	}
    	
    	
        if (cas()->isAuthenticated()) {
            
            //获取cas用户信息
            $domain = config('app.account_url');
            //...

            //根据casid查询用户
            $user = User::where('casid', cas()->user())->first();

            if ($user == null && !empty($casAttributes['email'])) {
                //根据email查询用户
                $user = User::where('email', $casAttributes['email'])->first();
            }
            if ($user == null && !empty($casAttributes['phone'])) {
                //根phone查询用户
                $user = User::where('phone', $casAttributes['phone'])->first();
            }

            date_default_timezone_set("PRC");
            //没有这个用户 要做注册用户处理
            if ($user == null) {
                $attributes['created_at'] = time();
                $attributes['updated_at'] = time();
                $user = new User($attributes);
                $user->save();
                //添加权限信息
                $this->addRes($user, $casAttributes);
            } else {
                //更新User信息
                $this->updateUserInfoWhenChange($user, $attributes);
                //更新权限信息
                $this->updateRes($user, $casAttributes);
            }
            //回调地址
            $redirect = $request->input("redirect");
            if ($redirect == null && isset($_SESSION['redirect'])) {
                $redirect = $_SESSION['redirect'];
                unset($_SESSION['redirect']);
            }
            $referer = $redirect == null ? '/' : $redirect;
            //登录
            $token = $request->session()->get('token');
            $jwt = app(\Tymon\JWTAuth\JWT::class);
            if (!$token || !$jwt->setToken($token)->check()) {
                $token = $jwt->fromUser($user);
                $request->session()->put('token', $token);
                $request->session()->save();
            }
            $config = config('http');
            $spa = $config['spa'];
            if ($agent->isMobile() && $spa['open']) {
                //$referer = "/#" . $referer;
                //获取?第一次出现的位置
                $check = strpos($referer, '?');
                //如果存在?
                if($check !== false){
                    //判断?后面有没有参数
                    if(substr($referer, $check+1) == ''){
                        $referer .= "token=" . $token;
                    }else{
                        $referer .= "&token=" . $token;
                    }
                }else{
                    $referer .= "?token=" . $token;
                }
            }
            
            //CAS已登录,AUTH未登录,获取用户未登录前的casticket,
            $ticket=session()->get('casticket');
            
            Auth::login($user, true);

            Cache::forever('ticket:'. $ticket, session()->getId());
            //把用户的remember_token扔进session 登出的时候删除相关数据
            $user = User::where('casid', cas()->user())->first();
            Cache::forever('remember_token'.session()->getId(), $user->remember_token);
                
                
            //根据权限点进行跳转
            if (strstr($referer, 'auth/login') != false) {
                //登出账号密码再次验证
                Auth::logout();
                return redirect()->route('adminlogin');
            }
            return redirect()->route('redirect', ['target' => $referer]);
        }
        //未认证,本地登出,跳转认证
        Auth::logout();
        return cas()->authenticate();
    }


    private function addRes($user, $casAtt)
    {
        //...
    }

    private function updateRes($user, $casAtt)
    {   //  移除用户的所有角色...
        $user->roles()->detach();
        $this->addRes($user, $casAtt);
    }

    private function doCertification($user, $casRoles, $casAtt)
    {
        //判断账户类型
       //...
    }

    private function updateUserInfoWhenChange($user, $attributes)
    {
        //本地用户信息和Cas信息差别更新
        $oddattributes = $user->getAttributes();
        $result = array_diff_assoc($attributes, $oddattributes);
        if ($result != null) {
            $attributes['updated_at'] = time();
            $user->update($attributes);
        }
    }


    public function logout(Request $request)
    {
        $this->guard()->logout();

        $request->session()->invalidate();

        //本地登出
        Auth::logout();
        //Cas登出
        cas()->logout();
    }


    /**
     * Get the login username to be used by the controller.
     *
     * @return string
     * @author Seven Du <shiweidu@outlook.com>
     */
    protected function username(): string
    {
        return username(
            cas()->user()
        );
    }

    /**
     * Get the post register / login redirect path.
     *
     * @return string
     * @author Seven Du <shiweidu@outlook.com>
     */
    protected function redirectTo(): string
    {
        return '/feeds';
    }

    /**
     * Get the guard to be used during authentication.
     *
     * @return \Illuminate\Contracts\Auth\StatefulGuard
     */
    protected function guard()
    {
        return Auth::guard();
    }
}

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-12-14 15:43:57  更:2021-12-14 15:44:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 14:50:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码