ok,現在開始本文的重點內容解說,先來了解一下cas 實現single sign out的原理,如圖所看到的:
圖一
圖二
第一張圖演示了單點登錄的工做原理。
第二張圖演示了單點登出的工做原理。
從第一張圖中。當一個web瀏覽器登陸到應用server時,應用server(application)會檢測用戶的session,假設沒有session,則應用server會把url跳轉到CASserver上,要求用戶登陸,用戶登陸成功後,CASserver會記請求的application的url和該用戶的sessionId(在應用server跳轉url時,經過參數傳給CASserver)。
此時在CASserver會種下TGC Cookie值到webbrowser.擁有該TGCCookie的webbrowser可以無需登陸進入所有創建sso服務的應用serverapplication。
在第二張圖中,當一個web瀏覽器要求登退應用server。應用server(application)會把url跳轉到CAS server上的/cas/logout url資源上,
CAS server接受請求後,會檢測用戶的TCG Cookie。把相應的session清除,同一時候會找到所有經過該TGCsso登陸的應用服務器URL提交請求,所有的回調請求中,包括一個參數logoutRequest,內容格式例如如下:
web
<
samlp:LogoutRequest
ID
="[RANDOM ID]"
Version
="2.0"
IssueInstant
="[CURRENT DATE/TIME]"
>
<
saml:NameID
>
@NOT_USED@
</
saml:NameID
>
<
samlp:SessionIndex
>
[SESSION IDENTIFIER]
</
samlp:SessionIndex
>
</
samlp:LogoutRequest
>
所有收到請求的應用serverapplication會解析這個參數,取得sessionId。依據這個Id取得session後。把session刪除。
這樣就實現單點登出的功能。
知道原理後,如下是結合源碼來說述一下內部的代碼怎麼實現的。
首先,要實現single sign out在 應用serverapplication端的web.xml要增長下面配置
<
filter
>
<
filter-name
>
CAS Single Sign Out Filter
</
filter-name
>
<
filter-class
>
org.jasig.cas.client.session.SingleSignOutFilter
</
filter-class
>
</
filter
>
<
filter-mapping
>
<
filter-name
>
CAS Single Sign Out Filter
</
filter-name
>
<
url-pattern
>
31
protected
static
SessionMappingStorage getSessionMappingStorage() {
32
return
SingleSignOutFilter.getSessionMappingStorage();
33
}
34
}
接下來,咱們來看一下CAS server端回調是怎麼實現的
先來看一下配置,咱們知道CASserver所有的用戶登陸,登出操做,都是由CentralAuthenticationSer
viceImpl對象來管理。
咱們就先把到CentralAuthenticationSer
viceImpl的spring配置,在applicationContext.xml文件裏
<!--
CentralAuthenticationService
-->
<
bean
id
="centralAuthenticationService"
class
="org.jasig.cas.CentralAuthenticationServiceImpl"
p:ticketGrantingTicketExpirationPolicy-ref
="grantingTicketExpirationPolicy"
p:serviceTicketExpirationPolicy-ref
="serviceTicketExpirationPolicy"
p:authenticationManager-ref
="authenticationManager"
p:ticketGrantingTicketUniqueTicketIdGenerator-ref
="ticketGrantingTicketUniqueIdGenerator"
p:ticketRegistry-ref
="ticketRegistry"
p:servicesManager-ref
="servicesManager"
p:persistentIdGenerator-ref
="persistentIdGenerator"
p:uniqueTicketIdGeneratorsForService-ref
="uniqueIdGeneratorsMap"
/>
配置使用了spring2.0的xsd。
CentralAuthenticationServiceImpl有一個屬性叫uniqueTicketIdGeneratorsForService,它是一個map對象
它的key值是所有實現org.jasig.cas.authentication.principal.Service接口的類名,用於保存Principal對象和進行單點登出回調
application server時使用value值爲org.jasig.cas.util.DefaultUniqueTicketIdGenerator對象,用於生成惟一的TGCticket。
該屬性引用的uniqueIdGeneratorsMap bean在uniqueIdGenerators.xml配置文件裏。
spring
<
util:map
id
="uniqueIdGeneratorsMap"
>
<
entry
key
="org.jasig.cas.authentication.principal.SimpleWebApplicationServiceImpl"
value-ref
="serviceTicketUniqueIdGenerator"
/>
<
entry
key
="org.jasig.cas.support.openid.authentication.principal.OpenIdService"
value-ref
="serviceTicketUniqueIdGenerator"
/>
<
entry
key
="org.jasig.cas.authentication.principal.SamlService"
value-ref
="samlServiceTicketUniqueIdGenerator"
/>
<
entry
key
="org.jasig.cas.authentication.principal.GoogleAccountsService"
value-ref
="serviceTicketUniqueIdGenerator"
/>
</
util:map
>
那CentralAuthenticationSer
viceImpl是怎麼調用的呢?
咱們跟蹤一下代碼,在建立ticket的方法 public StringcreateTicketGrantingTick
et(final Credentials credentials)中
可以找到下面這樣一段代碼:
1
//
建立 TicketGrantingTicketImpl 實例
2
final
TicketGrantingTicket ticketGrantingTicket
=
new
TicketGrantingTicketImpl(
3
this
.ticketGrantingTicketUniqueTicketIdGenerator
4
.getNewTicketId(TicketGrantingTicket.PREFIX),
5
authentication,
this
.ticketGrantingTicketExpirationPolicy);
6
//
並把該對象保存到 ticketRegistry中
7
this
.ticketRegistry.addTicket(ticketGrantingTicket);
上面的代碼,看到ticketRegistry對象保存了建立的TicketGrantingTicketImpl
對象,如下咱們看一下當ticket銷燬的時候。會作什麼
事情,代碼例如如下:
1
public
void
destroyTicketGrantingTicket(
final
String ticketGrantingTicketId) {
2
Assert.notNull(ticketGrantingTicketId);
3
4
if
(log.isDebugEnabled()) {
5
log.debug(
"
Removing ticket [
"
+
ticketGrantingTicketId
6
+
"
] from registry.
"
);
7
}
8
//
從 ticketRegistry對象中。取得TicketGrantingTicket對象
9
final
TicketGrantingTicket ticket
=
(TicketGrantingTicket)
this
.ticketRegistry
10
.getTicket(ticketGrantingTicketId, TicketGrantingTicket.
class
);
11
12
if
(ticket
==
null
) {
13
return
;
14
}
15
16
if
(log.isDebugEnabled()) {
17
log.debug(
"
Ticket found. Expiring and then deleting.
"
);
18
}
19
ticket.expire();
//
調用expire()方法,讓ticket過時失效
20
this
.ticketRegistry.deleteTicket(ticketGrantingTicketId);
//
從ticketRegistry中刪除的ticket 對象
21
}
咱們看到,它是從
ticketRegistry對象中取得
TicketGrantingTicket對象後,調用expire方法。接下來。要關心的就是expire方法作什麼事情
1
public
synchronized
void
expire() {
2
this
.expired.set(
true
);
3
logOutOfServices();
4
}
5
6
private
void
logOutOfServices() {
7
for
(
final
Entry
<
String, Service
>
entry :
this
.services.entrySet()) {
8
entry.getValue().logOutOfService(entry.getKey());
9
}
10
}
從代碼可以看到,它是遍歷每個 Service對象,並運行logOutOfService方法。參數是StringsessionIdentifier
現在咱們可以相應中,它存放的Service就是在
uniqueIdGeneratorsMap bean定義中的那些實現類
因爲logOutOfService方法的實現。所有實現類都是由它們繼承的抽象類AbstractWebApplicationSe
rvice來實現。咱們來看一下
AbstractWebApplicationSe
rvice的logOutOfService方法,就可以終於找出,實現singlesign out的真正實現代碼,如下是主要代碼片斷:
1
public
synchronized
boolean
logOutOfService(
final
String sessionIdentifier) {
2
if
(
this
.loggedOutAlready) {
3
return
true
;
4
}
5
6
LOG.debug(
"
Sending logout request for:
"
+
getId());
7
//
組裝 logoutRequest參數內容
8
final
String logoutRequest
=
"
<samlp:LogoutRequest xmlns:samlp=\
"
urn:oasis:names:tc:SAML:
2.0
:protocol\
"
ID=\
""
9
+
GENERATOR.getNewTicketId(
"
LR
"
)
10
+
"
\
"
Version
=
\
"
2.0\
"
IssueInstant
=
\
""
+
SamlUtils.getCurrentDateAndTime()
11
+
"
\
"
><
saml:NameID
12
13
xmlns:saml
=
\
"
urn:oasis:names:tc:SAML:2.0:assertion\
"
>
@NOT_USED@
</
saml:NameID
><
samlp:SessionIndex
>
"
14
+
sessionIdentifier
+
"
</samlp:SessionIndex></samlp:LogoutRequest>
"
;
15
16
this
.loggedOutAlready
=
true
;
17
//
回調所有的application,getOriginalUrl()是取得回調的application url
18
if
(
this
.httpClient
!=
null
) {
19
return
this
.httpClient.sendMessageToEndPoint(getOriginalUrl(), logoutRequest);
20
}
21
22
return
false
;
23
}
至此。已經經過源碼把 CAS實現 single signout的實現原理和方法完整敘述了一遍,但願對CAS感興趣的朋友有所幫忙。