日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

CAS客户端整合(三) Otrs

發(fā)布時(shí)間:2023/12/13 综合教程 36 生活家
生活随笔 收集整理的這篇文章主要介紹了 CAS客户端整合(三) Otrs 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

OTRS 是用Perl寫(xiě)的一個(gè)工單郵件系統(tǒng),非常強(qiáng)大。

登錄流程

流程圖略過(guò)
otrs沒(méi)有像 discuz 和 zabbix 類(lèi)似的游客登錄狀態(tài),這樣處理起來(lái)邏輯分支少一些。
不過(guò)還是考慮用 otrs 的 session 機(jī)制,這樣可以在用戶(hù)已登錄的時(shí)候減少想 CAS-server 認(rèn)證的次數(shù)。
因此,登錄的流程基本跟Zabbix一致

Perl-cas 客戶(hù)端

perl 沒(méi)有官方的 cas 客戶(hù)端代碼。網(wǎng)上找到一個(gè)比較新的 perlcashttps://subversion.renater.fr/perlcas/trunk/
直接用的話有點(diǎn)問(wèn)題,需要做點(diǎn)修改。
cas-server 默認(rèn)使用https協(xié)議,因此需要修改 perlcas 的 curl 操作,這里是 get_https2() 方法, 改成 LWP::UserAgent

sub get_https2 {
    my $host = shift;
    my $port = shift;
    my $path = shift;

    unless ( eval "require LWP::UserAgent" ) {
        $errors = sprintf
"Unable to use LWP library, LWP::UserAgent required, install LWP (CPAN) first
";
        return undef;
    }
    require LWP::UserAgent;

    # user LWP::UserAgent
    my $ua = LWP::UserAgent->new(
        protocols_allowed => ['http', 'https'],
        timeout	          => 30,
        ssl_opt           => {
            verify_hostname => 0
        }
    );
    $ua->default_header('cookie'=>'');
    my $url = 'http://'.$host.':'.$port.$path;
    my $response = $ua->get($url);
    return $response->{_content};
}

改之前返回的是一個(gè)xml列表,改之后變成了一整個(gè)xml字符串,因此在callCAS()作以下修改:

sub callCAS {
    my $self = shift;
    my $url  = shift;

    my ( $host, $port, $path ) = &_parse_url($url);

    my $xml = get_https2(
        $host, $port, $path,
        {
            'cafile'      => $self->{'CAFile'},
            'capath'      => $self->{'CAPath'},
            'SSL_version' => $self->{'SSL_version'}
        }
    );

    # use Data::Dumper; die ''.Dumper($xml);

    # unless ($xml && $#$xml >= 0) {
    #     warn $errors;
    #     return undef;
    # }

    # ## Skip HTTP header fields
    # my $line = shift @$xml;
    # while ( $line !~ /^s*$/ ) {
    #     $line = shift @$xml;
    # }
    return &_parse_xml( $xml );
}

修改登錄過(guò)程

OTRS 的用戶(hù)登錄驗(yàn)證都在 Kernel/System/Web/InterfaceAgent.pm, 根據(jù) OTRS 開(kāi)發(fā)建議,我們復(fù)制原文件到 `Custom/Kernel/System/Web/InterfaceAgent.pm',然后再進(jìn)行修改。
為了方便調(diào)用, 增加兩個(gè)自定義方法:

# logout cas.
# 2017-11-14 by Carl
sub _logoutCAS {
    my $Self = shift;

    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');

    my $wsCAS = new AuthCAS(
        casUrl => "http://cas.cloud.com:8088/cas-server",
    );
    my $app = 'http://'.$ENV{SERVER_NAME}.$ENV{REQUEST_URI};
    # 非常關(guān)鍵,
    $app =~ s/?$ENV{QUERY_STRING}//;
    ## Redirect the User for login at CAS
    ## This step is not required if we already have a PGT (Proxy Granting Ticket)
    my $login_url = $wsCAS->getServerLogoutURL($app);
    my $cookie = $ParamObject->SetCookie(
                        Key      => $ConfigObject->Get('SessionName') || 'SessionID',
                        Value    => '',
                        Expires  => '-1y',
                        Path     => $ConfigObject->Get('ScriptAlias'),
                        Secure   => 0,
                        HTTPOnly => 1,
                        );
    printf "Set-Cookie: $cookie
Content-Type: text/html; charset=UTF-8;
Status: 302 Found
Location: %s

", $login_url;
    exit 0;
    return 1;
}

# login cas.
# 2017-11-14 by Carl
sub _loginCAS {
    my $Self = shift;

    my $wsCAS = new AuthCAS(
        casUrl => "http://cas.cloud.com:8088/cas-server",
    );
    my $app = 'http://'.$ENV{SERVER_NAME}.$ENV{REQUEST_URI};
    my $login_url = $wsCAS->getServerLoginURL($app);
    unless ($ENV{'QUERY_STRING'} =~ /ticket=/) {
        ## Redirect the User for login at CAS
        ## This step is not required if we already have a PGT (Proxy Granting Ticket)
        my $login_url = $wsCAS->getServerLoginURL($app);

        printf "Location: $login_url

";
        exit 0;
    }

    my $ST;
    # 非常重要,否則會(huì)導(dǎo)致cas-token反復(fù)認(rèn)證失敗
    $ENV{'QUERY_STRING'} =~ /ticket=([^&]+)/;
    $ST = $1;
    # very important!
    # Remove ST-ticket from query string
    $app =~ s/&ticket(=[^&]*)?|?ticket(=[^&]*)?&?//;
    my $User = $wsCAS->validateST($app, $ST);
    return $User;
}

首先處理登出請(qǐng)求

# Handle CAS-Server logout request.
# 2017-11-13 by Carl.
if ( $ParamObject->GetParam(Param => 'logoutRequest') || $Param{Action} eq 'Logout' ) {
# logout
# elsif ( $Param{Action} eq 'Logout' ) {

    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');

    # check session id
    if ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) {
        $Self->_logoutCAS();
        return 1;
    #...  if 分支結(jié)束后也要掉用一次,這里省略不表

考慮沒(méi)有系統(tǒng)session的情況:

# show login site
elsif ( !$Param{SessionID} ) {
    
    # use CAS Auth login
    # 2017-11-13 by Carl.

    my $User = $Self->_loginCAS();

    # login is successful
    my %UserData = $UserObject->GetUserData(
        User  => $User,
        Valid => 1
    );

這樣保證無(wú)session的時(shí)候必先驗(yàn)證 cas,驗(yàn)證通過(guò)才有session。退出系統(tǒng)清除session
然而,這里出現(xiàn)一個(gè)bug。因?yàn)閛trs的session也是寫(xiě)在本地cookie,而調(diào)用logout的時(shí)候直接重定向到了cas-server。此時(shí)本地cookie未被清除。這就是為什么logout方法中需要輸出 Set-cookie文件頭的原因。(注:此處表現(xiàn)了對(duì)http協(xié)議還不夠熟悉。服務(wù)端的logout請(qǐng)求只與cas-client端的web服務(wù)器交互,無(wú)法影響瀏覽器客戶(hù)端的cookie!)

小結(jié)

代碼仍舊很簡(jiǎn)單。難點(diǎn)在于除了要分析otrs的登錄流程,還需要自己構(gòu)造perl客戶(hù)端。
不過(guò)正由于如此,反而對(duì)cas認(rèn)證機(jī)制有了更清晰的認(rèn)識(shí)。之前雖然修改了幾個(gè)系統(tǒng),但是對(duì)cas的過(guò)程并沒(méi)有認(rèn)真分析。
客戶(hù)端驗(yàn)證cas的時(shí)候,先向服務(wù)器驗(yàn)證本地isAuthenticated,未成功則發(fā)起登錄請(qǐng)求。這是第一次302
服務(wù)端發(fā)現(xiàn)已有用戶(hù)登錄,直接根據(jù)客戶(hù)端請(qǐng)求中的service參數(shù),獲取重定向地址,并附帶 Server-token 。這是第二次302
客戶(hù)端收到 server-token 之后,拿這個(gè)ST 向服務(wù)端發(fā)起 curl 請(qǐng)求獲取xml用戶(hù)信息。此時(shí)需要一并傳入之前發(fā)起認(rèn)證時(shí)的客戶(hù)端url(未帶st參數(shù))。
這時(shí)如果直接使用 REQUEST_URI (攜帶了ST參數(shù)) 將導(dǎo)致認(rèn)證失敗。
成功的xml:

失敗的xml:

php-client 的做法是獲取用戶(hù)信息后寫(xiě)入session, 然后直接再次重定向到?jīng)]有ST參數(shù)的頁(yè)面. 這是第三次302

如何才能保留cookie機(jī)制的同時(shí)認(rèn)證cas?
我的想法是在接受服務(wù)端的 logoutRequest的時(shí)候清楚數(shù)據(jù)庫(kù)中的session(或使之過(guò)期)。
但是目前發(fā)現(xiàn),在其他客戶(hù)端推出的時(shí)候,好像收不到服務(wù)端的登出通知?
日志中也無(wú)法得知
繼續(xù)探索

============================================================================

【2017-11-17】修復(fù)客戶(hù)端同步退出問(wèn)題。

查看Apache的access_log發(fā)現(xiàn),其實(shí)cas-server有發(fā)送 logoutRequest:

經(jīng)過(guò)一個(gè)下午的調(diào)試研究,終于解決了同步退出的問(wèn)題,下面是過(guò)程:

Otrs的session機(jī)制

用戶(hù)登錄后,會(huì)在后臺(tái)sessions表創(chuàng)建大量session變量,當(dāng)用戶(hù)推出后,會(huì)根據(jù)session_id 清空所有變量。

解決方案

既然不能清除瀏覽器的cookie,那么我們可以清除sessions表里的session_id,從而使從瀏覽器讀取的cookie無(wú)法驗(yàn)證通過(guò),達(dá)到退出登錄的目的。
研究cas-server的POST請(qǐng)求發(fā)現(xiàn),只有一個(gè)參數(shù):
logoutRequest: <samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="LR-2105-Kha1YoKgOCpxoCHuPf7qrdYVMTHvH4HYRVK" Version="2.0" IssueInstant="2017-11-17T16:20:03Z"><saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">@NOT_USED@</saml:NameID><samlp:SessionIndex>ST-2105-6fyJS6MtEhRUKsBqECQG-cas.cloud.com</samlp:SessionIndex></samlp:LogoutRequest>
唯一有用的就是里面的ST token。這個(gè)在登錄的時(shí)候也能獲取到。
然后需要做的就是吧這個(gè)ST 與 Otrs產(chǎn)生的 SessionID 綁定。
我們?cè)诘卿洺晒螅瑒?chuàng)建一個(gè) CasServerToken的 session 變量。然后在退出的時(shí)候根據(jù)這個(gè)變量值查找對(duì)應(yīng)的session_id, 刪除這個(gè) session_id.

代碼過(guò)程

1) 登錄的時(shí)候保存ST my ($User, $CASST) = $Self->_loginCAS();,在后面創(chuàng)建了SessionID的位置加上:

    # login cas. save cas-ST
    # 2017-11-17 by Carl.
    $SessionObject->UpdateSessionID(
        SessionID => $NewSessionID,
        Key       => 'CasServerToken',
        Value     => $CASST,
    );

2) 處理同步登出請(qǐng)求:

# Handle CAS-Server logout request.
# 2017-11-13 by Carl.
if ( $ParamObject->GetParam(Param => 'logoutRequest') ){
    # handle logout request

    # read CAS ST ticket from logout request.
    my $CasServerToken = $ParamObject->GetParam(Param => 'logoutRequest');
    $CasServerToken =~ s/^.*<samlp:SessionIndex>(ST-.*)</samlp:SessionIndex>.*$/$1/;
    # find current session ID.
    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
    $DBObject->Prepare(
        SQL => "
            SELECT session_id
            FROM sessions
            WHERE data_key = 'CasServerToken' AND data_value = ?
            LIMIT 1 ",
        Bind => [ $CasServerToken ],
    );
    my @Row = $DBObject->FetchrowArray();
    if (my $CasSessionID = $Row[0]) {
        # Remove session ID.
        $SessionObject->RemoveSessionID( SessionID => $CasSessionID );
    }
    exit 0;
}

=============================================================
至此,otrs 的cas接入完成。

總結(jié)

以上是生活随笔為你收集整理的CAS客户端整合(三) Otrs的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。