改造CAS单点登录 --- 自定义登陆页面(服务端)
2014-02-13 14:23
441 查看
服务端通过自定义spring-flow流程实现登录和退出功能。
一、修改cas-server-core
1.在类AuthenticationViaFormAction中重载submit和validatorCode方法
2.自定义登录RemoteLoginAction
3.自定义退出RemoteLogoutAction
二、修改cas-server-webapp
1.web.xml增加登录、退出请求mapping映射的处理
2.在cas-servlet.xml增加请求流程
3.增加远程登录流程控制remoteLogin-webflow.xml
4.增加远程退出流程控制remoteLogout-webflow.xml
5.配置remoteCallbackView显示节点,修改src/default_views.properties文件,增加remoteCallbackView配置
6.创建/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp文件
服务器端修改完成。
一、修改cas-server-core
1.在类AuthenticationViaFormAction中重载submit和validatorCode方法
public final String submit(final RequestContext context, 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); final HttpServletRequest request = WebUtils.getHttpServletRequest(context); String username = request.getParameter("username"); if(!StringUtils.hasText(username)){ context.getFlowScope().put("errorCode", "required.username"); return "error"; } String password = request.getParameter("password"); if(!StringUtils.hasText(password)){ context.getFlowScope().put("errorCode", "required.password"); return "error"; } UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(); credentials.setUsername(username); credentials.setPassword(password); 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 (e.getCause() != null && AuthenticationException.class.isAssignableFrom(e.getCause().getClass())) { //populateErrorsInstance(e, messageContext); context.getFlowScope().put("errorCode", e.getCode()); return "error"; } 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); context.getFlowScope().put("errorCode", e.getCode()); return "error"; } }
public final String validatorCode(final RequestContext context, final MessageContext messageContext) throws Exception { final HttpServletRequest request = WebUtils.getHttpServletRequest(context); HttpSession session = request.getSession(); String authcode = (String)session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY); session.removeAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY); String submitAuthcode =request.getParameter("authcode"); if(!StringUtils.hasText(submitAuthcode)){ context.getFlowScope().put("errorCode", NullAuthcodeAuthenticationException.CODE); return "error"; } if(submitAuthcode.equals(authcode)){ context.getFlowScope().remove("errorCode"); return "success"; } context.getFlowScope().put("errorCode", BadAuthcodeAuthenticationException.CODE); return "error"; }
2.自定义登录RemoteLoginAction
/** * 远程登陆票据提供Action. * 根据InitialFlowSetupAction修改. * 由于InitialFlowSetupAction为final类,因此只能将代码复制过来再进行修改. */ package org.jasig.cas.web.flow; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.validation.constraints.NotNull; import org.hibernate.validator.constraints.NotEmpty; import org.jasig.cas.authentication.principal.Service; import org.jasig.cas.web.support.ArgumentExtractor; import org.jasig.cas.web.support.CookieRetrievingCookieGenerator; import org.jasig.cas.web.support.WebUtils; import org.springframework.util.StringUtils; import org.springframework.webflow.action.AbstractAction; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; public class RemoteLoginAction extends AbstractAction { /** CookieGenerator for the Warnings. */ @NotNull private CookieRetrievingCookieGenerator warnCookieGenerator; /** CookieGenerator for the TicketGrantingTickets. */ @NotNull private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator; /** Extractors for finding the service. */ @NotEmpty private List<ArgumentExtractor> argumentExtractors; /** Boolean to note whether we've set the values on the generators or not. */ private boolean pathPopulated = false; protected Event doExecute(final RequestContext context) throws Exception { final HttpServletRequest request = WebUtils.getHttpServletRequest(context); if (!this.pathPopulated) { final String contextPath = context.getExternalContext().getContextPath(); final String cookiePath = StringUtils.hasText(contextPath) ? contextPath : "/"; logger.info("Setting path for cookies to: " + cookiePath); this.warnCookieGenerator.setCookiePath(cookiePath); this.ticketGrantingTicketCookieGenerator.setCookiePath(cookiePath); this.pathPopulated = true; } context.getFlowScope().put( "ticketGrantingTicketId", this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request)); context.getFlowScope().put( "warnCookieValue", Boolean.valueOf(this.warnCookieGenerator.retrieveCookieValue(request))); final Service service = WebUtils.getService(this.argumentExtractors, context); if (service != null && logger.isDebugEnabled()) { logger.debug("Placing service in FlowScope: " + service.getId()); } context.getFlowScope().put("contextPath", request.getContextPath()); context.getFlowScope().put("service", service); // 客户端必须传递loginUrl参数过来,否则无法确定登陆目标页面 if (StringUtils.hasText(request.getParameter("loginUrl"))) { context.getFlowScope().put("remoteLoginUrl", request.getParameter("loginUrl")); } else { request.setAttribute("remoteLoginMessage", "loginUrl parameter must be supported."); return error(); } // 若参数包含submit则进行提交,否则进行验证 if (StringUtils.hasText(request.getParameter("submit"))) { return result("submit"); } else { return result("checkTicketGrantingTicket"); } } public void setTicketGrantingTicketCookieGenerator( final CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator) { this.ticketGrantingTicketCookieGenerator = ticketGrantingTicketCookieGenerator; } public void setWarnCookieGenerator(final CookieRetrievingCookieGenerator warnCookieGenerator) { this.warnCookieGenerator = warnCookieGenerator; } public void setArgumentExtractors( final List<ArgumentExtractor> argumentExtractors) { this.argumentExtractors = argumentExtractors; } }
3.自定义退出RemoteLogoutAction
package org.jasig.cas.web.flow; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotNull; import org.hibernate.validator.constraints.NotEmpty; import org.jasig.cas.CentralAuthenticationService; import org.jasig.cas.authentication.principal.Service; import org.jasig.cas.web.support.ArgumentExtractor; import org.jasig.cas.web.support.CookieRetrievingCookieGenerator; import org.jasig.cas.web.support.WebUtils; import org.springframework.util.StringUtils; import org.springframework.webflow.action.AbstractAction; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; public class RemoteLogoutAction extends AbstractAction { @NotNull private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator; @NotNull private CookieRetrievingCookieGenerator warnCookieGenerator; @NotNull private CentralAuthenticationService centralAuthenticationService; @NotEmpty private List<ArgumentExtractor> argumentExtractors; private boolean pathPopulated = false; @Override protected Event doExecute(final RequestContext context) throws Exception { final HttpServletRequest request = WebUtils .getHttpServletRequest(context); final HttpServletResponse response = WebUtils .getHttpServletResponse(context); if (!this.pathPopulated) { final String contextPath = context.getExternalContext() .getContextPath(); final String cookiePath = StringUtils.hasText(contextPath) ? contextPath : "/"; logger.info("Setting path for cookies to: " + cookiePath); this.warnCookieGenerator.setCookiePath(cookiePath); this.ticketGrantingTicketCookieGenerator.setCookiePath(cookiePath); this.pathPopulated = true; } context.getFlowScope().put( "ticketGrantingTicketId", this.ticketGrantingTicketCookieGenerator .retrieveCookieValue(request)); context.getFlowScope().put( "warnCookieValue", Boolean.valueOf(this.warnCookieGenerator .retrieveCookieValue(request))); final Service service = WebUtils.getService(this.argumentExtractors, context); if (service != null && logger.isDebugEnabled()) { logger.debug("Placing service in FlowScope: " + service.getId()); } context.getFlowScope().put("service", service); context.getFlowScope().put("remoteLoginUrl",request.getParameter("service")); final String ticketGrantingTicketId = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request); if (ticketGrantingTicketId != null) { this.centralAuthenticationService .destroyTicketGrantingTicket(ticketGrantingTicketId); this.ticketGrantingTicketCookieGenerator.removeCookie(response); this.warnCookieGenerator.removeCookie(response); } return result("success"); } public void setTicketGrantingTicketCookieGenerator( final CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator) { this.ticketGrantingTicketCookieGenerator = ticketGrantingTicketCookieGenerator; } public void setWarnCookieGenerator( final CookieRetrievingCookieGenerator warnCookieGenerator) { this.warnCookieGenerator = warnCookieGenerator; } public void setArgumentExtractors( final List<ArgumentExtractor> argumentExtractors) { this.argumentExtractors = argumentExtractors; } public void setCentralAuthenticationService( final CentralAuthenticationService centralAuthenticationService) { this.centralAuthenticationService = centralAuthenticationService; } }
二、修改cas-server-webapp
1.web.xml增加登录、退出请求mapping映射的处理
<servlet-mapping> <servlet-name>cas</servlet-name> <url-pattern>/remoteLogin</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>cas</servlet-name> <url-pattern>/remoteLogout</url-pattern> </servlet-mapping>
2.在cas-servlet.xml增加请求流程
<!-- 增加远程控制者,允许以/remote请求启动remote控制流程 --> <bean id="handlerMappingB" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/remoteLogin">remoteLoginController</prop> <prop key="/remoteLogout">remoteLogoutController</prop> </props> </property> <property name="interceptors"> <list> <ref bean="localeChangeInterceptor" /> </list> </property> </bean>
<!-- 远程配置 --> <bean id="remoteLoginController" class="org.springframework.webflow.mvc.servlet.FlowController"> <property name="flowExecutor" ref="remoteLoginFlowExecutor" /> <property name="flowUrlHandler" ref="flowUrlHandler"/> </bean> <webflow:flow-executor id="remoteLoginFlowExecutor" flow-registry="remoteLoginFlowRegistry"> <webflow:flow-execution-attributes> <webflow:always-redirect-on-pause value="false"/> </webflow:flow-execution-attributes> </webflow:flow-executor> <webflow:flow-registry id="remoteLoginFlowRegistry" flow-builder-services="builder"> <webflow:flow-location path="/WEB-INF/remoteLogin-webflow.xml" id="remoteLogin"/> </webflow:flow-registry> <webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="viewFactoryCreator"/> <bean id="remoteLoginAction" class="org.jasig.cas.web.flow.RemoteLoginAction" p:argumentExtractors-ref="argumentExtractors" p:warnCookieGenerator-ref="warnCookieGenerator" p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator" /> <bean id="remoteLogoutController" class="org.springframework.webflow.mvc.servlet.FlowController"> <property name="flowExecutor" ref="remoteLogoutFlowExecutor" /> <property name="flowUrlHandler" ref="flowUrlHandler"/> </bean> <webflow:flow-executor id="remoteLogoutFlowExecutor" flow-registry="remoteLogoutFlowRegistry"> <webflow:flow-execution-attributes> <webflow:always-redirect-on-pause value="false"/> </webflow:flow-execution-attributes> </webflow:flow-executor> <webflow:flow-registry id="remoteLogoutFlowRegistry" flow-builder-services="builder"> <webflow:flow-location path="/WEB-INF/remoteLogout-webflow.xml" id="remoteLogout"/> </webflow:flow-registry> <bean id="remoteLogoutAction" class="org.jasig.cas.web.flow.RemoteLogoutAction" p:argumentExtractors-ref="argumentExtractors" p:warnCookieGenerator-ref="warnCookieGenerator" p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator" p:centralAuthenticationService-ref="centralAuthenticationService" /> <!-- 远程配置 -->
3.增加远程登录流程控制remoteLogin-webflow.xml
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd" start-state="remoteLogin"> <!-- 远程登陆主要Action --> <action-state id="remoteLogin"> <evaluate expression="remoteLoginAction" /> <transition on="error" to="remoteCallbackView" /> <transition on="submit" to="authcodeValidate" /> <transition on="checkTicketGrantingTicket" to="ticketGrantingTicketExistsCheck" /> </action-state> <!-- 远程回调页面,主要以JavaScript的方式回传一些参数用 --> <end-state id="remoteCallbackView" view="remoteCallbackView" /> <action-state id="authcodeValidate"> <evaluate expression="authenticationViaFormAction.validatorCode(flowRequestContext, messageContext)" /> <transition on="error" to="remoteCallbackView" /> <transition on="success" to="bindAndValidate" /> </action-state> <action-state id="bindAndValidate"> <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" /> <transition on="success" to="submit" /> <transition on="error" to="remoteCallbackView" /> </action-state> <decision-state id="ticketGrantingTicketExistsCheck"> <if test="flowScope.ticketGrantingTicketId != null" then="hasServiceCheck" else="gatewayRequestCheck" /> </decision-state> <decision-state id="hasServiceCheck"> <if test="flowScope.service != null" then="generateServiceTicket" else="remoteCallbackView" /> </decision-state> <decision-state id="gatewayRequestCheck"> <if test="externalContext.requestParameterMap['gateway'] neq '' && externalContext.requestParameterMap['gateway'] neq null && flowScope.service neq null" then="redirect" else="remoteCallbackView" /> </decision-state> <action-state id="generateServiceTicket"> <evaluate expression="generateServiceTicketAction" /> <transition on="success" to ="warn" /> <transition on="error" to="remoteCallbackView" /> <transition on="gateway" to="redirect" /> </action-state> <decision-state id="warn"> <if test="flowScope.warnCookieValue" then="showWarningView" else="redirect" /> </decision-state> <action-state id="submit"> <evaluate expression="authenticationViaFormAction.submit(flowRequestContext,messageContext)" /> <transition on="warn" to="warn" /> <transition on="success" to="sendTicketGrantingTicket" /> <transition on="error" to="remoteCallbackView" /> </action-state> <action-state id="sendTicketGrantingTicket"> <evaluate expression="sendTicketGrantingTicketAction" /> <transition to="serviceCheck" /> </action-state> <decision-state id="serviceCheck"> <if test="flowScope.service neq null" then="generateServiceTicket" else="remoteCallbackView" /> </decision-state> <end-state id="showWarningView" view="casLoginConfirmView" /> <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() eq 'POST'" then="postView" else="redirectView" /> </decision-state> <end-state id="postView" view="postResponseView"> <on-entry> <set name="requestScope.parameters" value="requestScope.response.attributes" /> <set name="requestScope.originalUrl" value="flowScope.service.id" /> </on-entry> </end-state> <end-state id="redirectView" view="externalRedirect:${requestScope.response.url}" /> <end-state id="viewServiceErrorView" view="viewServiceErrorView" /> <end-state id="viewServiceSsoErrorView" view="viewServiceSsoErrorView" /> <global-transitions> <transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException" /> <transition to="viewServiceSsoErrorView" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException" /> <transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException" /> </global-transitions> </flow>
4.增加远程退出流程控制remoteLogout-webflow.xml
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd" start-state="remoteLogout"> <!-- 远程登陆主要Action --> <action-state id="remoteLogout"> <evaluate expression="remoteLogoutAction" /> <transition on="success" to="remoteCallbackView" /> </action-state> <end-state id="remoteCallbackView" view="remoteCallbackView" /> <global-transitions> <transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException" /> <transition to="viewServiceSsoErrorView" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException" /> <transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException" /> </global-transitions> </flow>
5.配置remoteCallbackView显示节点,修改src/default_views.properties文件,增加remoteCallbackView配置
### 配置远程回调页面 remoteCallbackView.(class)=org.springframework.web.servlet.view.JstlView remoteCallbackView.url=/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp
6.创建/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp文件
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript"> var remoteLoginUrl = "${remoteLoginUrl}"; if(remoteLoginUrl == ""){ window.location.href ="${pageContext.request.contextPath}"; }else{ var remoteUrl = remoteLoginUrl+"?validated=true"; var service = null; <c:if test="${service != null && service != ''}"> service = "&service=" + encodeURIComponent("${service}"); </c:if> // 构造错误消息 var errorMessageCode = "${errorCode}"; var errorCode = null; if(errorMessageCode.indexOf("required.authcode")!=-1){ errorCode =501; }else if(errorMessageCode.indexOf("error.authentication.authcode.bad")!=-1){ errorCode =502; }else if(errorMessageCode.indexOf("required.username")!=-1 || errorMessageCode.indexOf("required.password")!=-1 ){ errorCode =503; }else if(errorMessageCode.indexOf("error.authentication.credentials.bad")!=-1){ errorCode = 504; } if(errorCode){ remoteUrl +="&errorCode="+errorCode; } // 跳转回去 window.location.href = remoteUrl + service; } </script> </head> <body>${remoteLoginMessage} </body> </html>
服务器端修改完成。
相关文章推荐
- 改造CAS单点登录 --- 自定义登陆页面(客户端)
- CAS单点登录(三)--服务端改造(登录页及登录方式的自定义)
- 自定义SharePoint的登陆页面
- 自定义Sharepoint的登陆页面
- spring-oauth集成cas单点登录,登陆完成进入授权页面后,按回退按钮进入404页面的问题
- 第 4 章 自定义登陆页面
- moss自定义登陆页面
- 自定义Sharepoint的登陆页面
- goahead 的认证和自定义登陆页面的cookie使用
- [翻译]自定义Sharepoint的登陆页面
- 服务端改造(登录页及登录方式的自定义)
- 自定义Sharepoint的登陆页面(2)
- spring-oauth集成cas单点登录,登陆完成进入授权页面后,按回退按钮进入404页面的问题
- 自定义Sharepoint的登陆页面
- DotNetNuke开发——自定义登陆页面
- 自定义Sharepoint的登陆页面
- Django里自定义用户登陆及登陆后跳转到登陆前页面的实现
- [翻译]自定义Sharepoint的登陆页面
- springsecurity2学习笔记三(登陆后与struts结合、自定义访问拒绝页面)
- Drupal 6 自定义用户登陆、注册页面