我在GlassFish 3.1.2.2上设置了一个CAS 3.5.2服务器,现在我试图通过the official documentation使用Spring Security 3.2.0来保护使用CAS的Jersey REST Web服务。我的配置:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>springtest</display-name>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<!-- - Location of the XML file that defines the root application context
- Applied by ContextLoaderListener. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-security.xml
</param-value>
</context-param>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>cas.root</param-value>
</context-param>
<!-- Include the character encoding Filter as per JASIG recommenation when
doing Single Sign Out https://wiki.jasig.org/display/CASC/Configuring+Single+Sign+Out -->
<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>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Included to support Single Logout. Note that the SingleSignOutFilter
is included in the springSecurityFilterChain. However, it could also be placed
as the first filter-mapping in the web.xml -->
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
<!-- - Loads the root application context of this web app at startup. -
The application context is then available via - WebApplicationContextUtils.getWebApplicationContext(servletContext). -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Jersey Servlet config -->
<servlet>
<description>JAX-RS Tools Generated - Do not modify</description>
<servlet-name>JAX-RS Servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
applicationContext-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns="http://www.springframework.org/schema/security" 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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- This section is used to configure CAS. The service is the actual redirect
that will be triggered after the CAS login sequence. -->
<b:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<b:property name="service" value="https://localhost:8181/springtest/" />
<b:property name="sendRenew" value="false" />
</b:bean>
<!-- this is what hooks up the CAS entry point -->
<b:bean id="exceptionTranslationFilter"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<b:property name="authenticationEntryPoint">
<b:ref local="casEntryPoint"client />
</b:property>
</b:bean>
<!-- Enable security, let the casAuthenticationEntryPoint handle all intercepted
urls. The CAS_FILTER needs to be in the right position within the filter
chain. -->
<http entry-point-ref="casEntryPoint">
<intercept-url pattern="/**" access="ROLE_USER" />
<custom-filter position="CAS_FILTER" ref="casFilter" />
</http>
<!-- The CAS filter handles the redirect from the CAS server and starts
the ticket validation. -->
<b:bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
<b:property name="authenticationManager" ref="authenticationManager" />
</b:bean>
<!-- The entryPoint intercepts all the CAS authentication requests. It redirects
to the CAS loginUrl for the CAS login page. -->
<b:bean id="casEntryPoint"
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<b:property name="loginUrl" value="https://192.168.10.144/cas/login" />
<b:property name="serviceProperties" ref="serviceProperties" />
</b:bean>
<!-- Required for the casProcessingFilter, so define it explicitly set and
specify an Id Even though the authenticationManager is created by default
when namespace based config is used. -->
<authentication-manager alias="authenticationManager">
<authentication-provider ref="casAuthenticationProvider" />
</authentication-manager>
<!-- Handles the CAS ticket processing. -->
<b:bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<b:property name="authenticationUserDetailsService">
<b:bean
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<b:constructor-arg ref="userService" />
</b:bean>
</b:property>
<b:property name="serviceProperties" ref="serviceProperties" />
<b:property name="ticketValidator">
<b:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<b:constructor-arg index="0"
value="https://192.168.10.144/cas" />
</b:bean>
</b:property>
<b:property name="key" value="myCAS" />
</b:bean>
<!-- The users available for this application. -->
<user-service id="userService">
<user name="joe" password="joe" authorities="ROLE_USER" />
</user-service>
</b:beans>
我已确保该服务信任CAS服务器的证书,但不知道是否需要相反的方向。以下消息一遍又一遍地循环,直到浏览器“厌倦了它”:
log4j DEBUG消息
DEBUG [http-thread-pool-8181(4)] (ExceptionTranslationFilter.java:165) - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
...
DEBUG [http-thread-pool-8181(4)] (HttpSessionRequestCache.java:41) - DefaultSavedRequest added to Session: DefaultSavedRequest[https://localhost:8181/springtest/?ticket=ST-44-L0mrrGmf3vNFeGXCRkAj]
DEBUG [http-thread-pool-8181(4)] (ExceptionTranslationFilter.java:185) - Calling Authentication entry point.
DEBUG [http-thread-pool-8181(4)] (HttpSessionSecurityContextRepository.java:300) - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
DEBUG [http-thread-pool-8181(4)] (SecurityContextPersistenceFilter.java:97) - SecurityContextHolder now cleared, as request processing completed
DEBUG [http-thread-pool-8181(1)] (FilterChainProxy.java:337) - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 1 of 9 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
DEBUG [http-thread-pool-8181(1)] (HttpSessionSecurityContextRepository.java:148) - HttpSession returned null object for SPRING_SECURITY_CONTEXT
DEBUG [http-thread-pool-8181(1)] (HttpSessionSecurityContextRepository.java:90) - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@50e4c821. A new one will be created.
DEBUG [http-thread-pool-8181(1)] (FilterChainProxy.java:337) - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 2 of 9 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
DEBUG [http-thread-pool-8181(1)] (FilterChainProxy.java:337) - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 3 of 9 in additional filter chain; firing Filter: 'CasAuthenticationFilter'
DEBUG [http-thread-pool-8181(1)] (CasAuthenticationFilter.java:311) - serviceTicketRequest = false
DEBUG [http-thread-pool-8181(1)] (CasAuthenticationFilter.java:362) - proxyReceptorConfigured = false
DEBUG [http-thread-pool-8181(1)] (CasAuthenticationFilter.java:349) - proxyReceptorRequest = false
DEBUG [http-thread-pool-8181(1)] (CasAuthenticationFilter.java:327) - proxyTicketRequest = false
DEBUG [http-thread-pool-8181(1)] (CasAuthenticationFilter.java:262) - requiresAuthentication = false
DEBUG [http-thread-pool-8181(1)] (FilterChainProxy.java:337) - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 4 of 9 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
DEBUG [http-thread-pool-8181(1)] (DefaultSavedRequest.java:325) - pathInfo: arg1=/; arg2=/ (property equals)
DEBUG [http-thread-pool-8181(1)] (DefaultSavedRequest.java:331) - queryString: arg1=ticket=ST-44-L0mrrGmf3vNFeGXCRkAj; arg2=ticket=ST-45-3m2F3CVknJk6Af2u7d26 (property not equals)
DEBUG [http-thread-pool-8181(1)] (HttpSessionRequestCache.java:75) - saved request doesn't match
DEBUG [http-thread-pool-8181(1)] (FilterChainProxy.java:337) - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 5 of 9 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
DEBUG [http-thread-pool-8181(1)] (FilterChainProxy.java:337) - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 6 of 9 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
DEBUG [http-thread-pool-8181(1)] (AnonymousAuthenticationFilter.java:102) - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@6faa1b5a: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff6a82: RemoteIpAddress: 127.0.0.1; SessionId: 3e9339134a98fa96a8dd34676e8f; Granted Authorities: ROLE_ANONYMOUS'
DEBUG [http-thread-pool-8181(1)] (FilterChainProxy.java:337) - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 7 of 9 in additional filter chain; firing Filter: 'SessionManagementFilter'
DEBUG [http-thread-pool-8181(1)] (FilterChainProxy.java:337) - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 8 of 9 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
DEBUG [http-thread-pool-8181(1)] (FilterChainProxy.java:337) - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 9 of 9 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
DEBUG [http-thread-pool-8181(1)] (AbstractSecurityInterceptor.java:194) - Secure object: FilterInvocation: URL: /?ticket=ST-45-3m2F3CVknJk6Af2u7d26; Attributes: [ROLE_USER]
DEBUG [http-thread-pool-8181(1)] (AbstractSecurityInterceptor.java:310) - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@6faa1b5a: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff6a82: RemoteIpAddress: 127.0.0.1; SessionId: 3e9339134a98fa96a8dd34676e8f; Granted Authorities: ROLE_ANONYMOUS
DEBUG [http-thread-pool-8181(1)] (AffirmativeBased.java:65) - Voter: org.springframework.security.access.vote.RoleVoter@65b46ab9, returned: -1
DEBUG [http-thread-pool-8181(1)] (AffirmativeBased.java:65) - Voter: org.springframework.security.access.vote.AuthenticatedVoter@27cacbd9, returned: 0
似乎CASFilter只是没有意识到提供了有效的服务票证。我错误配置了什么吗?
答案 0 :(得分:5)
在最新版本中,默认网址现在为/login/cas
而不是/j_spring_cas_security_check
(https://jira.spring.io/browse/SEC-3053)
答案 1 :(得分:4)
看起来您的serviceProperties未正确定义。具体来说是the service must be a URL that is monitored by the CasAuthenticationFilter。否则,CasAuthenticationFilter会忽略该请求,然后Spring Security需要对URL进行身份验证并重新请求ST。
默认情况下,CasAuthenticationFilter处理对/ j_spring_cas_security_check的请求。所以你可能想要这样的东西:
<b:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<b:property name="service" value="https://localhost:8181/springtest/j_spring_cas_security_check" />
<b:property name="sendRenew" value="false" />
</b:bean>