CAS-3.2.1自定义客户端登录界面----完整篇

[b]前言:[/b]在已有的CAS SSO架构代码上,经过4天的研究终于完成了客户端登录界面需求,其中多亏网上的资料【让CAS支持客户端自定义登陆页面——服务器新篇与客户端新篇】本文也是基于此完成的,不过我修改了一些代码,CAS SSO的内部流程我只了解了60%,如果你想看CAS SSO原理,请忽略此文章;本文致力提供完整,详细的搭建流程,争取让你一次成功!附件中有本文工程,分客户端与服务端,采用maven搭建,想要运行请配置好maven环境,数据库采用mysql。

原文链接地址:[url]http://lsz1023-126-com.iteye.com/blog/2098973[/url]

[b]实现原理:[/b]
一、 逻辑
 客户端修改CAS Authentication Filter过滤器,该过滤器会判断用户是否登录,如果没有登录则跳转到自身配置的登录界面;
 用户输入正确的信息,登录时,会被提交到服务端的登录流程中;
 服务端通过新增一个remoteLogin处理类,专门处理客户端自定义登录业务;
 该remoteLogin处理类与原始的login处理极为类似,只是修改了获取用户名与密码的方式;
 如果用户名与密码不匹配,校验失败,会通过remoteCallbackView.jsp界面将错误提示与service一并响应给客户端的登录界面,接收之后将错误提示显示到界面上;
 如果用户名与密码匹配,校验成功,会直接重定向到客户端的service页面,这时CAS Authentication Filter过滤器与CAS Validation Filter过滤器分别校验用户是否登录与ticket票据是否正确,完成最后的校验,否则要求用户重新登录。
二、 修改点
 客户端
1. 修改web.xml,修改原先的CAS Authentication Filter过滤器
2. 新增RemoteAuthenticationFilter过滤器
3. 新增login.jsp
 服务端
1. 修改web.xml,给cas过滤器添加remoteLogin servlet-mapping映射
2. 新增RemoteLoginAction登录处理类与AuthenticationViaFormAction表单处理类
3. 新增remoteLogin-webflow.xml自定义登录webflow流程文件
4. 修改cas-servlet.xml配置文件,新增一些bean配置
5. 新增remoteCallbackView.jsp响应界面,校验错误时用来通知客户端登录界面。


[b]客户端篇:[/b]
1.替换原来过滤器org.jasig.cas.client.authentication.AuthenticationFilter,改成自己的过滤器RemoteAuthenticationFilter.java,这个过滤器可以自己随便放到哪个包中,保证web.xml能够正确引用到就行:

public class RemoteAuthenticationFilter extends AbstractCasFilter
{
public static final String CONST_CAS_GATEWAY = "_const_cas_gateway_";
/**
* 本地登陆页面URL.
*/
private String localLoginUrl;

/**
* The URL to the CAS Server login.
*/
private String casServerLoginUrl;
/**
* Whether to send the renew request or not.
*/
private boolean renew = false;
/**
* Whether to send the gateway request or not.
*/
private boolean gateway = false;

protected void initInternal(final FilterConfig filterConfig)
throws ServletException
{
super.initInternal(filterConfig);
setCasServerLoginUrl(getPropertyFromInitParams(filterConfig,
"casServerLoginUrl", null));
log.trace("Loaded CasServerLoginUrl parameter: "
+ this.casServerLoginUrl);
setLocalLoginUrl(getPropertyFromInitParams(filterConfig,
"localLoginUrl", null));
log.trace("Loaded LocalLoginUrl parameter: " + this.localLoginUrl);
setRenew(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig,
"renew", "false")));
log.trace("Loaded renew parameter: " + this.renew);
setGateway(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig,
"gateway", "false")));
log.trace("Loaded gateway parameter: " + this.gateway);
}

public void init()
{
super.init();
CommonUtils.assertNotNull(this.localLoginUrl,
"localLoginUrl cannot be null.");
CommonUtils.assertNotNull(this.casServerLoginUrl,
"casServerLoginUrl cannot be null.");
}

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 String ticket = request.getParameter(getArtifactParameterName());
final Assertion assertion = session != null ? (Assertion) session
.getAttribute(CONST_CAS_ASSERTION) : null;
final boolean wasGatewayed = session != null
&& session.getAttribute(CONST_CAS_GATEWAY) != null;
// 如果访问路径为localLoginUrl且带有validated参数则跳过
URL url = new URL(localLoginUrl);
final boolean isValidatedLocalLoginUrl = request.getRequestURI()
.endsWith(url.getPath())
&& CommonUtils.isNotBlank(request.getParameter("validated"));

if (!isValidatedLocalLoginUrl && CommonUtils.isBlank(ticket)
&& assertion == null && !wasGatewayed)
{
log.debug("no ticket and no assertion found");
if (this.gateway)
{
log.debug("setting gateway attribute in session");
request.getSession(true).setAttribute(CONST_CAS_GATEWAY, "yes");
}
final String serviceUrl = constructServiceUrl(request, response);
if (log.isDebugEnabled())
{
log.debug("Constructed service url: " + serviceUrl);
}
String urlToRedirectTo = CommonUtils.constructRedirectUrl(
this.casServerLoginUrl, getServiceParameterName(),
serviceUrl, this.renew, this.gateway);
// 加入localLoginUrl
urlToRedirectTo += (urlToRedirectTo.contains("?") ? "&" : "?")
+ "loginUrl=" + URLEncoder.encode(localLoginUrl, "utf-8");
if (log.isDebugEnabled())
{
log.debug("redirecting to \"" + urlToRedirectTo + "\"");
}

response.sendRedirect(urlToRedirectTo);
return;
}
if (session != null)
{
log.debug("removing gateway attribute from session");
session.setAttribute(CONST_CAS_GATEWAY, null);
}
filterChain.doFilter(request, response);
}

public final void setRenew(final boolean renew)
{
this.renew = renew;
}

public final void setGateway(final boolean gateway)
{
this.gateway = gateway;
}

public final void setCasServerLoginUrl(final String casServerLoginUrl)
{
this.casServerLoginUrl = casServerLoginUrl;
}

public final void setLocalLoginUrl(String localLoginUrl)
{
this.localLoginUrl = localLoginUrl;
}

}


2.web.xml中配置:

旧配置

<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://localhost:8443/cas/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost: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>

修改成这样,注:其它路径mapping不用改。

<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>org.demo.user.common.RemoteAuthenticationFilter</filter-class>
<init-param>
<param-name>localLoginUrl</param-name>
<param-value>http://localhost:8080/app/mylogin.jsp</param-value>
</init-param>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://localhost:8443/cas/remoteLogin</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>


3.加上你自己定义的登录界面,注:我修改了一些网上介绍的代码:

<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>APP1客户端登录</title>
<link rel="stylesheet" type="text/css"
href="<%=request.getContextPath()%>/styles/main.css" />
<script type="text/javascript">
function getParam(name)
{
var queryString = window.location.search;
var param = queryString.substr(1, queryString.length - 1).split("&");
for (var i = 0; i < param.length; i++)
{
var keyValue = param[i].split("=");
if (keyValue[0] == name)
return keyValue[1];
}
return null;
}
function init()
{
// 显示异常信息
var error = getParam("errorMessage");
if (error)
{
document.getElementById("errorMessage").innerHTML = decodeURIComponent(error);
}

// 注入service
var service = getParam("service");
if (service)
document.getElementById("service").value = decodeURIComponent(service);
else
document.getElementById("service").value = location.href;
}
</script>
</head>
<body>
<h1>APP1客户端登录</h1>

<div id="errorMessage" style="color: red;"></div>
<form id="myLoginForm" action="https://localhost:8443/cas/remoteLogin?service=http://localhost:8080/app/pages/home.jsp"
method="post">

<input type="hidden" name="loginUrl" value="http://localhost:8080/app/mylogin.jsp">
<input type="hidden" name="submit" value="true" />
<input type="hidden" name="lt" id="loginTicket" value="" />

<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密  码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登陆" /></td>
</tr>
</table>
</form>
<script type="text/javascript">
init()
</script>
</body>
</html>

至此客户端完结!

[b]服务端篇:注我没有加上登出代码,因为登出代码可以使用原有的[/b]
1.添加客户端登录Action,org.jasig.cas.web.flow.RemoteLoginAction:

/**
* 远程登陆票据提供Action. 根据InitialFlowSetupAction修改.
* 由于InitialFlowSetupAction为final类,因此只能将代码复制过来再进行修改.
*/
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. */
@NotNull
@Size(min = 1)
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)));

// 存放service url
// context.getFlowScope().put("serviceUrl", request.getParameter("service"));

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);

// 客户端必须传递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;
}
}


2.重写LoginForm代码,org.jasig.cas.web.flow.AuthenticationViaFormAction重写,此类基本上采用原有代码,只是添加了获取用户名与密码的代码:

public class AuthenticationViaFormAction
{
/**
* Binder that allows additional binding of form object beyond Spring
* defaults.
*/
private CredentialsBinder credentialsBinder;

/** Core we delegate to for handling all ticket related tasks. */
@NotNull
private CentralAuthenticationService centralAuthenticationService;

@NotNull
private CookieGenerator warnCookieGenerator;

protected Logger logger = LoggerFactory.getLogger(getClass());

public final void doBind(final RequestContext context,
final Credentials credentials) throws Exception
{
final HttpServletRequest request = WebUtils
.getHttpServletRequest(context);

if (this.credentialsBinder != null
&& this.credentialsBinder.supports(credentials.getClass()))
{
this.credentialsBinder.bind(request, credentials);
}
}

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);
org.jasig.cas.authentication.principal.UsernamePasswordCredentials credentials = new org.jasig.cas.authentication.principal.UsernamePasswordCredentials();
credentials.setPassword(request.getParameter("password"));
credentials.setUsername(request.getParameter("username"));
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(context, e, messageContext);
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(context, e, messageContext);
return "error";
}
}

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";
}
}

private void populateErrorsInstance(final TicketException e,
final MessageContext messageContext)
{

try
{
messageContext.addMessage(new MessageBuilder().error()
.code(e.getCode()).defaultText(e.getCode()).build());
} catch (final Exception fe)
{
logger.error(fe.getMessage(), fe);
}
}

private void populateErrorsInstance(final RequestContext context,
final TicketException e, final MessageContext messageContext)
{

try
{
messageContext.addMessage(new MessageBuilder().error()
.code(e.getCode()).defaultText(e.getCode()).build());

Message[] messages = messageContext.getAllMessages();

context.getFlowScope().put("remoteLoginMessage",
messages[messages.length - 1].getText());

} catch (final Exception fe)
{
logger.error(fe.getMessage(), fe);
}
}

private void putWarnCookieIfRequestParameterPresent(
final RequestContext context)
{
final HttpServletResponse response = WebUtils
.getHttpServletResponse(context);

if (StringUtils.hasText(context.getExternalContext()
.getRequestParameterMap().get("warn")))
{
this.warnCookieGenerator.addCookie(response, "true");
} else
{
this.warnCookieGenerator.removeCookie(response);
}
}

private AuthenticationException getAuthenticationExceptionAsCause(
final TicketException e)
{
return (AuthenticationException) e.getCause();
}

private String getAuthenticationExceptionEventId(final TicketException e)
{
final AuthenticationException authEx = getAuthenticationExceptionAsCause(e);

if (this.logger.isDebugEnabled())
this.logger
.debug("An authentication error has occurred. Returning the event id "
+ authEx.getType());

return authEx.getType();
}

private boolean isCauseAuthenticationException(final TicketException e)
{
return e.getCause() != null
&& AuthenticationException.class.isAssignableFrom(e.getCause()
.getClass());
}

public final void setCentralAuthenticationService(
final CentralAuthenticationService centralAuthenticationService)
{
this.centralAuthenticationService = centralAuthenticationService;
}

/**
* Set a CredentialsBinder for additional binding of the HttpServletRequest
* to the Credentials instance, beyond our default binding of the
* Credentials as a Form Object in Spring WebMVC parlance. By the time we
* invoke this CredentialsBinder, we have already engaged in default binding
* such that for each HttpServletRequest parameter, if there was a JavaBean
* property of the Credentials implementation of the same name, we have set
* that property to be the value of the corresponding request parameter.
* This CredentialsBinder plugin point exists to allow consideration of
* things other than HttpServletRequest parameters in populating the
* Credentials (or more sophisticated consideration of the
* HttpServletRequest parameters).
*
* @param credentialsBinder
* the credentials binder to set.
*/
public final void setCredentialsBinder(
final CredentialsBinder credentialsBinder)
{
this.credentialsBinder = credentialsBinder;
}

public final void setWarnCookieGenerator(
final CookieGenerator warnCookieGenerator)
{
this.warnCookieGenerator = warnCookieGenerator;
}
}


3.web.xml配置,原有基础上新增这两句:

<servlet-mapping>
<servlet-name>cas</servlet-name>
<url-pattern>/remoteLogin</url-pattern>
</servlet-mapping>


4.在cas-servlet.xml中最后面增加以下信息:

<bean id="handlerMappingB"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/remoteLogin">remoteLoginController</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>


5.新建一个文件与login-webflow.xml同级,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">

<!-- <on-start> <evaluate expression="remoteLoginAction.doBind(flowRequestContext,
flowScope.credentials)" /> </on-start> -->
<var name="credentials"
class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />

<!-- 远程登陆主要Action -->
<action-state id="remoteLogin">
<evaluate expression="remoteLoginAction" />
<transition on="error" to="remoteCallbackView" />
<transition on="submit" to="bindAndValidate" />
<transition on="checkTicketGrantingTicket" to="ticketGrantingTicketExistsCheck" />
</action-state>

<!-- 远程回调页面,主要以JavaScript的方式回传一些参数用 -->
<end-state id="remoteCallbackView" view="remoteCallbackView" />

<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" />

<!-- <end-state id="redirect" view="bean:dynamicRedirectViewSelector" /> -->

<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>

<!-- <decision-state id="hashServiceUrl">
<if test="flowScope.serviceUrl neq null" then="redirectServiceView" else="redirectView"/>
</decision-state>
<end-state id="redirectServiceView" view="externalRedirect:${flowScope.serviceUrl}" /> -->

<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>


6.加上一个回调视图配置,在default_views.properties中新增以下两句:
### 配置远程回调页面
remoteCallbackView.(class)=org.springframework.web.servlet.view.JstlView
remoteCallbackView.url=/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp
其它不变

7.加上回调页面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"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> --%>
<script type="text/javascript">
var remoteUrl = "${remoteLoginUrl}?validated=true";
// 构造错误消息,从webflow scope中取出
var errorMessage = '${remoteLoginMessage}';
/* <spring:hasBindErrors name="credentials">
errorMessage = "&errorMessage=" + encodeURIComponent('<c:forEach var="error" items="${errors.allErrors}"><spring:message code="${error.code}" text="${error.defaultMessage}" /></c:forEach>');
</spring:hasBindErrors> */
// 如果存在错误消息则追加到 url中
if(null != errorMessage && errorMessage.length > 0)
{
errorMessage = "&errorMessage=" + encodeURIComponent(errorMessage);
}
// 构造service
var service = "";
<c:if test="${service != null && service != ''}">
service = "&service=" + encodeURIComponent("${service}");
</c:if>

// 跳转回去(客户端)
window.location.href = remoteUrl + errorMessage + service;
</script>


完结...

请关注下一篇,shiro + cas sso实现客户端自定义登录界面完整实现
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值