【问题标题】:CAS with Spring Security - Redirect loopCAS 与 Spring Security - 重定向循环
【发布时间】:2015-03-10 15:58:23
【问题描述】:

我一直在为集成了 Spring Security 的现有应用程序使用 CAS 配置 SSO 解决方案。我在 Stackoverflow 上经历了很多答案,但不幸的是任何人都可以帮助我。您能否从我的配置的角度帮助我解决这个问题?提前谢谢!

这是我的核心安全配置:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:security="http://www.springframework.org/schema/security"
   xmlns:util="http://www.springframework.org/schema/util"
   xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

<description>This context sets up security configurations of the core module.</description>

<!-- Enabled support for @Secured annotations on Spring bean methods -->
<security:global-method-security secured-annotations="enabled"
                                 access-decision-manager-ref="accessDecisionManager"/>

<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="authenticationUserDetailsService">
        <bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
            <constructor-arg ref="userDetailsService"/>
        </bean>
    </property>
    <property name="serviceProperties" ref="serviceProperties"/>
    <property name="ticketValidator">
        <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
            <constructor-arg index="0" value="https://localhost:8443/cas"/>
        </bean>
    </property>
    <property name="key" value="an_id_for_this_auth_provider_only"/>
</bean>

<security:http entry-point-ref="casEntryPoint">
    <security:custom-filter ref="casFilter" position="CAS_FILTER"/>
</security:http>

<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
</bean>

<bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
    <property name="loginUrl" value="https://localhost:8443/cas/login"/>
    <property name="serviceProperties" ref="serviceProperties"/>
</bean>

<bean id="exceptionTranslationFilter"
      class="org.springframework.security.web.access.ExceptionTranslationFilter">
    <property name="authenticationEntryPoint" ref="casEntryPoint"/>
</bean>

<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">        
    <property name="service" value="http://localhost:8080/myApp/j_spring_cas_security_check"/>
    <property name="sendRenew" value="false"/>
</bean>

<!-- Service that retrieves UserDetails from DB for authentication -->
<bean id="userDetailsService" class="com.xxx.yyy.core.security.userdetails.DefaultUserDetailsService">
    <property name="pmUserService" ref="pmUserService"/>
</bean>

<bean class="org.springframework.security.authentication.dao.ReflectionSaltSource" id="randomSaltSource">
    <property name="userPropertyToUse" value="salt"/>
</bean>


<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <property name="decisionVoters">
        <!-- At least one voter must agree that the user can access a resource -->
        <bean class="org.springframework.security.access.vote.RoleVoter">
            <!-- Override the default is 'ROLE_' prefix for role names -->
            <property name="rolePrefix">
                <util:constant static-field="com.xxx.yyy.core.security.SecurityConstants.AUTHORITY_PREFIX"/>
            </property>
        </bean>
    </property>
</bean>

...和额外的安全模块:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:security="http://www.springframework.org/schema/security"
   xmlns:util="http://www.springframework.org/schema/util"
   xsi:schemaLocation="
    http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/security   http://www.springframework.org/schema/security/spring-security-3.1.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring- util-3.0.xsd">

<description>This context sets up security configurations of the web module</description>

<import resource="web-security-urls.xml"/>

<!-- Spring Security Filter Chain -->
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
    <security:filter-chain-map path-type="ant">
        <security:filter-chain pattern="/**"
                               filters="securityContextPersistenceFilter,
                                        logoutFilter,
                                        authenticationFilter,
                                        anonymousAuthenticationFilter,
                                        exceptionTranslationFilter,
                                        filterSecurityInterceptor"/>
    </security:filter-chain-map>
</bean>

<!-- Responsible for propagation of SecurityContext on ThreadLocal from HttpSession -->
<bean id="securityContextPersistenceFilter"
      class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>

<!-- define the logout exit point -->
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <!-- go to login page upon successful logout -->
    <constructor-arg value="/"/>
    <!-- Classes that get run when a user logs out -->
    <constructor-arg>
        <list>
            <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
        </list>
    </constructor-arg>
    <property name="filterProcessesUrl" value="/j_spring_security_logout"/>
</bean>

<!-- Enable expression evaluation for Spring Security -->
<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>

<bean class="org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator">
    <constructor-arg ref="filterSecurityInterceptor"/>
</bean>

我们还有单独的模块web-security-urls.xml,其中o.s.s.web.access.intercept.FilterSecurityInterceptor被配置为声明安全URL

【问题讨论】:

  • 问题已解决!看来,我只需要在 filterChainProxy bean 中用 casFilter 替换 authenticationFilter

标签: spring-security single-sign-on cas jasig


【解决方案1】:

我不知道你是否还有这个问题。我也面临同样的情况,这令人伤脑筋。当CasAuthenticationFilter 无法确定传入的 URL(来自 CAS 服务器的重定向 URL)是否需要身份验证时,导致无限重定向的问题。

  if (!requiresAuthentication(request, response)) {
        chain.doFilter(request, response);

        return;
    }

要求身份验证检查请求是否被标识为“可过滤”URL。

 private static final class FilterProcessUrlRequestMatcher implements RequestMatcher {
    private final String filterProcessesUrl;

    private FilterProcessUrlRequestMatcher(String filterProcessesUrl) {
        Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
        Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
        this.filterProcessesUrl = filterProcessesUrl;
    }

    public boolean matches(HttpServletRequest request) {
        String uri = request.getRequestURI();
        int pathParamIndex = uri.indexOf(';');

        if (pathParamIndex > 0) {
            // strip everything after the first semi-colon
            uri = uri.substring(0, pathParamIndex);
        }

        if ("".equals(request.getContextPath())) {
            return uri.endsWith(filterProcessesUrl);
        }

        return uri.endsWith(request.getContextPath() + filterProcessesUrl);
    }
}

使用上下文路径,检查主要检查“服务 URL”是否以 request.getContextPath() + filterProcessesUrl 结尾

因此,ServiceProperties 对象的“服务”属性中的 URL 需要与 filterProcessesUrl 中提供的内容相匹配。 例如:

<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
    <property name="service"
        value="https://localhost:9444/SpringSecurity2.5/tbr/j_spring_cas_security_check" />
    <property name="sendRenew" value="false" />

</bean>

<bean id="casFilter"
    class="org.springframework.security.cas.web.CasAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="authenticationSuccessHandler">
        <bean
            class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler" />
    </property>
    <property name="filterProcessesUrl" value="/tbr/j_spring_cas_security_check"/>

</bean>

注意这两种配置中 /tbr/j_spring_cas_security_check 的匹配模式。

【讨论】:

  • 另外,在 spring-security+CAS 的情况下,服务 URL 的概念与“普通”CAS 客户端过滤的 Web 应用程序的工作方式不同。 Spring security 在尝试任何身份验证方案(BASIC、FORM BASED、CAS、X.509 等)之前在缓存(会话对象)中维护请求 URL。当身份验证成功时,它从缓存中获取原始请求并尝试客户端重定向(302)。因此,服务 URL 实际上不是您在 spring 控制器中映射的 URL,而只是被 CAS 过滤器识别的 URL
  • SavedRequestAwareAuthenticationSuccessHandler (SavedRequest) session.getAttribute(SAVED_REQUEST); String targetUrl = savedRequest.getRedirectUrl(); getRedirectStrategy().sendRedirect(request, response, targetUrl);
【解决方案2】:

我不确定这是否是确切的问题,但看起来您可能在此处缺少过滤器 url,因此您将获得无限重定向循环。

<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
</bean>

您可以添加过滤器 url 以仅过滤特定的 url 模式

<property name="filterProcessesUrl" value="/j_spring_cas_security_check"/>

当我遇到相同类型的问题时,这对我很有帮助。

【讨论】:

  • 感谢您的反馈!然而,不幸的是,它并没有太大帮助:(
猜你喜欢
  • 2015-09-12
  • 2014-07-18
  • 1970-01-01
  • 2014-01-21
  • 2021-02-03
  • 2019-08-05
  • 2015-01-12
  • 2015-08-31
  • 2015-11-22
相关资源
最近更新 更多