首先,cas 服務端的執行流程是WEB-INF/login-webflow.xml定義的,可以大概看看。 我用的代碼是cas-server-3.5.2-release.zip和cas-client-3.2.1-release.zip。
(1)直接訪問server端login頁面 請求:
GET /cas/login HTTP/1.1 ...省略..
響應:
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Pragma: no-cache Expires: Thu, 01 Jan 1970 00:00:00 GMT Cache-Control: no-cache Cache-Control: no-store Set-Cookie: JSESSIONID=2DA712066025406394C7C644AF16FD18; Path=/cas Content-Type: text/html;charset=UTF-8 Content-Length: 1993 Date: Wed, 30 Oct 2013 01:53:49 GMT
內容就是登陸表單,省略
對於Server端流程來說,走的是
<decision-state id="ticketGrantingTicketExistsCheck"> <if test="flowScope.ticketGrantingTicketId != null" then="hasServiceCheck" else="gatewayRequestCheck" /> </decision-state>
TGT不存在,走gatewayRequestCheck
<decision-state id="gatewayRequestCheck"> <if test="requestParameters.gateway != '' and requestParameters.gateway != null and flowScope.service != null" then="gatewayServicesManagementCheck" else="serviceAuthorizationCheck" /> </decision-state>
走serviceAuthorizationCheck
<action-state id="serviceAuthorizationCheck"> <evaluate expression="serviceAuthorizationCheck"/> <transition to="generateLoginTicket"/> </action-state>
執行認證check,然後轉到generateLoginTicket
<action-state id="generateLoginTicket"> <evaluate expression="generateLoginTicketAction.generate(flowRequestContext)" /> <transition on="generated" to="viewLoginForm" /> </action-state>
生成登錄小票,轉到login表單
<view-state id="viewLoginForm" view="casLoginView" model="credentials"> <binder> <binding property="username" /> <binding property="password" /> </binder> <on-entry> <set name="viewScope.commandName" value="'credentials'" /> </on-entry> <transition on="submit" bind="true" validate="true" to="realSubmit"> <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" /> </transition> </view-state>
對應的jsp頁面是WEB-INF\view\jsp\default\ui\casLoginView.jsp -------------------------------------------------------------------------- (2)輸入用戶名、密碼,登錄 請求:
POST /cas/login;jsessionid=2DA712066025406394C7C644AF16FD18 HTTP/1.1 Content-Type: application/x-www-form-urlencoded ...部分省略... Cookie: JSESSIONID=2DA712066025406394C7C644AF16FD18
username=admin&password=1&warn=true<=LT-3-Ahdqm03UKMR9vwuy7A5ZSCMa9zJkNi&execution=e1s1&_eventId=submit&x=53&y=55
其中的LT-3-Ahdqm03UKMR9vwuy7A5ZSCMa9zJkNi就是登錄小票, 後臺會比較“提交的值”和“後臺保存的是否一致”。這個類似於驗證碼,沒啥說的。
響應: HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Pragma: no-cache Expires: Thu, 01 Jan 1970 00:00:00 GMT Cache-Control: no-cache Cache-Control: no-store Set-Cookie: CASPRIVACY=true; Path=/cas/; Secure Set-Cookie: CASTGC=TGT-2-DmMvVs9Wy01ScbnMtgtLzgcyX5X14TGcM1f5iGuWIBrlDwTg4Q-cas01.example.org; Path=/cas/ Content-Type: text/html;charset=UTF-8 Content-Length: 1602 Date: Wed, 30 Oct 2013 01:54:03 GMT
後臺設置了兩個cookie,一個是CASPRIVACY=true,不知道幹嘛用的 一個是CASTGC,這個就是TicketGrantingTicket Cookie,就是說,你想買通票,你還得先買門票進去的意思,tgc就是門票。 這樣,客戶端瀏覽器就擁有了TGC門票。下次再訪問cas server時就會帶着這個門票來了。
後臺執行流程: 由上面viewLoginForm的定義,表單提交後,會執行authenticationViaFormAction.doBind方法,然後跳到realSubmit
<action-state id="realSubmit"> <evaluate expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credentials, messageContext)" /> <transition on="warn" to="warn" /> <transition on="success" to="sendTicketGrantingTicket" /> <transition on="error" to="generateLoginTicket" /> <transition on="accountDisabled" to="casAccountDisabledView" /> <transition on="mustChangePassword" to="casMustChangePassView" /> <transition on="accountLocked" to="casAccountLockedView" /> <transition on="badHours" to="casBadHoursView" /> <transition on="badWorkstation" to="casBadWorkstationView" /> <transition on="passwordExpired" to="casExpiredPassView" /> </action-state>
執行authenticationViaFormAction.submit方法, 看一下代碼:
public final String submit(final RequestContext context, final Credentials credentials, final MessageContext messageContext) throws Exception { // Validate login ticket final String authoritativeLoginTicket = WebUtils.getLoginTicketFromFlowScope(context); final String providedLoginTicket = WebUtils.getLoginTicketFromRequest(context); if (!authoritativeLoginTicket.equals(providedLoginTicket)) { this.logger.warn("Invalid login ticket " + providedLoginTicket); final String code = "INVALID_TICKET"; messageContext.addMessage( new MessageBuilder().error().code(code).arg(providedLoginTicket).defaultText(code).build()); return "error"; }
final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context); final Service service = WebUtils.getService(context); if (StringUtils.hasText(context.getRequestParameters().get("renew")) && ticketGrantingTicketId != null && service != null) {
try { final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicketId, service, credentials); WebUtils.putServiceTicketInRequestScope(context, serviceTicketId); putWarnCookieIfRequestParameterPresent(context); return "warn"; } catch (final TicketException e) { if (isCauseAuthenticationException(e)) { populateErrorsInstance(e, messageContext); return getAuthenticationExceptionEventId(e); }
this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId); if (logger.isDebugEnabled()) { logger.debug("Attempted to generate a ServiceTicket using renew=true with different credentials", e); } } }
try { WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService.createTicketGrantingTicket(credentials)); putWarnCookieIfRequestParameterPresent(context); return "success"; } catch (final TicketException e) { populateErrorsInstance(e, messageContext); if (isCauseAuthenticationException(e)) return getAuthenticationExceptionEventId(e); return "error"; } }
service就是指子系統的URL,由於直接訪問的server端login,所以URL中沒有service參數,所以grantServiceTicket不會執行。 最後,會createTicketGrantingTicket,然後放到request中。 返回success,跳轉到sendTicketGrantingTicket
<action-state id="sendTicketGrantingTicket"> <evaluate expression="sendTicketGrantingTicketAction" /> <transition to="serviceCheck" /> </action-state>
這裏是把TicketGrantingTicket寫到Cookie。 然後跳到serviceCheck
<!-- <decision-state id="serviceCheck"> <if test="flowScope.service != null" then="generateServiceTicket" else="getSubSystemUrl" /> </decision-state> --> <decision-state id="serviceCheck"> <if test="flowScope.service != null" then="getSubSystemUrl" else="getSubSystemUrl" /> </decision-state>
<action-state id="generateServiceTicket"> <evaluate expression="generateServiceTicketAction" /> <transition on="success" to ="warn" /> <transition on="error" to="generateLoginTicket" /> <transition on="gateway" to="gatewayServicesManagementCheck" /> </action-state>
<action-state id="getSubSystemUrl"> <evaluate expression="getSubSystemUrlAction.get(flowRequestContext, flowScope.credentials)" /> <transition to ="viewGenericLoginSuccess" /> </action-state>
這段被我改過,原來是:有service到generateServiceTicket,沒有直接到viewGenericLoginSuccess 我改成了,都跳到getSubSystemUrl。這個action是我自己加的,目的是去數據庫取得子系統URL,並取得用戶對每個子系統的權限。 然後跳轉到viewGenericLoginSuccess
<end-state id="viewGenericLoginSuccess" view="casLoginGenericSuccessView" />
viewGenericLoginSuccess,也就是登錄成功頁面,就是服務端WEB-INF\view\jsp\default\ui\casGenericSuccess.jsp這個頁面。 我在這裏顯示了幾個子系統的鏈接。
這裏的view寫的都是id,實際對應的文件在WEB-INF\classes\default_views.properties中定義。
-------------------------------------------------------------------------- (3)然後點擊頁面上的鏈接,訪問子系統 請求:
GET /UserManage/ HTTP/1.1
響應:
HTTP/1.1 302 Moved Temporarily Server: Apache-Coyote/1.1 Location: http://10.0.103.137:8080/cas/login?service=http%3A%2F%2F10.0.103.137%3A8080%2FUserManage%2F Content-Length: 0 Date: Wed, 30 Oct 2013 01:54:22 GMT
由於子系統增加了CAS client的Filter。
<filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <!-- CAS login 服務地址--> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>http://10.0.103.137:8080/cas/login</param-value> </init-param> <!-- 客戶端應用服務地址--> <init-param> <param-name>serverName</param-name> <param-value>http://10.0.103.137:8080</param-value> </init-param> <init-param> <param-name>renew</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>gateway</param-name> <param-value>false</param-value> </init-param> </filter>
看一下AuthenticationFilter的代碼:
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; final HttpSession session = request.getSession(false); final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
if (assertion != null) { filterChain.doFilter(request, response); return; }
final String serviceUrl = constructServiceUrl(request, response); final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName()); final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
if (CommonUtils.isNotBlank(ticket) || wasGatewayed) { filterChain.doFilter(request, response); return; }
final String modifiedServiceUrl;
log.debug("no ticket and no assertion found"); if (this.gateway) { log.debug("setting gateway attribute in session"); modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl); } else { modifiedServiceUrl = serviceUrl; }
if (log.isDebugEnabled()) { log.debug("Constructed service url: " + modifiedServiceUrl); }
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
if (log.isDebugEnabled()) { log.debug("redirecting to \"" + urlToRedirectTo + "\""); }
response.sendRedirect(urlToRedirectTo); }
從Session取assertion,就是登錄信息,如果有,就繼續執行其他Filter。 然後看有沒有ticket,也就是通票。有的話,繼續執行其他Filter。 else就拼接URL,跳轉。 http://10.0.103.137:8080/cas/login?service=http%3A%2F%2F10.0.103.137%3A8080%2FUserManage%2F
(4)跳轉到server端了 請求:
GET /cas/login?service=http%3A%2F%2F10.0.103.137%3A8080%2FUserManage%2F HTTP/1.1 Cookie: CASTGC=TGT-2-DmMvVs9Wy01ScbnMtgtLzgcyX5X14TGcM1f5iGuWIBrlDwTg4Q-cas01.example.org; JSESSIONID=2DA712066025406394C7C644AF16FD18
可以看到,瀏覽器自動把CASTGC帶過來了。
響應:
HTTP/1.1 302 Moved Temporarily Server: Apache-Coyote/1.1 Pragma: no-cache Expires: Thu, 01 Jan 1970 00:00:00 GMT Cache-Control: no-cache Cache-Control: no-store Location: http://10.0.103.137:8080/UserManage/?ticket=ST-1-0dnlMstfctMYVcat5wfy-cas01.example.org Content-Length: 0 Date: Wed, 30 Oct 2013 01:54:22 GMT
又跳回了子系統,但是帶着ticket過來的
後臺流程:
<decision-state id="ticketGrantingTicketExistsCheck"> <if test="flowScope.ticketGrantingTicketId != null" then="hasServiceCheck" else="gatewayRequestCheck" /> </decision-state>
CASTGC有了,所以走hasServiceCheck
<decision-state id="hasServiceCheck"> <if test="flowScope.service != null" then="renewRequestCheck" else="getSubSystemUrl" /> </decision-state>
service參數也有了,所以走renewRequestCheck
<decision-state id="renewRequestCheck"> <if test="requestParameters.renew != '' and requestParameters.renew != null" then="serviceAuthorizationCheck" else="generateServiceTicket" /> </decision-state>
renew客戶端配置的是false,所以請求裏無此參數。走generateServiceTicket
<action-state id="generateServiceTicket"> <evaluate expression="generateServiceTicketAction" /> <transition on="success" to ="warn" /> <transition on="error" to="generateLoginTicket" /> <transition on="gateway" to="gatewayServicesManagementCheck" /> </action-state>
看下代碼:
protected Event doExecute(final RequestContext context) { final Service service = WebUtils.getService(context); final String ticketGrantingTicket = WebUtils.getTicketGrantingTicketId(context);
try { final String serviceTicketId = this.centralAuthenticationService .grantServiceTicket(ticketGrantingTicket, service); WebUtils.putServiceTicketInRequestScope(context, serviceTicketId); return success(); } catch (final TicketException e) { if (isGatewayPresent(context)) { return result("gateway"); } }
return error(); }
根據ticketGrantingTicket和service,生成通票,並放到request中。成功跳到warn
<decision-state id="warn"> <if test="flowScope.warnCookieValue" then="showWarningView" else="redirect" /> </decision-state>
跳轉時,不顯示提醒頁面,所以直接走redirect.
<action-state id="redirect"> <evaluate expression="flowScope.service.getResponse(requestScope.serviceTicketId)" result-type="org.jasig.cas.authentication.principal.Response" result="requestScope.response" /> <transition to="postRedirectDecision" /> </action-state>
<decision-state id="postRedirectDecision"> <if test="requestScope.response.responseType.name() == 'POST'" then="postView" else="redirectView" /> </decision-state>
這個看不太懂,反正最後執行redirectView了。
<end-state id="redirectView" view="externalRedirect:${requestScope.response.url}" />
執行跳轉,哦了
(5)又回到客戶端了 請求:
GET /UserManage/?ticket=ST-1-0dnlMstfctMYVcat5wfy-cas01.example.org HTTP/1.1
響應:
HTTP/1.1 302 Moved Temporarily Server: Apache-Coyote/1.1 Set-Cookie: JSESSIONID=3A8260292C0430806E231B0E76907137; Path=/UserManage Location: http://10.0.103.137:8080/UserManage/;jsessionid=3A8260292C0430806E231B0E76907137 Content-Length: 0 Date: Wed, 30 Oct 2013 01:54:24 GMT
這個有點莫名其妙,又跳轉了。
這時,是客戶端的CAS Validation Filter在作怪。
<filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://10.0.103.137:8080/cas/</param-value> </init-param> <!-- 客戶端應用服務地址--> <init-param> <param-name>serverName</param-name> <param-value>http://10.0.103.137:8080</param-value> </init-param> </filter>
它發現URL中有ticket了,馬上就拿它去服務端check一下,看是否有效,是否過期什麼的。 這裏用的是Cas20ProxyReceivingTicketValidationFilter,它繼承了AbstractTicketValidationFilter 看看它的doFilter代碼:
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
if (!preFilter(servletRequest, servletResponse, filterChain)) { return; }
final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
if (CommonUtils.isNotBlank(ticket)) { if (log.isDebugEnabled()) { log.debug("Attempting to validate ticket: " + ticket); }
try { final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));
if (log.isDebugEnabled()) { log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName()); }
request.setAttribute(CONST_CAS_ASSERTION, assertion);
if (this.useSession) { request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion); } onSuccessfulValidation(request, response, assertion);
if (this.redirectAfterValidation) { log. debug("Redirecting after successful ticket validation."); response.sendRedirect(constructServiceUrl(request, response)); return; } } catch (final TicketValidationException e) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); log.warn(e, e);
onFailedValidation(request, response);
if (this.exceptionOnValidationFailure) { throw new ServletException(e); }
return; } }
filterChain.doFilter(request, response);
}
取ticket,然後 final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response)) 看看,直接就返回了assertion,這就是登錄者信息,包括用戶名什麼的。 然後放到request和session裏了。
validate方法,實際是調用的AbstractUrlBasedTicketValidator的validate方法,看看
final String validationUrl = constructValidationUrl(ticket, service); if (log.isDebugEnabled()) { log.debug("Constructing validation url: " + validationUrl); }
try { log.debug("Retrieving response from server."); final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket);
if (serverResponse == null) { throw new TicketValidationException("The CAS server returned no response."); }
if (log.isDebugEnabled()) { log.debug("Server response: " + serverResponse); }
return parseResponseFromServer(serverResponse); } catch (final MalformedURLException e) { throw new TicketValidationException(e); }
構建一個URL,然後讀取響應。實際調用的AbstractCasProtocolUrlBasedTicketValidator的retrieveResponseFromServer方法
protected final String retrieveResponseFromServer(final URL validationUrl, final String ticket) { if (this.hostnameVerifier != null) { return CommonUtils.getResponseFromServer(validationUrl, this.hostnameVerifier, getEncoding()); } else { return CommonUtils.getResponseFromServer(validationUrl, getEncoding()); } }
不知道走的那個,但都是getResponseFromServer方法,看看先。
public static String getResponseFromServer(final URL constructedUrl, final HostnameVerifier hostnameVerifier, final String encoding) { URLConnection conn = null; try { conn = constructedUrl.openConnection(); if (conn instanceof HttpsURLConnection) { ((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier); } final BufferedReader in;
if (CommonUtils.isEmpty(encoding)) { in = new BufferedReader(new InputStreamReader(conn.getInputStream())); } else { in = new BufferedReader(new InputStreamReader(conn.getInputStream(), encoding)); }
String line; final StringBuilder stringBuffer = new StringBuilder(255);
while ((line = in.readLine()) != null) { stringBuffer.append(line); stringBuffer.append("\n"); } return stringBuffer.toString(); } catch (final Exception e) { LOG.error(e.getMessage(), e); throw new RuntimeException(e); } finally { if (conn != null && conn instanceof HttpURLConnection) { ((HttpURLConnection)conn).disconnect(); } }
}
就是直接訪問URL,讀取數據。 ------------------------- 代碼看完,看抓包數據吧。 實際上,上面帶着ticket跳回客戶端後,Filter發起了一個訪問
請求:
GET /cas/serviceValidate?ticket=ST-1-0dnlMstfctMYVcat5wfy-cas01.example.org&service=http%3A%2F%2F10.0.103.137%3A8080%2FUserManage%2F%3Bjsessionid%3D3A8260292C0430806E231B0E76907137 HTTP/1.1 User-Agent: Java/1.6.0_26
看,User-Agent是Java哦~~
響應:
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: text/html;charset=UTF-8 Content-Language: zh-CN Content-Length: 177 Date: Wed, 30 Oct 2013 01:54:24 GMT
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> <cas:authenticationSuccess> <cas:user>admin</cas:user> </cas:authenticationSuccess> </cas:serviceResponse>
可以看到,服務端給返回的xml數據,只有用戶名。 實際上,想返回更多信息也是可以的,需要修改服務端配置,具體方法請搜索,大概是:cas 登錄 返回更多信息
服務端/serviceValidate這個請求是
<servlet> <servlet-name>cas</servlet-name> <servlet-class> org.jasig.cas.web.init.SafeDispatcherServlet </servlet-class> </servlet>
這個Servlet響應的,但沒看太明白,反正最後調到了ServiceValidateController的handleRequestInternal方法裏 裏面有一句: final Assertion assertion = this.centralAuthenticationService.validateServiceTicket(serviceTicketId, service); 實際是調用CentralAuthenticationServiceImpl的validateServiceTicket方法
這個類有
@NotNull private TicketRegistry ticketRegistry;
/** New Ticket Registry for storing and retrieving services tickets. Can point to the same one as the ticketRegistry variable. */ @NotNull private TicketRegistry serviceTicketRegistry;
實際上,ticket信息就是保存在這兩個變量裏的。
裏面有這樣的代碼:
final ServiceTicket serviceTicket = (ServiceTicket) this.serviceTicketRegistry.getTicket(serviceTicketId, ServiceTicket.class);
final List<Authentication> chainedAuthenticationsList = serviceTicket.getGrantingTicket().getChainedAuthentications(); final Authentication authentication = chainedAuthenticationsList.get(chainedAuthenticationsList.size() - 1); final Principal principal = authentication.getPrincipal();
所以,能拿到Ticket或grant ticket的話,就可以拿到當前用戶信息。
後面看不動了,大概就是把用戶信息寫到響應裏吧。
(6)跳轉到之前訪問的子系統URL 請求:
GET /UserManage/;jsessionid=3A8260292C0430806E231B0E76907137 HTTP/1.1 Cookie: JSESSIONID=3A8260292C0430806E231B0E76907137
實際上,到前一步,用戶信息已經去到,認證過程已經完畢了,單點登錄已經實現。 後面就是正常訪問子系統了。
但,每次請求,AuthenticationFilter仍然會檢查一下。 看一下AuthenticationFilter的代碼:
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; final HttpSession session = request.getSession(false); final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
if (assertion != null) { filterChain.doFilter(request, response); return; } .....
由於已經登錄,assertion不爲空,所以直接跳過了。對性能影響不大。
(7)執行logout時,實際是訪問server端的logout 請求:
GET /cas/logout HTTP/1.1 Cookie: CASTGC=TGT-2-DmMvVs9Wy01ScbnMtgtLzgcyX5X14TGcM1f5iGuWIBrlDwTg4Q-cas01.example.org; JSESSIONID=0F5355FE89E099FF14E149CF9C55644C
響應:
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Pragma: no-cache Expires: Thu, 01 Jan 1970 00:00:00 GMT Cache-Control: no-cache Cache-Control: no-store Set-Cookie: CASTGC=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/cas/ Set-Cookie: CASPRIVACY=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/cas/ Set-Cookie: JSESSIONID=6DA0ACAEBA3A801CB897E98CE5E92AFC; Path=/cas Content-Type: text/html;charset=UTF-8 Content-Language: zh-CN Content-Length: 728 Date: Wed, 30 Oct 2013 03:31:39 GMT
設置Cookie過期。Session總是新起一個。 然後內容裏有: <script>window.location="/cas/login";</script>
同時,會通知所有已登錄的子系統,進行退出操作
請求:
POST /UserManage/ HTTP/1.1 Content-Type: application/x-www-form-urlencoded Cache-Control: no-cache Pragma: no-cache User-Agent: Java/1.6.0_26 Host: 10.0.103.137:8080 Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive Content-Length: 484
logoutRequest=%3Csamlp%3ALogoutRequest+xmlns%3Asamlp%3D%22urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Aprotocol%22+ID%3D%22LR-2-0EQR24uNI0TlMr52CzUYMuf1fOAsQg1O447%22+Version%3D%222.0%22+IssueInstant%3D%222013-10-30T11%3A31%3A39Z%22%3E%3Csaml%3ANameID+xmlns%3Asaml%3D%22urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Aassertion%22%3E%40NOT_USED%40%3C%2Fsaml%3ANameID%3E%3Csamlp%3ASessionIndex%3EST-2-nfx6OgRedoAMqowQayXr-cas01.example.org%3C%2Fsamlp%3ASessionIndex%3E%3C%2Fsamlp%3ALogoutRequest%3E
響應:
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Length: 0 Date: Wed, 30 Oct 2013 03:31:39 GMT
響應是由客戶端的SingleSignOutFilter做出的。
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest;
if (handler.isTokenRequest(request)) { handler.recordSession(request); } else if (handler.isLogoutRequest(request)) { handler.destroySession(request); // Do not continue up filter chain return; } else { log.trace("Ignoring URI " + request.getRequestURI()); }
filterChain.doFilter(servletRequest, servletResponse); }
(8)奇怪的一點,退出後,在登錄頁面,馬上進行登錄, 提交後,服務端還會執行一次跳轉。
HTTP/1.1 302 Moved Temporarily Server: Apache-Coyote/1.1 Pragma: no-cache Expires: Thu, 01 Jan 1970 00:00:00 GMT Cache-Control: no-cache Cache-Control: no-store Set-Cookie: JSESSIONID=3911117B64346B50E24F36632C2419D7; Path=/cas Location: http://10.0.103.137:8080/cas/login Content-Length: 0 Date: Wed, 30 Oct 2013 03:37:07 GMT
非得重新生成一個session才幹~~導致登錄頁面又刷了一下,這時再登錄就OK了。 奇怪,奇怪~~ 原因不明,調查中~~ ---------------- 我靠: 登錄成功後,馬上點退出,就有此問題 等幾秒,再點退出,就無此問題。
原因是,登錄成功後,會馬上咔嚓掉session,默認是2秒後掛掉。 馬上點退出,Session還沒掛,所以不會重建Session, 所以,登錄頁面提交時,是帶着這個Session的ID去的,而此時Session掛掉了,故引起頁面刷新。
而等幾秒再退出,會創建新session,故後面無問題。
更多信息請看 http://www.mytju.com/classcode/news_readNews.asp?newsID=504 |