spring security 与cas 结合

简介:

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
< 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


1
2
3
4
5
6
7
8
9
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是否需要进一步的认证处理


1
2
3
public  CasAuthenticationFilter() {  
     super ( "/j_spring_cas_security_check" );  
}



CasAuthenticationFilter类的attemptAuthentication方法源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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的,所以接下来看

1
2
3
< 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是如何完成认证工作的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Java代码    
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





      本文转自布拉君君 51CTO博客,原文链接:http://blog.51cto.com/5148737/1827795,如需转载请自行联系原作者



相关文章
|
4天前
|
安全 Java API
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)
22 0
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
|
1月前
|
存储 安全 Java
Spring Boot整合Spring Security--学习笔记
Spring Boot整合Spring Security--学习笔记
53 0
|
15天前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
|
4天前
|
存储 安全 Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(下)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
17 2
|
4天前
|
安全 Cloud Native Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(上)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
20 2
|
4天前
|
安全 Java API
第5章 Spring Security 的高级认证技术(2024 最新版)(上)
第5章 Spring Security 的高级认证技术(2024 最新版)
29 0
|
4天前
|
存储 安全 Java
第3章 Spring Security 的用户认证机制(2024 最新版)(下)
第3章 Spring Security 的用户认证机制(2024 最新版)
28 0
|
4天前
|
存储 安全 Java
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
13 0
|
4天前
|
安全 Java 数据库
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(上)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)
28 0
|
4天前
|
安全 Java API
第1章 Spring Security 概述(2024 最新版)(下)
第1章 Spring Security 概述(2024 最新版)
16 0

热门文章

最新文章