使用CAS和Spring Security重定向循环

时间:2014-02-21 11:18:12

标签: spring spring-security jersey cas

我在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 &#91;http-thread-pool-8181(4)&#93; &#40;ExceptionTranslationFilter.java&#58;165&#41; - 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 &#91;http-thread-pool-8181(4)&#93; &#40;HttpSessionRequestCache.java&#58;41&#41; - DefaultSavedRequest added to Session: DefaultSavedRequest[https://localhost:8181/springtest/?ticket=ST-44-L0mrrGmf3vNFeGXCRkAj]

DEBUG &#91;http-thread-pool-8181(4)&#93; &#40;ExceptionTranslationFilter.java&#58;185&#41; - Calling Authentication entry point.

DEBUG &#91;http-thread-pool-8181(4)&#93; &#40;HttpSessionSecurityContextRepository.java&#58;300&#41; - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.

DEBUG &#91;http-thread-pool-8181(4)&#93; &#40;SecurityContextPersistenceFilter.java&#58;97&#41; - SecurityContextHolder now cleared, as request processing completed

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;FilterChainProxy.java&#58;337&#41; - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 1 of 9 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;HttpSessionSecurityContextRepository.java&#58;148&#41; - HttpSession returned null object for SPRING_SECURITY_CONTEXT

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;HttpSessionSecurityContextRepository.java&#58;90&#41; - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@50e4c821. A new one will be created.

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;FilterChainProxy.java&#58;337&#41; - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 2 of 9 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;FilterChainProxy.java&#58;337&#41; - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 3 of 9 in additional filter chain; firing Filter: 'CasAuthenticationFilter'

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;CasAuthenticationFilter.java&#58;311&#41; - serviceTicketRequest = false

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;CasAuthenticationFilter.java&#58;362&#41; - proxyReceptorConfigured = false

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;CasAuthenticationFilter.java&#58;349&#41; - proxyReceptorRequest = false

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;CasAuthenticationFilter.java&#58;327&#41; - proxyTicketRequest = false

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;CasAuthenticationFilter.java&#58;262&#41; - requiresAuthentication = false

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;FilterChainProxy.java&#58;337&#41; - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 4 of 9 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;DefaultSavedRequest.java&#58;325&#41; - pathInfo: arg1=/; arg2=/ (property equals)

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;DefaultSavedRequest.java&#58;331&#41; - queryString: arg1=ticket=ST-44-L0mrrGmf3vNFeGXCRkAj; arg2=ticket=ST-45-3m2F3CVknJk6Af2u7d26 (property not equals)

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;HttpSessionRequestCache.java&#58;75&#41; - saved request doesn't match

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;FilterChainProxy.java&#58;337&#41; - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 5 of 9 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;FilterChainProxy.java&#58;337&#41; - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 6 of 9 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;AnonymousAuthenticationFilter.java&#58;102&#41; - 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 &#91;http-thread-pool-8181(1)&#93; &#40;FilterChainProxy.java&#58;337&#41; - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 7 of 9 in additional filter chain; firing Filter: 'SessionManagementFilter'

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;FilterChainProxy.java&#58;337&#41; - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 8 of 9 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;FilterChainProxy.java&#58;337&#41; - /?ticket=ST-45-3m2F3CVknJk6Af2u7d26 at position 9 of 9 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;AbstractSecurityInterceptor.java&#58;194&#41; - Secure object: FilterInvocation: URL: /?ticket=ST-45-3m2F3CVknJk6Af2u7d26; Attributes: [ROLE_USER]

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;AbstractSecurityInterceptor.java&#58;310&#41; - 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 &#91;http-thread-pool-8181(1)&#93; &#40;AffirmativeBased.java&#58;65&#41; - Voter: org.springframework.security.access.vote.RoleVoter@65b46ab9, returned: -1

DEBUG &#91;http-thread-pool-8181(1)&#93; &#40;AffirmativeBased.java&#58;65&#41; - Voter: org.springframework.security.access.vote.AuthenticatedVoter@27cacbd9, returned: 0

似乎CASFilter只是没有意识到提供了有效的服务票证。我错误配置了什么吗?

2 个答案:

答案 0 :(得分:5)

在最新版本中,默认网址现在为/login/cas而不是/j_spring_cas_security_checkhttps://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>