关于cas基本搭建,就不再描述了,随便一搜一大堆。我这里只关注后期的修正,当有这样一个需求:
当你登录系统软件时,不希望跳转到cas默认的页面去做登录。你希望在自己的子系统的登录页面进入。
我这里用服务端是cas4.0版本,客服端是3.1.12 其实服务器的版本之间差别还是有很多的。
实现原理:
一、 逻辑
1,客户端修改CAS Authentication Filter过滤器,该过滤器会判断用户是否登录,如果没有登录则跳转到自身配置的登录界面;
2,用户输入正确的信息,登录时,会被提交到服务端的登录流程中;
3,服务端通过新增一个remoteLogin处理类,专门处理客户端自定义登录业务;
4,该remoteLogin处理类与原始的login处理极为类似,只是修改了获取用户名与密码的方式;
5,如果用户名与密码不匹配,校验失败,会通过remoteCallbackView.jsp界面将错误提示与service一并响应给客户端的登录界面,接收之后将错误提示显示到界面上;
6,如果用户名与密码匹配,校验成功,会直接重定向到客户端的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响应界面,校验错误时用来通知客户端登录界面
客户端篇:
1.替换原来过滤器org.jasig.cas.client.authentication.AuthenticationFilter,改成自己的过滤器RemoteAuthenticationFilter.java,参照原始的页面去修改,这个过滤器可以自己随便放到哪个包中,保证web.xml能够正确引用到就行:
package com.xb.login;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;
/**
* qzg 17.11.01
*/
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;
/** Instance of commons logging for logging purposes. */
protected final Log log = LogFactory.getLog(getClass());
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);
}
System.out.println("---------end---2---#");
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>CASFilterfilter-name>
<filter-class>com.xb.login.RemoteAuthenticationFilterfilter-class>
<init-param>
<param-name>localLoginUrlparam-name>
<param-value>http://127.0.0.1:8080/casClient/login.jspparam-value>
init-param>
<init-param>
<param-name>casServerLoginUrlparam-name>
<param-value>https://sso.castest.com:8443/cas/remoteLoginparam-value>
init-param>
<init-param>
<param-name>serverNameparam-name>
<param-value>http://127.0.0.1:8080param-value>
init-param>
filter>
<filter-mapping>
<filter-name>CASFilterfilter-name>
<url-pattern>/pages/*url-pattern>
filter-mapping>
<filter-mapping>
<filter-name>CASFilterfilter-name>
<url-pattern>*.actionurl-pattern>
filter-mapping>
3.加上你自己定义的登录界面,就是子系统的login.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>系统1客户端登录title>
<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");
console.log("service:"+service);
if (service)
document.getElementById("service").value = decodeURIComponent(service);
else
document.getElementById("service").value = location.href;
var loginTicket = getParam("loginTicket");
console.log("loginTicket:"+loginTicket);
if (loginTicket)
document.getElementById("loginTicket").value = decodeURIComponent(loginTicket);
else
document.getElementById("loginTicket").value = '';
// 这个参数在4.0之前的版本没有,搞了我好久。。。。
var flowExecutionKey = getParam("flowExecutionKey");
console.log("flowExecutionKey:"+flowExecutionKey);
if (flowExecutionKey)
document.getElementById("execution").value = decodeURIComponent(flowExecutionKey);
else
document.getElementById("execution").value = '';
}
script>
head>
<body>
<h1>系统1客户端登录h1>
<div id="errorMessage" style="color: red;">div>
<form id="myLoginForm" action="https://sso.castest.com:8443/cas/remoteLogin"
method="post">
<input type="hidden" name="service" value="http://127.0.0.1:8080/casClient/index.jsp">
<input type="hidden" name="loginUrl" value="http://127.0.0.1:8080/casClient/login.jsp">
<input type="hidden" name="submit" value="LOGIN" />
<input type="hidden" name="msubmit" value="true" />
<input type="hidden" name="lt" id="loginTicket" value="" />
<input type="hidden" name="execution" id="execution" value="" />
<input type="hidden" name="service" id="service" value="" />
<input type="hidden" name="_eventId" value="submit" />
<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>
<a href="https://sso.castest.com:8443/cas/logout">注销登录a>
<script type="text/javascript">
init()
script>
body>
html>
客户端完成改造。
服务端篇
1.添加客户端登录Action,org.jasig.cas.web.flow.RemoteLoginAction:一样的是参照源码修改的。可以先看看源码再去修改。
package com.xb.casLogin;
import java.text.SimpleDateFormat;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
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;
/**
*
* 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 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
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String timetodaypi= sdf.format(new java.util.Date());
System.out.println(timetodaypi+"----------------------------Relogin------go--------------------------------------------------");
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参数过来,否则无法确定登陆目标页面 loginUrl
System.out.println("------loginUrl--"+request.getParameter("loginUrl"));
if (StringUtils.hasText(request.getParameter("loginUrl")))
{
context.getFlowScope().put("remoteLoginUrl",
request.getParameter("loginUrl"));
} else
{
request.setAttribute("remoteLoginMessage",
"loginUrl parameter must be supported.");
return result("error");
}
// 若参数包含submit则进行提交,否则进行验证
if (StringUtils.hasText(request.getParameter("msubmit")))
{
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 argumentExtractors)
{
this.argumentExtractors = argumentExtractors;
}
}
2 web.xml配置,
<servlet-mapping>
<servlet-name>casservlet-name>
<url-pattern>/remoteLoginurl-pattern>
servlet-mapping>
3 cas-servlet.xml中最后面增加以下信息
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd">
<import resource="spring-configuration/propertyFileConfigurer.xml"/>
<bean id="themeResolver" class="org.jasig.cas.services.web.ServiceThemeResolver"
p:defaultThemeName="${cas.themeResolver.defaultThemeName}"
p:argumentExtractors-ref="argumentExtractors"
p:servicesManager-ref="servicesManager">
<property name="mobileBrowsers">
<util:map>
<entry key=".*iPhone.*" value="iphone"/>
<entry key=".*Android.*" value="iphone"/>
<entry key=".*Safari.*Pre.*" value="iphone"/>
<entry key=".*Nokia.*AppleWebKit.*" value="iphone"/>
util:map>
property>
bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"
p:order="0">
<property name="basenames">
<util:list>
<value>${cas.viewResolver.basename}value>
<value>protocol_viewsvalue>
util:list>
property>
bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" p:defaultLocale="en" />
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
<bean id="urlBasedViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"
p:viewClass="org.springframework.web.servlet.view.InternalResourceView"
p:prefix="/WEB-INF/view/jsp/"
p:suffix=".jsp"
p:order="1"/>
<bean id="errorHandlerResolver" class="org.jasig.cas.web.FlowExecutionExceptionResolver"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<bean
id="handlerMappingC"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"
p:alwaysUseFullPath="true">
<property name="mappings">
<util:properties>
<prop key="/serviceValidate">serviceValidateControllerprop>
<prop key="/proxyValidate">proxyValidateControllerprop>
<prop key="/p3/serviceValidate">v3ServiceValidateControllerprop>
<prop key="/p3/proxyValidate">v3ProxyValidateControllerprop>
<prop key="/validate">legacyValidateControllerprop>
<prop key="/proxy">proxyControllerprop>
<prop key="/authorizationFailure.html">passThroughControllerprop>
<prop key="/status">healthCheckControllerprop>
<prop key="/statistics">statisticsControllerprop>
util:properties>
property>
bean>
<bean id="passThroughController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
<bean id="loginFlowHandlerMapping" class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping"
p:flowRegistry-ref="loginFlowRegistry" p:order="2">
<property name="interceptors">
<ref local="localeChangeInterceptor" />
property>
bean>
<bean id="loginHandlerAdapter" class="org.jasig.cas.web.flow.SelectiveFlowHandlerAdapter"
p:supportedFlowId="login" p:flowExecutor-ref="loginFlowExecutor" p:flowUrlHandler-ref="loginFlowUrlHandler" />
<bean id="loginFlowUrlHandler" class="org.jasig.cas.web.flow.CasDefaultFlowUrlHandler" />
<webflow:flow-executor id="loginFlowExecutor" flow-registry="loginFlowRegistry">
<webflow:flow-execution-attributes>
<webflow:always-redirect-on-pause value="false" />
<webflow:redirect-in-same-state value="false" />
webflow:flow-execution-attributes>
<webflow:flow-execution-listeners>
<webflow:listener ref="terminateWebSessionListener" />
webflow:flow-execution-listeners>
webflow:flow-executor>
<webflow:flow-registry id="loginFlowRegistry" flow-builder-services="builder">
<webflow:flow-location path="/WEB-INF/login-webflow.xml" id="login" />
webflow:flow-registry>
<bean id="logoutFlowHandlerMapping" class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping"
p:flowRegistry-ref="logoutFlowRegistry" p:order="3">
<property name="interceptors">
<ref local="localeChangeInterceptor" />
property>
bean>
<bean id="logoutHandlerAdapter" class="org.jasig.cas.web.flow.SelectiveFlowHandlerAdapter"
p:supportedFlowId="logout" p:flowExecutor-ref="logoutFlowExecutor" p:flowUrlHandler-ref="logoutFlowUrlHandler" />
<bean id="logoutFlowUrlHandler" class="org.jasig.cas.web.flow.CasDefaultFlowUrlHandler"
p:flowExecutionKeyParameter="RelayState" />
<webflow:flow-executor id="logoutFlowExecutor" flow-registry="logoutFlowRegistry">
<webflow:flow-execution-attributes>
<webflow:always-redirect-on-pause value="false" />
<webflow:redirect-in-same-state value="false" />
webflow:flow-execution-attributes>
<webflow:flow-execution-listeners>
<webflow:listener ref="terminateWebSessionListener" />
webflow:flow-execution-listeners>
webflow:flow-executor>
<webflow:flow-registry id="logoutFlowRegistry" flow-builder-services="builder">
<webflow:flow-location path="/WEB-INF/logout-webflow.xml" id="logout" />
webflow:flow-registry>
<webflow:flow-builder-services id="builder" view-factory-creator="viewFactoryCreator" expression-parser="expressionParser" />
<bean id="logoutConversionService" class="org.jasig.cas.web.flow.LogoutConversionService" />
<bean id="terminateWebSessionListener" class="org.jasig.cas.web.flow.TerminateWebSessionListener"
p:timeToDieInSeconds="10"/>
<bean id="expressionParser" class="org.springframework.webflow.expression.spel.WebFlowSpringELExpressionParser"
c:conversionService-ref="logoutConversionService">
<constructor-arg>
<bean class="org.springframework.expression.spel.standard.SpelExpressionParser" />
constructor-arg>
bean>
<bean id="viewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
<property name="viewResolvers">
<util:list>
<ref local="viewResolver"/>
util:list>
property>
bean>
<bean id="abstractValidateController" class="org.jasig.cas.web.ServiceValidateController" abstract="true"
p:centralAuthenticationService-ref="centralAuthenticationService"
p:proxyHandler-ref="proxy20Handler"
p:argumentExtractor-ref="casArgumentExtractor"/>
<bean id="proxyValidateController" parent="abstractValidateController"/>
<bean id="serviceValidateController" parent="abstractValidateController"
p:validationSpecificationClass="org.jasig.cas.validation.Cas20WithoutProxyingValidationSpecification"/>
<bean id="v3AbstractValidateController" parent="abstractValidateController" abstract="true"
p:successView="cas3ServiceSuccessView"
p:failureView="cas3ServiceFailureView" />
<bean id="v3ProxyValidateController" parent="v3AbstractValidateController" />
<bean id="v3ServiceValidateController" parent="v3AbstractValidateController"
p:validationSpecificationClass="org.jasig.cas.validation.Cas20WithoutProxyingValidationSpecification"/>
<bean id="legacyValidateController" parent="abstractValidateController"
p:proxyHandler-ref="proxy10Handler"
p:successView="cas1ServiceSuccessView"
p:failureView="cas1ServiceFailureView"
p:validationSpecificationClass="org.jasig.cas.validation.Cas10ProtocolValidationSpecification"/>
<bean id="proxyController" class="org.jasig.cas.web.ProxyController"
p:centralAuthenticationService-ref="centralAuthenticationService"/>
<bean id="statisticsController" class="org.jasig.cas.web.StatisticsController"
p:casTicketSuffix="${host.name}" c:ticketRegistry-ref="ticketRegistry" />
<bean id="logoutAction" class="org.jasig.cas.web.flow.LogoutAction"
p:servicesManager-ref="servicesManager"
p:followServiceRedirects="${cas.logout.followServiceRedirects:false}"/>
<bean id="frontChannelLogoutAction" class="org.jasig.cas.web.flow.FrontChannelLogoutAction"
c:logoutManager-ref="logoutManager"/>
<bean id="healthCheckController" class="org.jasig.cas.web.HealthCheckController"
p:healthCheckMonitor-ref="healthCheckMonitor"/>
<bean id="initialFlowSetupAction" class="org.jasig.cas.web.flow.InitialFlowSetupAction"
p:argumentExtractors-ref="argumentExtractors"
p:warnCookieGenerator-ref="warnCookieGenerator"
p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator"/>
<bean id="authenticationViaFormAction" class="org.jasig.cas.web.flow.AuthenticationViaFormAction"
p:centralAuthenticationService-ref="centralAuthenticationService"
p:warnCookieGenerator-ref="warnCookieGenerator"
p:ticketRegistry-ref="ticketRegistry"/>
<bean id="authenticationExceptionHandler" class="org.jasig.cas.web.flow.AuthenticationExceptionHandler" />
<bean id="generateServiceTicketAction" class="org.jasig.cas.web.flow.GenerateServiceTicketAction"
p:centralAuthenticationService-ref="centralAuthenticationService"/>
<bean id="sendTicketGrantingTicketAction" class="org.jasig.cas.web.flow.SendTicketGrantingTicketAction"
p:centralAuthenticationService-ref="centralAuthenticationService"
p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator"/>
<bean id="gatewayServicesManagementCheck" class="org.jasig.cas.web.flow.GatewayServicesManagementCheck"
c:servicesManager-ref="servicesManager" />
<bean id="serviceAuthorizationCheck" class="org.jasig.cas.web.flow.ServiceAuthorizationCheck"
c:servicesManager-ref="servicesManager" />
<bean id="generateLoginTicketAction" class="org.jasig.cas.web.flow.GenerateLoginTicketAction"
p:ticketIdGenerator-ref="loginTicketUniqueIdGenerator"/>
<bean id="messageInterpolator" class="org.jasig.cas.util.SpringAwareMessageMessageInterpolator"/>
<bean id="credentialsValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
p:messageInterpolator-ref="messageInterpolator"/>
<bean id="ticketGrantingTicketCheckAction" class="org.jasig.cas.web.flow.TicketGrantingTicketCheckAction"
c:registry-ref="ticketRegistry" />
<bean id="terminateSessionAction" class="org.jasig.cas.web.flow.TerminateSessionAction"
c:cas-ref="centralAuthenticationService"
c:tgtCookieGenerator-ref="ticketGrantingTicketCookieGenerator"
c:warnCookieGenerator-ref="warnCookieGenerator"/>
<bean id="handlerMappingB"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/remoteLogin">remoteLoginControllerprop>
props>
property>
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
list>
property>
bean>
<bean id="flowUrlHandler" class="org.jasig.cas.web.flow.CasDefaultFlowUrlHandler" />
<bean id="remoteLoginController" class="org.springframework.webflow.mvc.servlet.FlowController">
<property name="flowExecutor" ref="remoteLoginFlowExecutor" />
<property name="flowUrlHandler" ref="loginFlowUrlHandler" />
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="com.xb.casLogin.RemoteLoginAction"
p:argumentExtractors-ref="argumentExtractors"
p:warnCookieGenerator-ref="warnCookieGenerator"
p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator" />
beans>
4,新建一个文件与login-webflow.xml同级,remoteLogin-webflow.xml:,这个与3.0的版本有很多差别,如果你用的还是3.0版本的。就需要注意了。
<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">
<var name="credentials"
class="org.jasig.cas.authentication.UsernamePasswordCredential" />
<action-state id="remoteLogin">
<evaluate expression="remoteLoginAction" />
<transition on="error" to="viewServiceErrorView" />
<transition on="submit" to="bindAndValidate" />
<transition on="checkTicketGrantingTicket" to="ticketGrantingTicketExistsCheck" />
action-state>
<action-state id="ticketGrantingTicketExistsCheck">
<evaluate expression="ticketGrantingTicketCheckAction.checkValidity(flowRequestContext)"/>
<transition on="notExists" to="gatewayRequestCheck"/>
<transition on="invalid" to="terminateSession"/>
<transition on="valid" to="hasServiceCheck"/>
action-state>
<action-state id="terminateSession">
<evaluate expression="terminateSessionAction.terminate(flowRequestContext)"/>
<transition to="generateLoginTicket"/>
action-state>
<decision-state id="gatewayRequestCheck">
<if test="requestParameters.gateway != '' and requestParameters.gateway != null and flowScope.service != null"
then="gatewayServicesManagementCheck" else="serviceAuthorizationCheck" />
decision-state>
<decision-state id="hasServiceCheck">
<if test="flowScope.service != null"
then="renewRequestCheck"
else="viewGenericLoginSuccess" />
decision-state>
<decision-state id="renewRequestCheck">
<if test="requestParameters.renew != '' and requestParameters.renew != null"
then="serviceAuthorizationCheck" else="generateServiceTicket" />
decision-state>
<action-state id="serviceAuthorizationCheck">
<evaluate expression="serviceAuthorizationCheck"/>
<transition to="generateLoginTicket"/>
action-state>
<decision-state id="warn">
<if test="flowScope.warnCookieValue"
then="showWarningView" else="redirect" />
decision-state>
<action-state id="generateLoginTicket">
<evaluate expression="generateLoginTicketAction.generate(flowRequestContext)" />
<transition on="generated" to="remoteCallbackView" />
action-state>
<view-state id="remoteCallbackView" view="remoteCallbackView" 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>
<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>
<action-state id="realSubmit">
<evaluate expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credentials, messageContext)" />
<transition on="warn" to="warn" />
<transition on="success" to="sendTicketGrantingTicket" />
<transition on="successWithWarnings" to="showMessages" />
<transition on="authenticationFailure" to="handleAuthenticationFailure" />
<transition on="error" to="generateLoginTicket" />
action-state>
<view-state id="showMessages" view="casLoginMessageView">
<on-entry>
<evaluate expression="sendTicketGrantingTicketAction" />
<set name="requestScope.messages" value="messageContext.allMessages" />
on-entry>
<transition on="proceed" to="serviceCheck" />
view-state>
<action-state id="handleAuthenticationFailure">
<evaluate expression="authenticationExceptionHandler.handle(currentEvent.attributes.error, messageContext)" />
<transition on="AccountDisabledException" to="casAccountDisabledView"/>
<transition on="AccountLockedException" to="casAccountLockedView"/>
<transition on="CredentialExpiredException" to="casExpiredPassView"/>
<transition on="InvalidLoginLocationException" to="casBadWorkstationView"/>
<transition on="InvalidLoginTimeException" to="casBadHoursView"/>
<transition on="FailedLoginException" to="generateLoginTicket"/>
<transition on="AccountNotFoundException" to="generateLoginTicket"/>
<transition on="UNKNOWN" to="generateLoginTicket"/>
action-state>
<action-state id="sendTicketGrantingTicket">
<evaluate expression="sendTicketGrantingTicketAction" />
<transition to="serviceCheck" />
action-state>
<decision-state id="serviceCheck">
<if test="flowScope.service != null"
then="generateServiceTicket"
else="viewGenericLoginSuccess" />
decision-state>
<action-state id="generateServiceTicket">
<evaluate expression="generateServiceTicketAction" />
<transition on="success" to="warn" />
<transition on="error" to="generateLoginTicket" />
<transition on="gateway" to="gatewayServicesManagementCheck" />
<transition on="authenticationFailure" to="handleAuthenticationFailure" />
action-state>
<action-state id="gatewayServicesManagementCheck">
<evaluate expression="gatewayServicesManagementCheck" />
<transition on="success" to="redirect" />
action-state>
<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>
<end-state id="viewGenericLoginSuccess" view="casLoginGenericSuccessView" />
<end-state id="showWarningView" view="casLoginConfirmView" />
<end-state id="abstactPasswordChangeView">
<on-entry>
<set name="flowScope.passwordPolicyUrl" value="passwordPolicy.passwordPolicyUrl" />
on-entry>
end-state>
<end-state id="casExpiredPassView" view="casExpiredPassView" parent="#abstactPasswordChangeView" />
<end-state id="casMustChangePassView" view="casMustChangePassView" parent="#abstactPasswordChangeView" />
<end-state id="casAccountDisabledView" view="casAccountDisabledView" />
<end-state id="casAccountLockedView" view="casAccountLockedView" />
<end-state id="casBadHoursView" view="casBadHoursView" />
<end-state id="casBadWorkstationView" view="casBadWorkstationView" />
<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>
5,回调页面jsp:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<script type="text/javascript">
var remoteUrl = "${remoteLoginUrl}?validated=true";
//登陆回调页面。
var re1 = '${loginTicket}';
var re2 = '${flowExecutionKey}';
console.log("-loginTicket--"+re1);
console.log("-execution--"+re2);
// 构造错误消息,从webflow scope中取出
var errorMessage = '${remoteLoginMessage}';
/*
errorMessage = "&errorMessage=" + encodeURIComponent(' ');
*/
// 如果存在错误消息则追加到 url中
if(null != errorMessage && errorMessage.length > 0)
{
errorMessage = "&errorMessage=" + encodeURIComponent(errorMessage);
}
console.log("客户端remoteUrl:---"+remoteUrl + errorMessage );
// 构造service
var service = "";
var loginTicket = "";
<c:if test="${service != null && service != ''}">
service = "&service=" + encodeURIComponent("${service}");
</c:if>
<c:if test="${loginTicket != null && loginTicket != ''}">
loginTicket = "&loginTicket=" + "${loginTicket}";
</c:if>
console.log("客户端service:"+ service);
var flowExecutionKey ="&flowExecutionKey=" + re2;
// 跳转回去(客户端)
window.location.href = remoteUrl + errorMessage + service+loginTicket+flowExecutionKey;
</script>
6.在default_views.properties中新增以下两句:
remoteCallbackView.(class)=org.springframework.web.servlet.view.JstlView
remoteCallbackView.url=/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp
完成,希望你也能一次成功。
当访问子系统的资源时:
127.0.0.1:8080/casClient/pages/index.jsp
会被拦截然后调往服务端的remoteLogin流程。服务器处理完后,地址链接就会跳转到:
127.0.0.1:8080/casClient/login.jsp?validated=true&service=http%3A%2F%2F127.0.0.1%3A8080%2FcasClient%2Fpages%2Findex.jsp&loginTicket=LT-2-gqkSPrIxkevb5TyqU2QprMQauC92OE-cas01.example.org&flowExecutionKey=e1s1
此时我们看到2 个关键的参数已经有值 loginTicket和 flowExecutionKey。
登录界面如下:
下面是我自己项目的连接地址,有需要可以去下载。
http://download.csdn.net/download/qinzuoguo/10133874