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


2)自定义登录RemoteLoginAction(远程登陆票据提供Action,根据InitialFlowSetupAction修改,相关的webflow 根据login-webflow.xml 修改)

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


3)自定义退出RemoteLogoutAction。销毁票据,删除cookie后,跳转到指定的cas-server-web 的页面中,在此页面中再一次跳转到指定的页面。
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;    }}


修改cas-server-webapp 工程 

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


6)增加远程登录流程控制remoteLogin-webflow.xml

<?xml version="1.0" encoding="UTF-8"?><flow xmlns="http://www.springframework.org/schema/webflow"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xsi:schemaLocation="http://www.springframework.org/schema/webflow                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"      start-state="remoteLogin">       <!-- 远程登陆主要Action -->       <action-state id="remoteLogin">              <evaluate expression="remoteLoginAction" />              <transition on="error" to="remoteCallbackView" />              <transition on="submit" to="authcodeValidate" />              <transition on="checkTicketGrantingTicket" to="ticketGrantingTicketExistsCheck" />       </action-state>       <!-- 远程回调页面,主要以JavaScript的方式回传一些参数用 -->       <end-state id="remoteCallbackView" view="remoteCallbackView" />       <action-state id="authcodeValidate">              <evaluate expression="authenticationViaFormAction.validatorCode(flowRequestContext, messageContext)" />              <transition on="error" to="remoteCallbackView" />              <transition on="success" to="bindAndValidate" />       </action-state>       <action-state id="bindAndValidate">              <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" />              <transition on="success" to="submit" />              <transition on="error" to="remoteCallbackView" />       </action-state>       <decision-state id="ticketGrantingTicketExistsCheck">              <if test="flowScope.ticketGrantingTicketId != null" then="hasServiceCheck" else="gatewayRequestCheck" />       </decision-state>       <decision-state id="hasServiceCheck">              <if test="flowScope.service != null" then="generateServiceTicket" else="remoteCallbackView" />       </decision-state>       <decision-state id="gatewayRequestCheck">              <if test="externalContext.requestParameterMap['gateway'] != '' 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>


7)增加远程退出流程控制remoteLogout-webflow.xml

<?xml version="1.0" encoding="UTF-8"?><flow xmlns="http://www.springframework.org/schema/webflow"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xsi:schemaLocation="http://www.springframework.org/schema/webflow                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"      start-state="remoteLogout">       <!-- 远程登陆主要Action -->       <action-state id="remoteLogout">              <evaluate expression="remoteLogoutAction" />              <transition on="success" to="remoteCallbackView" />       </action-state>       <end-state id="remoteCallbackView" view="remoteCallbackView" />       <global-transitions>              <transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException" />              <transition to="viewServiceSsoErrorView" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException" />              <transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException" />       </global-transitions></flow>


8)配置remoteCallbackView显示节点,修改src/default_views.properties文件,增加remoteCallbackView配置

### 配置自定义远程登录失败回调页面remoteCallbackView.(class)=org.springframework.web.servlet.view.JstlViewremoteCallbackView.url=/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp


9)创建/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp文件(即上面的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;    }}


2)登录成功后,ST超时失效抛出异常解决,跳转到首页重新获取ST

修改: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;}


客户端demo工程
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







0 0
原创粉丝点击