基于服务端的修改流程:客户端所有的远程请求都必须携带有loginUrl参数信息以使得服务器端知道在认证失败后转向客户端登陆页面。
我们首先需要重新实现一个自己的认证过滤器,以下是我们自己的认证过滤器的代码:
/**
* 远程认证过滤器.
* 由于AuthenticationFilter的doFilter方法被声明为final,
* 只好重新实现一个认证过滤器,支持localLoginUrl设置.
*
*/
public class RemoteAuthenticationFilter extendsAbstractCasFilter {
public static finalString CONST_CAS_GATEWAY = "_const_cas_gateway_";
/**
* 本地登陆页面URL.
*/
privateString localLoginUrl;
/**
* The URL to the CASServer login.
*/
private StringcasServerLoginUrl;
/**
* Whether to send therenew request or not.
*/
private boolean renew= false;
/**
* Whether to send thegateway request or not.
*/
private booleangateway = false;
protected voidinitInternal(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 LocalLoginUrlparameter: " + 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 cannotbe null.");
CommonUtils.assertNotNull(this.casServerLoginUrl,"casServerLoginUrl cannot be null.");
}
public final voiddoFilter(final ServletRequest servletRequest,
finalServletResponse servletResponse, final FilterChain filterChain)
throwsIOException, ServletException {
finalHttpServletRequest request = (HttpServletRequest) servletRequest;
finalHttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpSessionsession = request.getSession(false);
final Stringticket = request.getParameter(getArtifactParameterName());
final Assertionassertion = session != null ? (Assertion) session
.getAttribute(CONST_CAS_ASSERTION) : null;
final booleanwasGatewayed = session != null
&&session.getAttribute(CONST_CAS_GATEWAY) != null;
// 如果访问路径为localLoginUrl且带有validated参数则跳过
URL url = newURL(localLoginUrl);
final booleanisValidatedLocalLoginUrl = 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 StringserviceUrl = constructServiceUrl(request, response);
if(log.isDebugEnabled()) {
log.debug("Constructed service url: " + serviceUrl);
}
StringurlToRedirectTo = 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 voidsetRenew(final boolean renew) {
this.renew =renew;
}
public final voidsetGateway(final boolean gateway) {
this.gateway =gateway;
}
public final voidsetCasServerLoginUrl(final String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}
public final voidsetLocalLoginUrl(String localLoginUrl) {
this.localLoginUrl= localLoginUrl;
}
}
代码均拷贝自org.jasig.cas.client.authentication.AuthenticationFilter,并进行修改,可以看到我们为原有的认证过滤器增加了一个参数localLoginUrl。在WEB-INF/web.xml中配置:
<filter>
<filter-name>CASAuthentication Filter</filter-name>
<filter-class>com.baidu.cas.client.validation.RemoteAuthenticationFilter</filter-class>
<init-param>
<param-name>localLoginUrl</param-name>
<param-value>http://client/login.jsp</param-value>//客户端未登录时转向页面
</init-param>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://server/remoteLogin</param-value>//服务端的验证地址,其中remoteLogin在服务端有配置
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://client </param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CASAuthentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
此处我们将过滤器指向自己的过滤器并增加本地登陆页面路径设置。最后我们来看看登陆页面login.jsp:
<%@ page language="java"contentType="text/html; charset=utf-8"pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<head>
<metahttp-equiv="Content-Type" content="text/html;charset=utf-8">
<title>远程CAS客户端登陆页面</title>
<linkrel="stylesheet" type="text/css" href="<%=request.getContextPath() %>/styles/main.css" />
<scripttype="text/javascript">
functiongetParam(name) {
var queryString =window.location.search;
var param =queryString.substr(1, query.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>远程CAS客户端登陆页面</h1>
<% if(request.getRemoteUser() == null) { %>
<divid="errorMessage"></div>
<formid="myLoginForm" action="https://server/remoteLogin"method="post">
<inputtype="hidden" id="service" name="service"value="">
<inputtype="hidden" name="loginUrl" value="http://client /login.jsp">
<inputtype="hidden" name="submit" value="true" />
<table>
<tr>
<td>用户名:</td>
<td><input type="text"name="username"></td>
</tr>
<tr>
<td>密 码:</td>
<td><input type="password"name="password"></td>
</tr>
<tr>
<tdcolspan="2"><input type="submit" value="登陆" /></td>
</tr>
</table>
</form>
<scripttype="text/javascript">init()</script>
<% } else { %>
<div class="welcome">您好:<%= request.getRemoteUser()%></div>
<divid="logout">
<ahref="https:// server/remoteLogout?service=http://client /login.jsp">单点
登出</a>
</div>
<% } %>
</body>
</html>
login.jsp为了简单起见,仅有是否已登陆判断使用了服务器端代码,其他均使用客户端代码实现。
我们首先将表单action指向服务器端remoteLogin,然后在里面设置了两个重要的hidden域以传递 loginUrl和submit参数,前者
用于告诉服务器失败后转向何处,后者告诉服务器端webflow现在要进行提交而不是TGT认证请求
http://localhost:3449/Admin/WebAPI/loginOSS.aspx