4

我最近将大部分 Spring 配置切换为使用 Spring 3.1 中基于代码的配置。但是,现在我已经切换,我的 Spring Security 无法正常工作,并在 Tomcat 启动时引发以下错误:

SEVERE: Exception starting filter springSecurityFilterChain
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named     'springSecurityFilterChain' is defined

我仍然在 XML 文件中有 Spring Security,并且知道它不能在 Spring 中转换为 Java 配置,因为它是一个自定义命名空间,但是,我在我的 Java 配置文件中引用它。我还尝试将 applicationContext-security.xml 配置引用从 Java 配置移动到我的 web.xml,但没有任何运气。

@Configuration
@EnableWebMvc
@Import(ServicesConfig.class)
@ImportResource({ "classpath:applicationContext-security.xml",
"classpath:dataSources.xml" })
@ComponentScan(basePackages = "com.foobar")
public class WebConfig {
   // left out Beans for clarity
}

applicationContext-security.xml:

<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
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/security/oauth2
      http://www.springframework.org/schema/security/spring-security-oauth2.xsd">

<!-- Security configuration -->

<global-method-security pre-post-annotations="enabled" />

<http use-expressions="true" access-denied-page="/error/accessDenied" 
        entry-point-ref="casEntryPoint">
    <intercept-url pattern="/**" access="isAuthenticated()" />
    <custom-filter position="CAS_FILTER" ref="casFilter" />
</http>

<authentication-manager alias="authenticationManager">
    <authentication-provider ref="casAuthenticationProvider" />
</authentication-manager>

<!-- Bean definitions -->
<b:bean id="casAuthenticationProvider"
    class="org.springframework.security.cas.authentication.CasAuthenticationProvider"
    p:serviceProperties-ref="serviceProperties" p:key="1234"
    p:authenticationUserDetailsService-ref="userDetailsByNameServiceWrapper"
    p:ticketValidator-ref="cas20ServiceTicketValidator" />

<b:bean id="userDetailsByNameServiceWrapper"
    class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"
    c:userDetailsService-ref="userDetailsService" />

<b:bean id="userDetailsService" class="foobar.MyUserDetailsService" />

<b:bean id="cas20ServiceTicketValidator" 
    class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"
    c:casServerUrlPrefix="https://mycasserver/cas" />

<b:bean id="casFilter"
    class="org.springframework.security.cas.web.CasAuthenticationFilter"
    p:authenticationManager-ref="authenticationManager" />

<b:bean id="casEntryPoint"
    class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
    p:loginUrl="https://mycasserver/cas/login" 
    p:serviceProperties-ref="serviceProperties" />

<b:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties"
    p:service="https://foobar.com/services/j_spring_cas_security_check"
    p:sendRenew="false" />

</b:beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<description>My App</description>

<!-- Context Params -->
<context-param>
    <param-name>contextClass</param-name>
    <param-value>
         org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
</context-param>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>foobar.WebConfig</param-value>
</context-param>

<!-- Filters --> 
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Listeners -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Servlets -->
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
         </param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

</web-app>
4

4 回答 4

8

不要使用security命名空间快捷方式并将所有 spring 配置从 XML 迁移到 Java。它将使您的安全性微调变得更加容易。我将在迁移到 3.1 后不久为我们的项目执行此操作。您可以在此处找到重要的几乎普通 bean 安全 XML 配置示例。

编辑: 完成配置(上面链接)迁移。故意将所有配置放入一个方法中以使其更短并证明您不需要为每个过滤器使用单独的 spring bean。当然,最好将复杂的初始化部分移动到单独的方法中(@Bean必要时标记)。X509AnnotationTest.Config您可以在上面的链接中找到工作示例。

@Bean
public FilterChainProxy springSecurityFilterChain() throws Exception {
    // AuthenticationEntryPoint
    BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
    entryPoint.setRealmName("AppName Realm");
    // accessDecisionManager
    List<AccessDecisionVoter> voters = Arrays.<AccessDecisionVoter>asList(new RoleVoter(), new WebExpressionVoter());
    AccessDecisionManager accessDecisionManager = new AffirmativeBased(voters);
    // SecurityExpressionHandler
    SecurityExpressionHandler<FilterInvocation> securityExpressionHandler = new DefaultWebSecurityExpressionHandler();
    // AuthenticationUserDetailsService
    UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService = new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>(authUserDetailService);
    authenticationUserDetailsService.afterPropertiesSet();
    // PreAuthenticatedAuthenticationProvider
    PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthenticatedAuthenticationProvider();
    preAuthenticatedAuthenticationProvider.setPreAuthenticatedUserDetailsService(authenticationUserDetailsService);
    preAuthenticatedAuthenticationProvider.afterPropertiesSet();
    // AuthenticationManager
    List<AuthenticationProvider> providers = Arrays.<AuthenticationProvider>asList(preAuthenticatedAuthenticationProvider);
    AuthenticationManager authenticationManager = new ProviderManager(providers);
    // HttpSessionSecurityContextRepository
    HttpSessionSecurityContextRepository httpSessionSecurityContextRepository = new HttpSessionSecurityContextRepository();
    // SessionRegistry
    SessionRegistry sessionRegistry = new SessionRegistryImpl();
    // ConcurrentSessionControlStrategy
    ConcurrentSessionControlStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlStrategy(sessionRegistry);

    // ConcurrentSessionFilter
    ConcurrentSessionFilter concurrentSessionFilter = new ConcurrentSessionFilter(sessionRegistry);
    concurrentSessionFilter.afterPropertiesSet();
    // SecurityContextPersistenceFilter
    SecurityContextPersistenceFilter securityContextPersistenceFilter = new SecurityContextPersistenceFilter(httpSessionSecurityContextRepository);
    // X509AuthenticationFilter
    X509AuthenticationFilter x509AuthenticationFilter = new X509AuthenticationFilter();
    x509AuthenticationFilter.setAuthenticationManager(authenticationManager);
    x509AuthenticationFilter.afterPropertiesSet();
    // RequestCacheAwareFilter
    RequestCacheAwareFilter requestCacheAwareFilter = new RequestCacheAwareFilter();
    // SecurityContextHolderAwareRequestFilter
    SecurityContextHolderAwareRequestFilter securityContextHolderAwareRequestFilter = new SecurityContextHolderAwareRequestFilter();
    // SessionManagementFilter
    SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(httpSessionSecurityContextRepository, concurrentSessionControlStrategy);
    // ExceptionTranslationFilter
    ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint);
    exceptionTranslationFilter.setAccessDeniedHandler(new AccessDeniedHandlerImpl());
    exceptionTranslationFilter.afterPropertiesSet();
    // FilterSecurityInterceptor
    FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
    filterSecurityInterceptor.setAuthenticationManager(authenticationManager);
    filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
    LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
    map.put(new AntPathRequestMatcher("/**"), Arrays.<ConfigAttribute>asList(new SecurityConfig("isAuthenticated()")));
    ExpressionBasedFilterInvocationSecurityMetadataSource ms = new ExpressionBasedFilterInvocationSecurityMetadataSource(map, securityExpressionHandler);
    filterSecurityInterceptor.setSecurityMetadataSource(ms);
    filterSecurityInterceptor.afterPropertiesSet();
    // SecurityFilterChain
    SecurityFilterChain chain = new DefaultSecurityFilterChain(new AntPathRequestMatcher("/**"),
            concurrentSessionFilter,
            securityContextPersistenceFilter,
            x509AuthenticationFilter,
            requestCacheAwareFilter,
            securityContextHolderAwareRequestFilter,
            sessionManagementFilter,
            exceptionTranslationFilter,
            filterSecurityInterceptor);
    return new FilterChainProxy(chain);
}
于 2012-04-04T19:11:50.490 回答
7

对于那些仍在寻找将 SpringSecurity XML 配置与 Java 配置 Web 应用程序一起使用的方法的人。我使用 Spring 3.2.0.RELEASE 和 SpringSecurity 3.2.0.M1 完成了这项工作。这是解决方案的重要部分

WebAppConfig.java

package com.foo.webapp;

@Configuration
@ComponentScan(basePackages = { "com.foo" })
@ImportResource(value = { "/WEB-INF/spring-security.xml" })
public class WebAppConfig {
    // other beans go here
}

spring-security.xml
请注意,我必须从 <beans:beans> 定义中删除默认的 xmlns="..." 。

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

    <security:http auto-config="true">
        <security:intercept-url pattern="/secured/**" access="ROLE_USER" />
        <security:form-login login-page="/login" default-target-url="/main"
                authentication-failure-url="/loginfailed" />
        <security:logout logout-success-url="/logout" />
    </security:http>

    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="user" authorities="ROLE_USER" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans:beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd">

    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.foo.webapp.WebAppConfig</param-value>
    </context-param>

    <!-- Spring Security -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>foo</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>foo</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
于 2013-01-22T21:53:08.213 回答
1

感谢大家在使用 Spring 的 Java 配置时提供有关 springSecurityFilterChain 的上述提示。当我一直在努力应用这些提示来修复我自己的代码中的类似错误时,我注意到 org.springframework.web.WebApplicationInitializer(由 AbstractAnnotationConfigDispatcherServletInitializer 实现的接口)的 Spring 文档具有以下相关的“警告”:

web.xml 版本控制

WEB-INF/web.xml 和 WebApplicationInitializer 的使用不是互斥的;例如,web.xml 可以注册一个 servlet,而 WebApplicationInitializer 可以注册另一个。初始化程序甚至可以通过 ServletContext.getServletRegistration(String) 等方法修改在 web.xml 中执行的注册。

但是,如果应用程序中存在 WEB-INF/web.xml,则其版本属性必须设置为“3.0”或更高版本,否则 ServletContainerInitializer 引导将被 servlet 容器忽略。

我指出这一点是因为我注意到上面提到的 web.xml 版本仍然是 2.5。

于 2014-03-26T18:42:47.000 回答
0

我让它与 Spring 3.2.3 和 Servlet 3.0 一起工作。打勾是扩展 DelegatingFilterProxy 类并将其命名为 SpringSecurityFilterChain。

public class ServiceInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] { ApplicationConfig.class };
}

@Override
protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[] { WebConfig.class };
}

@Override
protected String[] getServletMappings() {
    return new String[] { "/" };
}

@Override
protected Filter[] getServletFilters() {
    return new Filter[] { new OpenEntityManagerInViewFilter(), new SpringSecurityFilterChain() };
}

public class SpringSecurityFilterChain extends DelegatingFilterProxy {

}

}

于 2013-06-28T09:49:24.597 回答