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

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();
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值