具有 CAS 身份验证和自定义授权的 Spring Security
Posted
技术标签:
【中文标题】具有 CAS 身份验证和自定义授权的 Spring Security【英文标题】:Spring Security with CAS authentication and custom authorization 【发布时间】:2012-10-25 19:04:56 【问题描述】:我是 Spring Security 和 CAS 的新手。我让 SSO 部分正常工作,但正在努力让授权部分正常工作。我按照这个来实现自定义授权
http://www.theserverside.com/tip/-Spring-Security-Customizing-Your-User-and-Authorization-in
应该何时调用 loadUserDetails。我在这里设置了一个断点,这个方法永远不会被调用。
任何帮助将不胜感激。看起来我缺少调用授权的配置。
我想让我的自定义用户对象填充权限并驻留在会话中,以便我可以在任何阶段检查用户角色。
当我登录日志时,这里是我登录后发生的步骤
1. /indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas at position 1 of 8 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' (FilterChainProxy:329)
No HttpSession currently exists (HttpSessionSecurityContextRepository:127)
No SecurityContext was available from the HttpSession: null. A new one will be created. (HttpSessionSecurityContextRepository:85)
2. /indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas at position 2 of 8 in additional filter chain; firing Filter: 'CasAuthenticationFilter' (FilterChainProxy:329)
3. /indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas at position 3 of 8 in additional filter chain; firing Filter: 'RequestCacheAwareFilter' (FilterChainProxy:329)
4. /indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas at position 4 of 8 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter' (FilterChainProxy:329)
5. /indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas at position 5 of 8 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter' (FilterChainProxy:329)
Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@6faa93c2: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffffe21a: RemoteIpAddress: 192.168.0.124; SessionId: null; Granted Authorities: ROLE_ANONYMOUS' (AnonymousAuthenticationFilter:102)
6. /indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas at position 6 of 8 in additional filter chain; firing Filter: 'SessionManagementFilter' (FilterChainProxy:329)
7. /indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas at position 7 of 8 in additional filter chain; firing Filter: 'ExceptionTranslationFilter' (FilterChainProxy:329)
8. /indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas at position 8 of 8 in additional filter chain; firing Filter: 'FilterSecurityInterceptor' (FilterChainProxy:329)
Checking match of request : '/indexcasepackoptions'; against '/login/**' (AntPathRequestMatcher:103)
Secure object: FilterInvocation: URL: /indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas; Attributes: [ROLE_ANONYMOUS] (FilterSecurityInterceptor:193)
Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@6faa93c2: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffffe21a: RemoteIpAddress:
192.168.0.124; SessionId: null; Granted Authorities: ROLE_ANONYMOUS (FilterSecurityInterceptor:298)
Voter: org.springframework.security.access.vote.RoleVoter@7077602d, returned: 1 (AffirmativeBased:65)
Authorization successful (FilterSecurityInterceptor:214)
RunAsManager did not change Authentication object (FilterSecurityInterceptor:226)
/indexCasePackOptions?ticket=ST-4295-4eFUHVziBcmVaOd3bifl-cas reached end of additional filter chain; proceeding with original chain (FilterChainProxy:315)
No Proxy Ticket found for (ProxyGrantingTicketStorageImpl:77)
SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. (HttpSessionSecurityContextRepository:269)
Chain processed normally (ExceptionTranslationFilter:115)
SecurityContextHolder now cleared, as request processing completed (SecurityContextPersistenceFilter:97)
我的自定义用户详情
package com.creata.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements Serializable, UserDetails
private static final long serialVersionUID = 1L;
private Collection<SimpleGrantedAuthority> authorities;
private final String username;
private String password;
public User(String username)
this.username=username;
@Override
public Collection<SimpleGrantedAuthority> getAuthorities()
return authorities;
public void setUserAuthorities(List<String> roles)
List<SimpleGrantedAuthority> listOfAuthorities = new ArrayList<SimpleGrantedAuthority>();
for (String role : roles)
listOfAuthorities.add(new SimpleGrantedAuthority(role));
authorities = listOfAuthorities;
@Override
public String getPassword()
return this.password;
@Override
public String getUsername()
return username;
@Override
public boolean isAccountNonExpired()
return false;
@Override
public boolean isAccountNonLocked()
return false;
@Override
public boolean isCredentialsNonExpired()
return false;
@Override
public boolean isEnabled()
return true;
我的自定义用户服务
package com.creata.security.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.creata.domain.User;
public class CustomUserService implements UserDetailsService, AuthenticationUserDetailsService
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException
System.out.println("loadUserByUsername called here ");
User currentUser = new User("Reena");
//Collection<SimpleGrantedAuthority> authorities = currentUser.getAuthorities();
List<String> roles = new ArrayList<String>();
roles.add("ROLE_Admin");
roles.add("ROLE_CPAD-Maintenance");
currentUser.setUserAuthorities(roles);
//authorities.add(new SimpleGrantedAuthority("PRIVILEGE_HOME"));
return currentUser;
@Override
public UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException
User currentUser = new User("Reena");
List<String> roles = new ArrayList<String>();
roles.add("ROLE_Admin");
roles.add("ROLE_CPAD-Maintenance");
currentUser.setUserAuthorities(roles);
//authorities.add(new SimpleGrantedAuthority("PRIVILEGE_HOME"));
return currentUser;
这是我的安全配置:
<beans:beans
xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<http entry-point-ref="casEntryPoint">
<custom-filter position="CAS_FILTER" ref="casFilter" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="casAuthenticationProvider" />
</authentication-manager>
<beans:bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"/>
</beans:bean>
<beans:bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<beans:property name="loginUrl" value="https://abc.com/cas/login"/>
<beans:property name="serviceProperties" ref="serviceProperties"/>
</beans:bean>
<beans:bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<beans:property name="authenticationUserDetailsService" ref="customUserService"/>
<beans:property name="serviceProperties" ref="serviceProperties" />
<beans:property name="ticketValidator">
<beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<beans:constructor-arg index="0" value="https://portal.creata.com/cas" />
</beans:bean>
</beans:property>
<beans:property name="key" value="an_id_for_this_auth_provider_only"/>
</beans:bean>
<beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<beans:property name="service" value="https://mypc.abc.com/Spring_CPAD2/"/>
</beans:bean>
<beans:bean id="customUserService" class="com.creata.security.service.CustomUserService"/>
</beans:beans>
这是我的 web.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
metadata-complete="true">
<display-name />
<context-param>
<param-name>serverName</param-name>
<param-value>https://cpaus-rsingh2.creata.com</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://abc.com/cas/login?AppName=app1</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>https://abc.com/cas</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
<filter>
<filter-name>ResponseOverrideFilter</filter-name>
<filter-class>org.displaytag.filter.ResponseOverrideFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ResponseOverrideFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter>
<description>generated-persistence-filter</description>
<filter-name>com_ibm_db2_jcc_DB2DriverFilter</filter-name>
<filter-class>
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
</filter-class>
<init-param>
<param-name>entityManagerFactoryBeanName</param-name>
<param-value>com_ibm_db2_jcc_DB2Driver</param-value>
</init-param>
</filter>
<filter>
<description>generated-sitemesh-filter</description>
<filter-name>Sitemesh Filter</filter-name>
<filter-class>
com.opensymphony.module.sitemesh.filter.PageFilter
</filter-class>
</filter>
<!-- <filter>
<description>generated-persistence-filter</description>
<filter-name>MyEclipse_DerbyFilter</filter-name>
<filter-class>
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
</filter-class>
<init-param>
<param-name>entityManagerFactoryBeanName</param-name>
<param-value>MyEclipse_Derby</param-value>
</init-param>
</filter> -->
<filter-mapping>
<filter-name>com_ibm_db2_jcc_DB2DriverFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Sitemesh Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- <filter-mapping>
<filter-name>MyEclipse_DerbyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> -->
<servlet>
<description>generated-servlet</description>
<servlet-name>Spring_CPAD2 Servlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:Spring_CPAD2-web-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<description>generated-resources-servlet</description>
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>
org.springframework.js.resource.ResourceServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<description>generated-webflow-servlet</description>
<servlet-name>webflow</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/config/Spring_CPAD2-webflow-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Resource Servlet</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Spring_CPAD2 Servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>webflow</servlet-name>
<url-pattern>*.flow</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>jsp/intro.jsp</welcome-file>
</welcome-file-list>
<jsp-config>
<taglib>
<taglib-uri>MyCustomTags</taglib-uri>
<taglib-location>/WEB-INF/tlds/MyCustomTags.tld</taglib-location>
</taglib>
</jsp-config>
【问题讨论】:
我应该调试代码的客户端,看看为什么我的自定义 UserDetails 没有被调用,还是发生在 CAS 服务器端?抱歉,如果这听起来像是一个基本问题,我无法解决此问题并感到困惑。在我的控制器中 SecurityContextHolder.getContext().getAuthentication().getPrincipal();让我匿名用户。如何获取已实现的 UserDetails?有人请给我亮光 @rodrigoap 我在***.com/questions/1322135/… 看到了你的回答,你能帮我解决我的问题吗?我需要创建一个预认证过滤器吗?我的方法不正确吗。我错过了什么? 这个问题你解决了吗?我遇到了同样的问题,我无法解决,如果你解决了,请发布答案 @rdev 我也有同样的问题。你罚款解决方案吗?如果没有,您是否知道任何替代解决方案?谢谢 【参考方案1】:CasAuthenticationProvider 仅在身份验证为空时调用 userDetailsService
我建议在 CasAuthenticationProvider.java 中放置一个调试点(在下面的代码中参考我的 cmets)
public class CasAuthenticationProvider
public Authentication authenticate(Authentication authentication) throws AuthenticationException
CasAuthenticationToken result = null;
if (stateless)
// Try to obtain from cache
result = statelessTicketCache.getByTicketId(authentication.getCredentials().toString());
if (result == null)
//following line calls userDetailsService. I would recommend to put a debug point here
///if it is not calling userDetailsService, that means result is not null
result = this.authenticateNow(authentication);
result.setDetails(authentication.getDetails());
if (stateless)
// Add to cache
statelessTicketCache.putTicketInCache(result);
return result;
您可以从http://mvnrepository.com/artifact/org.springframework.security/spring-security-cas-client 下载源代码并将其附加到eclipse。
【讨论】:
感谢您查看我的问题,我按照您的建议设置了断点,但是当我登录时,不会调用此方法。还有什么我可以尝试的吗? @Saurabh 我在static.springsource.org/spring-security/site/docs/3.1.x/… 中读到,要使 CAS 运行,ExceptionTranslationFilter 必须将其 authenticationEntryPoint 属性设置为 CasAuthenticationEntryPoint bean。我添加了以上是关于具有 CAS 身份验证和自定义授权的 Spring Security的主要内容,如果未能解决你的问题,请参考以下文章
未指定 authenticationScheme,并且没有找到具有默认身份验证和自定义授权的 DefaultChallengeScheme
使用 CAS 进行身份验证和 LDAP 进行授权的 Spring 项目