CAS单点登录开源框架解读(三)--CAS单点登录服务端认证之loginFlowRegistry流程

如何进入loginFlowRegistry流程

1、如何从”/”变为”/login”

在4.2.6源码包中的cas-server-webapp工程下,webapp\WEB-INF\目录下有个web.xml文件,其中有如下的配置。

*web.xml:*
<welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

由”/”转化为”index.jsp”页面,index.jsp页面在webapp\目录下。

*index.jsp:*
<%@ page language="java"  session="false" %>
<%
final String queryString = request.getQueryString();
final String url = request.getContextPath() + "/login" + (queryString != null ? '?' + queryString : "");
response.sendRedirect(response.encodeURL(url));%>

如果对于是CAS客户端请求过来的地址,会被CAS客户端的过滤器进行封装后形成请求CAS单点登录服务端的重定向地址为如下格式:http://localhost:8080/cas/login?service=http%3A%2F%2Flocalhost%3A8088%2Fcas-client%2F。
但是在本文进行源码解读时,直接访问的是CAS单点登录服务端的地址,因此index.jsp中的queryString的值为空,所以会直接请求跳转到http://localhost:8080/cas/login地址。

2、如何映射login到CAS中,通过servlet-mapping到cas

在cas-server-webapp工程下,webapp\WEB-INF\目录下的web.xml文件。通过最基础的servlet进行相关的映射。

*web.xml:*
<servlet-mapping>
        <servlet-name>cas</servlet-name>
        <url-pattern>/login</url-pattern>
</servlet-mapping>

3、CAS把映射交给Spring的DispatcherServlet处理

我们知道CAS服务端是采用了Spring web flow来进行流程的处理,那么如何将servlet和Spring进行结合的。还是看web.xml配置文件。

*web.xml:*
<servlet>
        <servlet-name>cas</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- Load the child application context. Start with the default, then modules, then overlays. -->
            <param-value>/WEB-INF/cas-servlet.xml,classpath*:/META-INF/cas-servlet-*.xml,/WEB-INF/cas-servlet-*.xml</param-value>
        </init-param>
        <init-param>
            <param-name>publishContext</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>

请注意配置文件中的/WEB-INF/cas-servlet.xml这个配置文件。

4、解析cas-servlet.xml相关配置

我们目前关心的还是登录流程到底是怎么进行的,那么请看下面这段配置。

*cas-servlet.xml:*
<!-- login webflow configuration -->
    <bean id="loginFlowHandlerMapping" class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping"
          p:flowRegistry-ref="loginFlowRegistry" p:order="2">
        <property name="interceptors">
            <array value-type="org.springframework.web.servlet.HandlerInterceptor">
                <ref bean="localeChangeInterceptor"/>
                <ref bean="authenticationThrottle"/>
            </array>
        </property>
    </bean>

从注释可以知道这是登录流程的入口配置,定义流程句柄bean为loginFlowHandlerMapping,流程为loginFlowRegistry。那么这个loginFlowRegistry属性是在哪里进行了定义呢?这个其实是在Spring相关的配置文件中进行配置的,那么我们继续看是如何解析Spring的配置文件的。
还是在web.xml文件中,我们看一下Spring的配置文件加载了哪一些。

*web.xml:*
<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring-configuration/*.xml
            /WEB-INF/deployerConfigContext.xml
            <!-- this enables extensions and addons to contribute to overall CAS' application context
                 by loading spring context files from classpath i.e. found in classpath jars, etc. -->
            classpath*:/META-INF/spring/*.xml
        </param-value>
    </context-param>

从上面的配置信息可以看出spring配置在/WEB-INF/spring-configuration/*.xml中。那么loginFlowRegistry这个是在哪里定义的呢,我们可以猜测这个应该是和webflow相关的,所以可以在Spring的配置文件下找到webflowContext.xml文件。

5、解析webflowContext.xml上下文配置

webflowContext.xml在/WEB-INF/spring-configuration/目录下。

*webflowContext.xml:*
<webflow:flow-registry id="loginFlowRegistry" flow-builder-services="builder" base-path="/WEB-INF/webflow">
        <webflow:flow-location-pattern value="/login/*-webflow.xml"/>
</webflow:flow-registry>

< webflow:flow-registry id="logoutFlowExecutor" flow-registry="logoutFlowRegistry">
        <webflow:flow-execution-attributes>
            <webflow:always-redirect-on-pause value="false"/>
            <webflow:redirect-in-same-state value="false"/>
        </webflow:flow-execution-attributes>
</webflow:flow-registry>

<webflow:flow-registry id="logoutFlowRegistry" flow-builder-services="builder" base-path="/WEB-INF/webflow">
        <webflow:flow-location-pattern value="/logout/*-webflow.xml"/>
</webflow:flow-registry>

定义了cas-servlet.xml 里登录Mapping对应的loginFlowRegistry,其流程配置文件/login/-webflow.xml即login-webflow.xml。同时还配置了退出流程logoutFlowRegistry,其配置文件/logout/-webflow.xml即logout-webflow.xml。

6、解析login-webflow.xml配置文件的初始化登录action

启动登录流程的是initialFlowSetupAction这个类。(具体解析login-webflow.xml文件请查看下一章节)

*login-webflow.xml:*

<var name="credential" class="org.jasig.cas.authentication.UsernamePasswordCredential"/>

    <!--
    <var name="credential" class="org.jasig.cas.authentication.RememberMeUsernamePasswordCredential" />
    -->
    <on-start>
        <evaluate expression="initialFlowSetupAction"/>
    </on-start>

7、登录流程处理HandlerAdapter

找到了登录流程,那么是如何通过/login进入此登录流程的呢?下面我们还是看/WEB-INF/cas-servlet.xml这个配置文件。

 *cas-servlet.xml:*
  <bean id="loginHandlerAdapter" class="org.jasig.cas.web.flow.SelectiveFlowHandlerAdapter"
          p:supportedFlowId="login" p:flowExecutor-ref="loginFlowExecutor" p:flowUrlHandler-ref="loginFlowUrlHandler"/>

    <bean id="loginFlowUrlHandler" class="org.jasig.cas.web.flow.CasDefaultFlowUrlHandler"/>

    <bean name="loginFlowExecutor" class="org.springframework.webflow.executor.FlowExecutorImpl"
          c:definitionLocator-ref="loginFlowRegistry"
          c:executionFactory-ref="loginFlowExecutionFactory"
          c:executionRepository-ref="loginFlowExecutionRepository"/>

    <bean name="loginFlowExecutionFactory" class="org.springframework.webflow.engine.impl.FlowExecutionImplFactory"
          p:executionKeyFactory-ref="loginFlowExecutionRepository"/>

请注意loginHandlerAdapter这个配置的bean,其中的属性有supportedFlowId的值为“login”,同时属性flowExecutor-ref的引用值为loginFlowExecutor。再看loginFlowExecutor这个bean中所配置的登录流程属性引用值就是我们webflow上下文配置中的loginFlowRegistry这个属性。
因此我们来看一下loginHandlerAdapter这个bean对应的类为org.jasig.cas.web.flow.SelectiveFlowHandlerAdapter所起的作用,是如何来处理登录动作的。先来看一下这个类的父类org.springframework.webflow.mvc.servlet.FlowHandlerAdapter,这个是Springmvc中的一个类。

public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAdapter, InitializingBean {

FlowHandlerAdapter实现接口HandlerAdapter,而SelectiveFlowHandlerAdapter继承自FlowHandlerAdapter(如下)。SelectiveFlowHandlerAdapter类在cas-server-webapp-actions模块下的org.jasig.cas.web.flow包下。
因此Spring的DispatcherServlet找到要处理的handleAdapter是SelectiveFlowHandlerAdapte。而且根据地址http://localhost:8080/cas/login?service=XXX,得到handler的flowId=“login”,即流程:loginFlowRegistry,然后进入下面的handle方法,开始调取流程:

*org.jasig.cas.web.flow.SelectiveFlowHandlerAdapter.java*
public class SelectiveFlowHandlerAdapter extends FlowHandlerAdapter {
//此handle方法在父类中FlowHandlerAdapter中
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		FlowHandler flowHandler = (FlowHandler) handler;
		checkAndPrepare(request, response, false);
//注意flowUrlHandler通过cas-servlet.xml中的配置类为//org.jasig.cas.web.flow.CasDefaultFlowUrlHandler,通过request来获取名称为
//“execution”的参数值,也就是flowExecutionKey。
		String flowExecutionKey = flowUrlHandler.getFlowExecutionKey(request);
//对于第一请求跳转到登录页面,这个值为空,执行else中的代码
		if (flowExecutionKey != null) {
			try {
				ServletExternalContext context = createServletExternalContext(request, response);
				FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context);
				handleFlowExecutionResult(result, context, request, response, flowHandler);
			} catch (FlowException e) {
				handleFlowException(e, request, response, flowHandler);
			}
		} else {
			try {
//第一次访问,在配置文件中配置的flowId为“login”
				String flowId = getFlowId(flowHandler, request);
				MutableAttributeMap<Object> input = getInputMap(flowHandler, request);
				ServletExternalContext context = createServletExternalContext(request, response);
//注意这里是最关键的点,加载登录流程的起始处,也就是通过这里把login-webflow.xml中//相关配置的流程走一遍。
				FlowExecutionResult result = flowExecutor.launchExecution(flowId, input, context);
				handleFlowExecutionResult(result, context, request, response, flowHandler);
			} catch (FlowException e) {
				handleFlowException(e, request, response, flowHandler);
			}
		}
		return null;
	}
发布了19 篇原创文章 · 获赞 5 · 访问量 3876
展开阅读全文

CAS服务端登陆成功,不能返回目标页面

08-16

输入网址 http://fighting.com:8080/loginPlatformClient 跳转到https://fighting.com:8443/cas/login?service=http%3A%2F%2Ffighting.com%3A8080%2FloginPlatformClient%2Fj_spring_cas_security_check输入账户和密码登陆成功 却跳转到 http://fighting.com:8080/loginPlatformClient/页面这是为什么? 如果输入网址http://fighting.com:8080/loginPlatformClient/indexUser.jsp 跳转到https://fighting.com:8443/cas/login?service=http%3A%2F%2Ffighting.com%3A8080%2FloginPlatformClient%2Fj_spring_cas_security_check输入用户名 密码后http://fighting.com:8080/loginPlatformClient/这是为什么? 配置如下: <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth2="http://www.springframework.org/schema/security/oauth2" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd"> <http auto-config="false" use-expressions="false" entry-point-ref="authEntryPoint" disable-url-rewriting="false"> <intercept-url pattern="/indexAdmin.jsp" access="ROLE_ADMIN" /> <intercept-url pattern="/indexUser.jsp" access="ROLE_USER" /> <intercept-url pattern="/indexSecurity.jsp" access="ROLE_SECURITY" /> <intercept-url pattern="/indexAuditor.jsp" access="ROLE_AUDITOR" /> <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <!-- 登出配置 --> <logout logout-url="/j_spring_security_logout" logout-success-url="/login.jsp" delete-cookies="JSESSIONID"/> <!-- 登出Cas Server的过滤器 --> <custom-filter ref="requestCasLogoutFilter" before="LOGOUT_FILTER"/> <!-- 登出Spring Security的过滤器 --> <custom-filter ref="casLogoutFilter" before="CAS_FILTER"/> <custom-filter ref="casFilter" position="CAS_FILTER"/> <!-- 添加自己定义的AuthenticationFilter到FilterChain的FORM_LOGIN_FILTER位置 --> <custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/> </http> <beans:bean id="requestCasLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter"> <!-- 指定登出成功后需要跳转的地址,这里指向Cas Server的登出URL,以实现单点登出 --> <beans:constructor-arg value="https://fighting.com:8443/cas/logout"/> <beans:constructor-arg> <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/> </beans:constructor-arg> <!-- 该Filter需要处理的地址,默认是Spring Security的默认登出地址“/j_spring_security_logout” --> <beans:property name="filterProcessesUrl" value="/j_spring_cas_security_logout"/> </beans:bean> <beans:bean id="casLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/> <beans:bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter"> <beans:property name="authenticationManager" ref="authenticationManager" /> </beans:bean> <!-- AuthenticationEntryPoint,引导用户进行登录 --> <beans:bean id="authEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"> <beans:property name="loginUrl" value="https://fighting.com:8443/cas/login"/><!-- Cas Server的登录地址 --> <beans:property name="serviceProperties" ref="serviceProperties" /><!-- service相关的属性 --> </beans:bean> <!-- 指定service相关信息 --> <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties"> <!-- Cas Server认证成功后的跳转地址,这里要跳转到我们的Spring Security应用,之后会由CasAuthenticationFilter处理,默认处理地址为/j_spring_cas_security_check --> <beans:property name="service" value="http://fighting.com:8080/loginPlatformClient/j_spring_cas_security_check" /> <beans:property name="sendRenew" value="false"/> </beans:bean> <!-- 认证过滤器 --> <beans:bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter" > <!-- 登录提交处理 --> <beans:property name="filterProcessesUrl" value="/j_spring_security_check"></beans:property> <!-- 登录成功跳转 --> <beans:property name="authenticationSuccessHandler" ref="authenticationDispatcher"></beans:property> <!-- 设置登录失败的网址 --> <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property> <!-- 用户拥有权限 --> <beans:property name="authenticationManager" ref="authenticationManager" /> </beans:bean> <beans:bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <beans:property name="defaultFailureUrl" value="/login.jsp?error=true"></beans:property> </beans:bean> <authentication-manager alias="authenticationManager"> <!-- 使用自定义的重写的MyUserService类来实现从数据库中读取账户密码和权限,并在提交表单的过程中使用md5进行加密后再发送post请求到j_spring_security_check进行登录验证 --> <authentication-provider user-service-ref="MyUserService"> <password-encoder hash="md5" ref="passwordEncoder"> </password-encoder> </authentication-provider> <authentication-provider ref="casAuthenticationProvider"/> </authentication-manager> <beans:bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider"> <!-- 通过username来加载UserDetails --> <beans:property name="authenticationUserDetailsService"> <beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"> <!-- 真正加载UserDetails的UserDetailsService实现 --> <beans:constructor-arg ref="userDetailsManager" /> </beans:bean> </beans:property> <beans:property name="serviceProperties" ref="serviceProperties" /> <!-- 配置TicketValidator在登录认证成功后验证ticket --> <beans:property name="ticketValidator"> <beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"> <!-- Cas Server访问地址的前缀,即根路径--> <beans:constructor-arg index="0" value="https://fighting.com:8443/cas" /> </beans:bean> </beans:property> <beans:property name="key" value="key4CasAuthenticationProvider" /> </beans:bean> <!-- 认证成功的处理类 --> <beans:bean id="authenticationDispatcher" class="com.potevio.serivce.MyAuthenticationSuccessHandler"> <beans:property name="authDispatcherMap"> <beans:ref bean="dispatcherMap"/> </beans:property> </beans:bean> <beans:bean id="dispatcherMap" class="java.util.HashMap"> <beans:constructor-arg> <beans:map> <beans:entry key="ROLE_ADMIN" value="/indexAdmin.jsp"/> <beans:entry key="ROLE_USER" value="/indexUser.jsp"/> <beans:entry key="ROLE_SECURITY" value="/indexSecurity.jsp"/> <beans:entry key="ROLE_AUDITOR" value="/indexAuditor.jsp"/> </beans:map> </beans:constructor-arg> </beans:bean> <beans:bean id="userDetailsManager" class="com.potevio.serivce.UserDetailsManager"/> <beans:bean id="MyUserService" class="com.potevio.serivce.MyUserService"> </beans:bean> <beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"> </beans:bean> <!-- 用于使用eID登录后再登录系统的手动认证过程,读取bean MyUserService,构建 UserDetails --> <!-- <beans:bean id="SpringContextUtil " class="com.potevio.common.utils.SpringContextUtil "> </beans:bean> --> </beans:beans> 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览