CAS项目登录流程介绍(二)

来源:互联网 发布:js将对象转换成字符串 编辑:程序博客网 时间:2024/05/22 16:43

CAS项目登录流程介绍(二)


上一篇介绍了cas的登陆流程,因为cas属于第三方登陆系统,用户在经过cas认证后会跳会原来用户访问的资源。

所以就会存在外部系统会和cas系统进行一系列的授权确认,保证用户是有权限访问现有资源的。


在应用端配置一些cas相关的filter

<bean id="casSingleSignOutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" /><bean id="casAuthenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter"><dynamic:property-config name="casServerLoginUrl" param-key="${config.cas.login.url}" default-value="http://${domain}/cas/login" /><dynamic:property-config name="serverName" param-key="${config.server.url}" default-value="http://${domain}" /></bean><bean id="casValidationFilter" class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter"><property name="ticketValidator"><bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator" p:encoding="UTF-8"><dynamic:constructor-config name="casServerUrlPrefix"param-key="${config.cas.url}" default-value="http://${domain}/cas" /></bean></property><dynamic:property-config name="serverName" param-key="${config.server.url}" default-value="http://${domain}" /><dynamic:property-config name="casServerLoginUrl" param-key="${config.cas.login.url}" default-value="http://${domain}/cas/login" /></bean><bean id="casAssertionThreadLocalFilter" class="org.jasig.cas.client.util.AssertionThreadLocalFilter" />

下面我来一个一个解释下这些filter的工作内容。


1.casSingleSignOutFilter

这个是单点登出功能,当用户在他处登出的时候就会在cas内部注销一切票据(tgt,st),而st和每个外部应用有关。所以登出时,cas会向所有外部应用发出一段saml,告诉这些应用该用户的票据失效了。

public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {        final HttpServletRequest request = (HttpServletRequest) servletRequest;        if (handler.isTokenRequest(request)) {            handler.recordSession(request);        } else if (handler.isLogoutRequest(request)) {            handler.destroySession(request);            // Do not continue up filter chain            return;        } else {            log.trace("Ignoring URI " + request.getRequestURI());        }        filterChain.doFilter(servletRequest, servletResponse);    } 
   public void recordSession(final HttpServletRequest request) {        final HttpSession session = request.getSession(true);        final String token = CommonUtils.safeGetParameter(request, this.artifactParameterName);        if (log.isDebugEnabled()) {            log.debug("Recording session for token " + token);        }        try {            this.sessionMappingStorage.removeBySessionById(session.getId());        } catch (final Exception e) {            // ignore if the session is already marked as invalid.  Nothing we can do!        }        sessionMappingStorage.addSessionById(token, session);    }
   public void destroySession(final HttpServletRequest request) {        final String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName);        if (log.isTraceEnabled()) {            log.trace ("Logout request:\n" + logoutMessage);        }                final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");        if (CommonUtils.isNotBlank(token)) {            final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token);            if (session != null) {                String sessionID = session.getId();                if (log.isDebugEnabled()) {                    log.debug ("Invalidating session [" + sessionID + "] for token [" + token + "]");                }                try {                    session.invalidate();                } catch (final IllegalStateException e) {                    log.debug("Error invalidating session.", e);                }            }        }    }
当用户的token不是登出请求时,调用recordSession,登出调用destroySession。我们可以看到其内部使用一个SessionMappingStorage对象维护session和st的关系。

而无论登出还是其他行为,cas等会带上st参数与外部应用发生交互。当登出是,这个filter做两件式意识移除SessionMappingStorage相应的session,而是将这个session失效。

2.casAuthenticationFilter

 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 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 = CommonUtils.safeGetParameter(request,getArtifactParameterName());        final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);        if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {            filterChain.doFilter(request, response);            return;        }        final String modifiedServiceUrl;        log.debug("no ticket and no assertion found");        if (this.gateway) {            log.debug("setting gateway attribute in session");            modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);        } else {            modifiedServiceUrl = serviceUrl;        }        if (log.isDebugEnabled()) {            log.debug("Constructed service url: " + modifiedServiceUrl);        }        final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);        if (log.isDebugEnabled()) {            log.debug("redirecting to \"" + urlToRedirectTo + "\"");        }        response.sendRedirect(urlToRedirectTo);    }
这个流程还是分成两部分。如果有st则进入下一个流程。如果无则重定向到cas/login服务,前面对这个服务介绍过是个weblfow,在拥有tgt情况下会生成st跳回,如果没有tgt那就必须输入用户名密码再跳回。

在跳回后url一定会用st参数所以,直接进入下一个filter


3.casValidationFilter

这是验证st的过程,客户端拿到st需要确保这个st是否是cas正确签发的。

这里直接贴核心方法dofilter,在他的父类中

 public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {        if (!preFilter(servletRequest, servletResponse, filterChain)) {            return;        }        final HttpServletRequest request = (HttpServletRequest) servletRequest;        final HttpServletResponse response = (HttpServletResponse) servletResponse;        final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());        if (CommonUtils.isNotBlank(ticket)) {            if (log.isDebugEnabled()) {                log.debug("Attempting to validate ticket: " + ticket);            }            try {                final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));                if (log.isDebugEnabled()) {                    log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName());                }                request.setAttribute(CONST_CAS_ASSERTION, assertion);                if (this.useSession) {                    request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion);                }                onSuccessfulValidation(request, response, assertion);                if (this.redirectAfterValidation) {                    log. debug("Redirecting after successful ticket validation.");                    response.sendRedirect(constructServiceUrl(request, response));                    return;                }....................................
这里客户端会将st作为参数,访问cas的cas/serviceValidate服务,返回assertion。如果不正确会抛出异常,如果正确则在request放入CONST_CAS_ASSERTION参数。只要参数存在,用户一次的访问行为将不再需要认证。


4.casAssertionThreadLocalFilter
这个filter做的事情很简单,将request中的assertion拿出来,放入一个threadlocal变量中,供其他的filter使用,最后再将filter清楚。
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {        final HttpServletRequest request = (HttpServletRequest) servletRequest;        final HttpSession session = request.getSession(false);        final Assertion assertion = (Assertion) (session == null ? request.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION));        try {            AssertionHolder.setAssertion(assertion);            filterChain.doFilter(servletRequest, servletResponse);        } finally {            AssertionHolder.clear();        }    }
原创粉丝点击