文档章节

Spring Security3源码分析-CAS支持

Dead_knight
 Dead_knight
发布于 2014/01/20 18:37
字数 1689
阅读 511
收藏 10

Spring Security3对CAS的支持主要在这个spring-security-cas-client-3.0.2.RELEASE.jar包中

Spring Security和CAS集成的配置资料很多。这里讲解的比较详细

http://lengyun3566.iteye.com/blog/1358323

配置方面,主要为下面的部分:

<security:http auto-config="true" entry-point-ref="casAuthEntryPoint" access-denied-page="/error/403.jsp">
		<security:custom-filter ref="casAuthenticationFilter" position="CAS_FILTER"/>
		<security:form-login login-page="/login.jsp"/>
		<security:logout logout-success-url="/login.jsp"/>
		<security:intercept-url pattern="/admin.jsp*" access="ROLE_ADMIN"/>
		<security:intercept-url pattern="/index.jsp*" access="ROLE_USER,ROLE_ADMIN"/>
		<security:intercept-url pattern="/home.jsp*" access="ROLE_USER,ROLE_ADMIN"/>
		<security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/>	
	</security:http>
	
	<security:authentication-manager alias="authenticationmanager">
		<security:authentication-provider ref="casAuthenticationProvider"/>
	</security:authentication-manager>
	
	<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
  		<property name="ticketValidator" ref="casTicketValidator"/>
  		<property name="serviceProperties" ref="casService"/>
  		<property name="key" value="docms"/>
  		<property name="authenticationUserDetailsService" ref="authenticationUserDetailsService"/>
	</bean>
	
	<bean id="casAuthEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
  		<property name="loginUrl" value="https://server:8443/cas/login"/>
  		<property name="serviceProperties" ref="casService"/>
	</bean>	
	
	<bean id="casService" class="org.springframework.security.cas.ServiceProperties">
		<property name="service" value="http://localhost:8888/docms/j_spring_cas_security_check"/>
	</bean>	
	
	<bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
  		<property name="authenticationManager" ref="authenticationmanager"/>
	</bean>
	
	<bean id="casTicketValidator" class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
  		<constructor-arg value="https://server:8443/cas/"/>
	</bean>

	<bean id="authenticationUserDetailsService" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
  		<property name="userDetailsService" ref="userDetailsManager"/>
	</bean>

 

这里需要强调一下http标签的entry-point-ref属性,因为之前没有着重的介绍,英文的意思是入口点引用。为什么需要这个入口点呢。这个入口点其实仅仅是被ExceptionTranslationFilter引用的。前面已经介绍过ExceptionTranslationFilter过滤器的作用是异常翻译,在出现认证异常、访问异常时,通过入口点决定redirect、forward的操作。比如现在是form-login的认证方式,如果没有通过UsernamePasswordAuthenticationFilter的认证就直接访问某个被保护的url,那么经过ExceptionTranslationFilter过滤器处理后,先捕获到访问拒绝异常,并把跳转动作交给入口点来处理。form-login的对应入口点类为LoginUrlAuthenticationEntryPoint,这个入口点类的commence方法会redirect或forward到指定的url(form-login标签的login-page属性)


清楚了entry-point-ref属性的意义。那么与CAS集成时,如果访问一个受保护的url,就通过CAS认证对应的入口点org.springframework.security.cas.web.CasAuthenticationEntryPoint类redirect到loginUrl属性所配置的url中,即一般为CAS的认证页面(比如:https://server:8443/cas/login)。

下面为CasAuthenticationEntryPoint类的commence方法。其主要任务就是构造跳转的url,再执行redirect动作。根据上面的配置,实际上跳转的url为:https://server:8443/cas/login?service=http%3A%2F%2Flocalhost%3A8888%2Fdocms%2Fj_spring_cas_security_check

public final void commence(final HttpServletRequest servletRequest, final HttpServletResponse response,
            final AuthenticationException authenticationException) throws IOException, ServletException {

        final String urlEncodedService = createServiceUrl(servletRequest, response);
        final String redirectUrl = createRedirectUrl(urlEncodedService);

        preCommence(servletRequest, response);
        response.sendRedirect(redirectUrl);
    }

 

 

接下来继续分析custom-filter ref="casAuthenticationFilter" position="CAS_FILTER"

这是一个自定义标签,并且在过滤器链中的位置是CAS_FILTER。这个过滤器在何时会起作用呢?带着这个疑问继续阅读源码

CasAuthenticationFilter对应的类路径是

org.springframework.security.cas.web.CasAuthenticationFilter

这个类与UsernamePasswordAuthenticationFilter一样,都继承于AbstractAuthenticationProcessingFilter。实际上所有认证过滤器都继承这个抽象类,其过滤器本身只要实现attemptAuthentication方法即可。

CasAuthenticationFilter的构造方法直接向父类的构造方法传入/j_spring_cas_security_check用于判断当前请求的url是否需要进一步的认证处理

public CasAuthenticationFilter() {
        super("/j_spring_cas_security_check");
    }

CasAuthenticationFilter类的attemptAuthentication方法源码如下

public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response)
            throws AuthenticationException {
        //设置用户名为有状态标识符
        final String username = CAS_STATEFUL_IDENTIFIER;
        //获取CAS认证成功后返回的ticket
        String password = request.getParameter(this.artifactParameter);

        if (password == null) {
            password = "";
        }
        //构造UsernamePasswordAuthenticationToken对象
        final UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        //由认证管理器完成认证工作
        return this.getAuthenticationManager().authenticate(authRequest);
    }

 在之前的源码分析中,已经详细分析了认证管理器AuthenticationManager认证的整个过程,这里就不再赘述了。

 

由于AuthenticationManager是依赖于具体的AuthenticationProvider的,所以接下来看

<security:authentication-manager alias="authenticationmanager">
<security:authentication-provider ref="casAuthenticationProvider"/>
</security:authentication-manager>

注意这里的ref属性定义。如果没有使用CAS认证,此处一般定义user-service-ref属性。这两个属性的区别在于

ref:直接将ref依赖的bean注入到AuthenticationProvider的providers集合中

user-service-ref:定义DaoAuthenticationProvider的bean注入到AuthenticationProvider的providers集合中,并且DaoAuthenticationProvider的变量userDetailsService由user-service-ref依赖的bean注入。

 

由此可见,采用CAS认证时,AuthenticationProvider只有AnonymousAuthenticationProvider和CasAuthenticationProvider

 

继续分析CasAuthenticationProvider是如何完成认证工作的

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        //省略若干判断
        CasAuthenticationToken result = null;
        //注意这里的无状态条件。主要用于无httpsession的环境中。如soap调用
        if (stateless) {
            // Try to obtain from cache
            //通过缓存来存储认证实体。主要避免每次请求最新ticket的网络开销
            result = statelessTicketCache.getByTicketId(authentication.getCredentials().toString());
        }

        if (result == null) {
            result = this.authenticateNow(authentication);
            result.setDetails(authentication.getDetails());
        }

        if (stateless) {
            // Add to cache
            statelessTicketCache.putTicketInCache(result);
        }

        return result;
    }

    //完成认证工作
    private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
        try {
            //通过cas client的ticketValidator完成ticket校验,并返回身份断言
            final Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), serviceProperties.getService());
           //根据断言信息构造UserDetails 
            final UserDetails userDetails = loadUserByAssertion(assertion);
           //检查账号状态
           userDetailsChecker.check(userDetails);
           //构造CasAuthenticationToken
            return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(), userDetails.getAuthorities(), userDetails, assertion);
        } catch (final TicketValidationException e) {
            throw new BadCredentialsException(e.getMessage(), e);
        }
    }

    //通过注入的authenticationUserDetailsService根据token中的认证主体即用户名获取UserDetails 
    protected UserDetails loadUserByAssertion(final Assertion assertion) {
        final CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, "");
        return this.authenticationUserDetailsService.loadUserDetails(token);
    }

需要注意的是为什么要定义authenticationUserDetailsService这个bean。由于CAS需要authentication-manager标签下定义<security:authentication-provider ref="casAuthenticationProvider"/>,而不是之前所介绍的
user-service-ref属性,所以这里仅仅定义了一个provider,而没有注入UserDetailsService,所以这里需要单独定义authenticationUserDetailsService这个bean,并注入到CasAuthenticationProvider中。

这里需要对CasAuthenticationToken、CasAssertionAuthenticationToken单独解释一下

CasAuthenticationToken:一个成功通过的CAS认证,与UsernamePasswordAuthenticationToken一样,都是继承于AbstractAuthenticationToken,并且最终会保存到SecurityContext上下文、session中

CasAssertionAuthenticationToken:一个临时的认证对象用于辅助获取UserDetails

 

配置文件中几个bean定义这里就不一一分析了,都是为了辅助完成CAS认证、跳转的工作。 

 

现在,可以对整个CAS认证的过程总结一下了:

1.客户端发起一个请求,试图访问系统系统中受保护的url

2.各filter链进行拦截并做相应处理,由于没有通过认证,ExceptionTranslationFilter过滤器会捕获到访问拒绝异常,并把该异常交给入口点处理

3.CAS 认证对应的入口点直接跳转到CAS Server端的登录界面,并携带参数service(一般为url:……/j_spring_cas_security_check)

4.CAS Server对登录信息进行处理,如果登录成功,就跳转到应用系统中service指定的url,并携带ticket

5.应用系统中的各filter链再次对该url拦截,此时CasAuthenticationFilter拦截到j_spring_cas_security_check,就会对ticket进行验证,验证成功返回一个身份断言,再通过身份断言从当前应用系统中获取对应的UserDetails、GrantedAuthority。此时,如果步骤1中受保护的url权限列表有一个权限存在于GrantedAuthority列表中,说明有权限访问,直接响应客户端所试图访问的url

© 著作权归作者所有

共有 人打赏支持
Dead_knight

Dead_knight

粉丝 254
博文 31
码字总数 36296
作品 2
合肥
《Spring Security3》附录翻译(参考资料)

附录:参考材料 在本附录中,将会涉及到一些我们感觉有用的参考材料(并相当缺乏文档),而将其插入到章节的内容中又会觉得过于综合。 JBCP Pets示例代码起步 就像我们在第一章:一个不安全应...

小样
2012/03/07
0
0
Spring Security3源码分析-FilterChainProxy初始化

很久没有更新博客了,最近对Spring Security做了比较深入的研究。 spring security的教程网上很多: http://lengyun3566.iteye.com/category/153689 http://wenku.baidu.com/view/b0c0dc0b7...

Dead_knight
2014/01/20
0
2
spring security开篇

老实说,本人使用Spring Security的时候还是在2011年,正在国内某一知名集团做外包,那时候Spring MVC 3.0刚开始被大面积推崇。 当时计划是给信息平台做一个统一的单点登录,于是Spring Secu...

遥借东风
2016/07/07
51
0
cas的入门了解第一章:识别cas的目录结构和名词以及一些基本的概念

概念:耶鲁大学的一个开源项目。JA-SIG 中央认证服务(Central Authentication Service简称CAS)是一个开源的企业级单点登录的java 服务器组件。客户端有多种语言实现,包括:PHP,PL/SQL,java,...

天使broken
2015/07/28
0
0
spring security3.x学习(20)_初探authorize标签和第四个例子

本文为转载学习 原文链接:http://blog.csdn.net/dsundsun/article/details/11880743 第四个例子也就是spring security3那本书中的第五章的例子。 csdn下载:http://download.csdn.net/deta...

heroShane
2014/02/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Shell特殊符号总结以及cut,sort,wc,uniq,tee,tr,split命令

特殊符号总结一 * 任意个任意字符 ? 任意一个字符 # 注释字符 \ 脱义字符 | 管道符 # #号后的备注被忽略[root@centos01 ~]# ls a.txt # 备注 a.txt[root@centos01 ~]# a=1[root@centos01...

野雪球
31分钟前
1
0
OSChina 周二乱弹 —— 程序员圣衣

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @达尔文:分享Skeeter Davis的单曲《The End of the World》 《The End of the World》- Skeeter Davis 手机党少年们想听歌,请使劲儿戳(这里...

小小编辑
46分钟前
4
0
[ python import module ] 导入模块

import moudle_name ----> import module_name.py ---> import module_name.py文件路径 -----> sys.path (这里进行查找文件) # from app.web import Personimport app.web.Person as Pe......

_______-
昨天
3
0
Redis性能问题排查解决手册

一、性能相关的数据指标 通过Redis-cli命令行界面访问到Redis服务器,然后使用info命令获取所有与Redis服务相关的信息。通过这些信息来分析文章后面提到的一些性能指标。 nfo命令输出的数据可...

IT--小哥
昨天
1
0
mixin混入

①新建mixin.js文件 const mixin = { methods: { /** * 分页公共方法 */ handleSizeChange(val) { this.pageData.size = val; this.query(); }, hand......

不负好时光
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部