cas源码改造-改造登录页面
来源:互联网 发布:c语言编译器推荐 知乎 编辑:程序博客网 时间:2024/05/01 14:45
实现原理:客户端提供一个登录界面,其中action为服务端登录地址,通过改写服务端的登录代码,生成ticket票据,写cookie,重定向到客户端的service地址(如果验证失败同样,并有错误码返回),过滤器判断是否带有ticket,验证通过获取远程用户名。
1、使用源码构建自己工程
版本:
cas-server 3.5.2 (https://github.com/apereo/cas/releases/tag/v3.5.2)
cas-client 3.4.1 (https://github.com/apereo/java-cas-client/releases)
cas-demo 为测试工程
其它的工程都是插件,可以不用(groupId,artifactId,version也可以自己定义)
改造登录页面分为服务器端和客户端,下面分别说明
2、服务器端改造
1)重载AuthenticationViaFormAction 的submit和validatorCode方法
submit 方法去掉了credentials 参数,该参数自己创建。用户名和密码从request 中获得。并且自定义errorCode 作为返回客户端的提示code。
this.centralAuthenticationService.createTicketGrantingTicket(credentials) 方法中实现了验证用户名和密码的方法。
public final String submit(final RequestContext context, final MessageContext messageContext) throws Exception { final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context); final Service service = WebUtils.getService(context); final HttpServletRequest request = WebUtils.getHttpServletRequest(context); //用户名和密码从rquest 中获得 String username = request.getParameter("username"); if(!StringUtils.hasText(username)){ context.getFlowScope().put("errorCode", "required.username");//设置errorCode ,在跳转页面返回给demo 页面提示错误信息 return "error"; } String password = request.getParameter("password"); if(!StringUtils.hasText(password)){ context.getFlowScope().put("errorCode", "required.password"); return "error"; } //用户名和密码从rquest 中获得,自己构建对象 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 { //createTicketGrantingTicket 方法验证用户名和密码(实现类SimpleTestUsernamePasswordAuthenticationHandler) 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"; } }validatorCode 方法,参考:http://blog.csdn.net/convict_eva/article/details/52848475。只是参数不同
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"; }
package org.jasig.cas.web.flow;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;import javax.servlet.http.HttpServletRequest;import javax.validation.constraints.NotNull;import java.util.List;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; }}
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()); } //回跳url 放入FlowScope 中 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);//销毁TGT this.ticketGrantingTicketCookieGenerator.removeCookie(response);//删除cookie 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; }}
4)web.xml,添加请求的servlet
<!-- remotelogin servlet --> <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>5)在cas-servlet.xml增加请求流程
<!-- 登录,登出的web-flow 配置 --> <bean id="remoteLoginHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/remoteLogin">remoteLoginController</prop><!-- servlet 对应的webflow --> <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" /> <!-- 登录,登出的web-flow 配置 -->
<?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'] != '' and externalContext.requestParameterMap['gateway'] != null and flowScope.service != 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 != 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>
<?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>
### 配置自定义远程登录失败回调页面remoteCallbackView.(class)=org.springframework.web.servlet.view.JstlViewremoteCallbackView.url=/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> // 构造错误消息,errorCode 为自定义放置在flowScope 中的值 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>
改造客户端:
1)自定义认证过滤器 RemoteAuthenticationFilter
主要改造为:
添加登录页面url属性loginUrl,验证失败时可跳转到此url
拦截验证要过滤掉此url
页面跳转url修改
package org.jasig.cas.client.authentication;import org.jasig.cas.client.Protocol;import org.jasig.cas.client.configuration.ConfigurationKey;import org.jasig.cas.client.configuration.ConfigurationKeys;import org.jasig.cas.client.util.AbstractCasFilter;import org.jasig.cas.client.util.CommonUtils;import org.jasig.cas.client.util.ReflectUtils;import org.jasig.cas.client.validation.Assertion;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.net.URL;import java.net.URLEncoder;import java.util.HashMap;import java.util.Map;public class RemoteAuthenticationFilter extends AbstractCasFilter { /** * The URL to the CAS Server login. */ private String casServerLoginUrl; /** * Whether to send the renew request or not. */ private boolean renew = false; /** * 本地登陆页面URL,登录失败重定向的页面。 */ private String localLoginUrl; /** * Whether to send the gateway request or not. */ private boolean gateway = false; private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl(); private AuthenticationRedirectStrategy authenticationRedirectStrategy = new DefaultAuthenticationRedirectStrategy(); private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null; private static final Map<String, Class<? extends UrlPatternMatcherStrategy>> PATTERN_MATCHER_TYPES = new HashMap<String, Class<? extends UrlPatternMatcherStrategy>>(); static { PATTERN_MATCHER_TYPES.put("CONTAINS", ContainsPatternUrlPatternMatcherStrategy.class); PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class); PATTERN_MATCHER_TYPES.put("EXACT", ExactUrlPatternMatcherStrategy.class); } public RemoteAuthenticationFilter() { this(Protocol.CAS2); } protected RemoteAuthenticationFilter(final Protocol protocol) { super(protocol); } protected void initInternal(final FilterConfig filterConfig) throws ServletException { if (!isIgnoreInitConfiguration()) { super.initInternal(filterConfig); setCasServerLoginUrl(getString(ConfigurationKeys.CAS_SERVER_LOGIN_URL)); setLocalLoginUrl(getString(new ConfigurationKey<String>("localLoginUrl", null)));//加载配置 setRenew(getBoolean(ConfigurationKeys.RENEW)); setGateway(getBoolean(ConfigurationKeys.GATEWAY)); final String ignorePattern = getString(ConfigurationKeys.IGNORE_PATTERN);//忽略拦截url配置 final String ignoreUrlPatternType = getString(ConfigurationKeys.IGNORE_URL_PATTERN_TYPE); if (ignorePattern != null) { final Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType); if (ignoreUrlMatcherClass != null) { this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlMatcherClass.getName()); } else { try { logger.trace("Assuming {} is a qualified class name...", ignoreUrlPatternType); this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlPatternType); } catch (final IllegalArgumentException e) { logger.error("Could not instantiate class [{}]", ignoreUrlPatternType, e); } } if (this.ignoreUrlPatternMatcherStrategyClass != null) { this.ignoreUrlPatternMatcherStrategyClass.setPattern(ignorePattern); } } final Class<? extends GatewayResolver> gatewayStorageClass = getClass(ConfigurationKeys.GATEWAY_STORAGE_CLASS); if (gatewayStorageClass != null) { setGatewayStorage(ReflectUtils.newInstance(gatewayStorageClass)); } final Class<? extends AuthenticationRedirectStrategy> authenticationRedirectStrategyClass = getClass(ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS); if (authenticationRedirectStrategyClass != null) { this.authenticationRedirectStrategy = ReflectUtils.newInstance(authenticationRedirectStrategyClass); } } } public void init() { super.init(); 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; //不拦截规则,添加如果访问路径为localLoginUrl且带有validated参数则跳过 URL url = new URL(localLoginUrl); if (isRequestUrlExcluded(request) || request.getRequestURI().endsWith(url.getPath())) { logger.debug("Request is ignored."); filterChain.doFilter(request, response); return; } 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 = retrieveTicketFromRequest(request); final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl); if (CommonUtils.isNotBlank(ticket) || wasGatewayed) { filterChain.doFilter(request, response); return; } final String modifiedServiceUrl; logger.debug("no ticket and no assertion found"); if (this.gateway) { logger.debug("setting gateway attribute in session"); modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl); } else { modifiedServiceUrl = serviceUrl; } logger.debug("Constructed service url: {}", modifiedServiceUrl); String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway); // 加入localLoginUrl urlToRedirectTo += (urlToRedirectTo.contains("?") ? "&" : "?") + "loginUrl=" + URLEncoder.encode(localLoginUrl, "utf-8"); logger.debug("redirecting to \"{}\"", urlToRedirectTo); this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo); } 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 setGatewayStorage(final GatewayResolver gatewayStorage) { this.gatewayStorage = gatewayStorage; } private boolean isRequestUrlExcluded(final HttpServletRequest request) { if (this.ignoreUrlPatternMatcherStrategyClass == null) { return false; } final StringBuffer urlBuffer = request.getRequestURL(); if (request.getQueryString() != null) { urlBuffer.append("?").append(request.getQueryString()); } final String requestUri = urlBuffer.toString(); return this.ignoreUrlPatternMatcherStrategyClass.matches(requestUri); } public String getLocalLoginUrl() { return localLoginUrl; } public void setLocalLoginUrl(String localLoginUrl) { this.localLoginUrl = localLoginUrl; }}
修改:Cas20ServiceTicketValidator parseResponseFromServer 方法:
//源码// final String error = XmlUtils.getTextForElement(response, "authenticationFailure");//// if (CommonUtils.isNotBlank(error)) {// throw new TicketValidationException(error);// } //验证失败,返回自定义message,在AbstractTicketValidationFilter 中捕获,做为特殊的处理 final String error = XmlUtils.getTextForElement(response,"authenticationFailure"); if (CommonUtils.isNotBlank(error)) { throw new TicketValidationException("convicteva-TicketValidation-fail"); }修改 AbstractTicketValidationFilter 的doFilter 方法:
} catch (final TicketValidationException e) {//如果是票据验证失败,就跳转到请求的urlif(e.getMessage().equalsIgnoreCase("convicteva-TicketValidation-fail")){response.sendRedirect(request.getRequestURL().toString());return;}response.setStatus(HttpServletResponse.SC_FORBIDDEN);logger.debug(e.getMessage(), e);onFailedValidation(request, response);if (this.exceptionOnValidationFailure) {throw new ServletException(e);}response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());return;}
1)项目依赖
<dependency><groupId>org.jasig.cas.client</groupId><artifactId>cas-client-core</artifactId><version>3.4.1</version></dependency>2)web.xml 配置
<!-- ======================== 单点登录配置 ======================== --> <!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置--> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!--该过滤器用于实现单点登出功能,可选配置。 --> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://sso.convicteva.com:8443/cas</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器负责用户的认证工作,必须启用它 --> <filter> <filter-name>CASFilter</filter-name> <filter-class>org.jasig.cas.client.authentication.RemoteAuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://sso.convicteva.com:8443/cas/remoteLogin</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://www.convicteva.com:8888/demo/</param-value> </init-param> <init-param> <param-name>localLoginUrl</param-name> <param-value>http://www.convicteva.com:8888/demo/login/page</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--该过滤器负责对Ticket的校验工作,必须启用它 --> <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>https://sso.convicteva.com:8443/cas</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://www.convicteva.com:8888/demo</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS_Validation_Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器负责实现HttpServletRequest请求的包裹, 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 --> <filter> <filter-name>HttpServletRequestWrapperFilter</filter-name> <filter-class> org.jasig.cas.client.util.HttpServletRequestWrapperFilter </filter-class> </filter> <filter-mapping> <filter-name>HttpServletRequestWrapperFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 比如AssertionHolder.getAssertion().getPrincipal().getName()。 --> <filter> <filter-name>assertionThreadLocalFilter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>assertionThreadLocalFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- ======================== 单点登录结束 ======================== --><!-- 自定义servlet --><servlet> <servlet-name>IndexServlet</servlet-name> <servlet-class>com.convicteva.sso.servlet.IndexServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>IndexServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>com.convicteva.sso.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/login/page</url-pattern> </servlet-mapping>
3)login.jsp
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!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" /> <title>登录</title></head><body><form id="myLoginForm" action="https://sso.convicteva.com:8443/cas/remoteLogin" method="POST"> <input type="hidden" name="service" value="http://www.convicteva.com:8888/demo" /> <input type="hidden" name="loginUrl" value="http://www.convicteva.com:8888/demo/login/page" /> <input type="hidden" name="submit" value="true" /> <table> <tr> <td>用户名:</td> <td><input type="text" value="" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="text" value="" name="password"></td> </tr> <tr> <td>验证码:</td> <td><input type="text" value="" name="authcode"><img src="https://sso.convicteva.com:8443/cas/captcha.jpg" alt="" /></td> </tr> <tr> <td align="right" colspan="2"><input type="submit" /></td> </tr> </table></form><!-- 错误码提示 --><input type="hidden" value="<%=request.getParameter("errorCode")%>" id="errorCode"/></body></html>
4)index.jsp
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!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" /> <title>登录成功</title></head><body><div class="welcome"> 您好:<%=request.getRemoteUser()%></div><div id="logout"> <a href="https://sso.convicteva.com:8443/cas/remoteLogout?service=http://www.convicteva.com:8888/demo/login/page">单点登出</a></div></body></html>
配置host:
127.0.0.1 sso.convicteva.com
127.0.0.1 www.convicteva.com
登录页面(www.convicteva.com:8888/demo/login/page):
登录成功:
登录成功后,访问cas,也是登录成功状态
修改代码:http://download.csdn.net/detail/convict_eva/9660148
- cas源码改造-改造登录页面
- 改造CAS单点登录 --- 自定义登陆页面(客户端)
- 改造CAS单点登录 --- 自定义登陆页面(服务端)
- 改造CAS单点登录 --- 验证码
- 改造CAS单点登录 --- 部署测试
- 单点登录(六)cas改造——退出到指定页面
- cas改造随笔
- cas改造随笔
- CAS Server改造
- cas server改造
- 基于CAS的单点登录SSO[2]: 改造cas-overlays-template支持MySQL数据库
- 基于CAS的单点登录SSO[3]: 改造cas-overlays-template支持Redis存储Ticket
- CAS单点登录(三)--服务端改造(登录页及登录方式的自定义)
- 单点登录(五)cas改造——使用jdbc进行用户验证
- SSO单点登录在互联网电商应用中的解决方案(基于CAS的改造)
- SSO单点登录在互联网电商应用中的解决方案(基于CAS的改造)
- SSO单点登录在互联网电商应用中的解决方案(基于CAS的改造)
- SSO单点登录在互联网电商应用中的解决方案(基于CAS的改造)
- SpringMVC原理图
- jtemplate示例
- 请问怎么用JS获取a标记的值
- 标准DOM和HTML DOM总结
- 心灵小鸡汤
- cas源码改造-改造登录页面
- 通过shadowsocks上网,根据国内外IP自动选择是否需要走代理
- 关于Android数据库orm工具库对比的几点思考(四)
- NYOJ - 17 - 单调递增最长子序列(动态规划--LIS--单调递增最长子序列)
- Python中的str is not callable问题分析
- Mysql几种索引方式的区别及适用情况
- 策略模式
- HTML5本地储存
- 第1节--python课程介绍