CAS客户端整合(三) Otrs
OTRS 是用Perl寫的一個工單郵件系統,非常強大。
登錄流程
流程圖略過
otrs沒有像 discuz 和 zabbix 類似的游客登錄狀態,這樣處理起來邏輯分支少一些。
不過還是考慮用 otrs 的 session 機制,這樣可以在用戶已登錄的時候減少想 CAS-server 認證的次數。
因此,登錄的流程基本跟Zabbix一致
Perl-cas 客戶端
perl 沒有官方的 cas 客戶端代碼。網上找到一個比較新的 perlcashttps://subversion.renater.fr/perlcas/trunk/
直接用的話有點問題,需要做點修改。
cas-server 默認使用https協議,因此需要修改 perlcas 的 curl 操作,這里是 get_https2() 方法, 改成 LWP::UserAgent
改之前返回的是一個xml列表,改之后變成了一整個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 ); }修改登錄過程
OTRS 的用戶登錄驗證都在 Kernel/System/Web/InterfaceAgent.pm, 根據 OTRS 開發建議,我們復制原文件到 `Custom/Kernel/System/Web/InterfaceAgent.pm',然后再進行修改。
為了方便調用, 增加兩個自定義方法:
首先處理登出請求
# 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 idif ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) {$Self->_logoutCAS();return 1;#... if 分支結束后也要掉用一次,這里省略不表考慮沒有系統session的情況:
# show login site elsif ( !$Param{SessionID} ) {# use CAS Auth login# 2017-11-13 by Carl.my $User = $Self->_loginCAS();# login is successfulmy %UserData = $UserObject->GetUserData(User => $User,Valid => 1);這樣保證無session的時候必先驗證 cas,驗證通過才有session。退出系統清除session
然而,這里出現一個bug。因為otrs的session也是寫在本地cookie,而調用logout的時候直接重定向到了cas-server。此時本地cookie未被清除。這就是為什么logout方法中需要輸出 Set-cookie文件頭的原因。(注:此處表現了對http協議還不夠熟悉。服務端的logout請求只與cas-client端的web服務器交互,無法影響瀏覽器客戶端的cookie!)
小結
代碼仍舊很簡單。難點在于除了要分析otrs的登錄流程,還需要自己構造perl客戶端。
不過正由于如此,反而對cas認證機制有了更清晰的認識。之前雖然修改了幾個系統,但是對cas的過程并沒有認真分析。
客戶端驗證cas的時候,先向服務器驗證本地isAuthenticated,未成功則發起登錄請求。這是第一次302
服務端發現已有用戶登錄,直接根據客戶端請求中的service參數,獲取重定向地址,并附帶 Server-token 。這是第二次302
客戶端收到 server-token 之后,拿這個ST 向服務端發起 curl 請求獲取xml用戶信息。此時需要一并傳入之前發起認證時的客戶端url(未帶st參數)。
這時如果直接使用 REQUEST_URI (攜帶了ST參數) 將導致認證失敗。
成功的xml:
失敗的xml:
php-client 的做法是獲取用戶信息后寫入session, 然后直接再次重定向到沒有ST參數的頁面. 這是第三次302
如何才能保留cookie機制的同時認證cas?
我的想法是在接受服務端的 logoutRequest的時候清楚數據庫中的session(或使之過期)。
但是目前發現,在其他客戶端推出的時候,好像收不到服務端的登出通知?
日志中也無法得知
繼續探索
============================================================================
【2017-11-17】修復客戶端同步退出問題。
查看Apache的access_log發現,其實cas-server有發送 logoutRequest:
經過一個下午的調試研究,終于解決了同步退出的問題,下面是過程:
Otrs的session機制
用戶登錄后,會在后臺sessions表創建大量session變量,當用戶推出后,會根據session_id 清空所有變量。
解決方案
既然不能清除瀏覽器的cookie,那么我們可以清除sessions表里的session_id,從而使從瀏覽器讀取的cookie無法驗證通過,達到退出登錄的目的。
研究cas-server的POST請求發現,只有一個參數:
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。這個在登錄的時候也能獲取到。
然后需要做的就是吧這個ST 與 Otrs產生的 SessionID 綁定。
我們在登錄成功后,創建一個 CasServerToken的 session 變量。然后在退出的時候根據這個變量值查找對應的session_id, 刪除這個 session_id.
代碼過程
1) 登錄的時候保存ST my ($User, $CASST) = $Self->_loginCAS();,在后面創建了SessionID的位置加上:
# login cas. save cas-ST# 2017-11-17 by Carl.$SessionObject->UpdateSessionID(SessionID => $NewSessionID,Key => 'CasServerToken',Value => $CASST,);2) 處理同步登出請求:
# 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_idFROM sessionsWHERE 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接入完成。
轉載于:https://www.cnblogs.com/dapianzi/p/7834250.html
總結
以上是生活随笔為你收集整理的CAS客户端整合(三) Otrs的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 东北大学材料成型机械设备 轧辊强度校核习
- 下一篇: HBase实现谷粒微博案例