JavaConfig 没有定义名为 'springSecurityFilterChain' 的 bean

JavaConfig No bean named 'springSecurityFilterChain' is defined

我按照 http://shazsterblog.blogspot.com.es/2014/07/spring-security-custom-filterchainproxy.html 上的教程使用 Java 配置 而不是 XML 创建了一个 安全过滤器。

未创建 bean,应用程序加载失败:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:575)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1111)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:276)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1123)
    at org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:323)
    at org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:235)
    at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:194)
    at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:234)
    at org.apache.catalina.core.ApplicationFilterConfig.setFilterDef(ApplicationFilterConfig.java:332)
    at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:90)
    at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:3783)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4409)
    at org.jboss.web.tomcat.service.deployers.TomcatDeployment.performDeployInternal(TomcatDeployment.java:313)
    at org.jboss.web.tomcat.service.deployers.TomcatDeployment.performDeploy(TomcatDeployment.java:145)
    at org.jboss.web.deployers.AbstractWarDeployment.start(AbstractWarDeployment.java:461)
    at org.jboss.web.deployers.WebModule.startModule(WebModule.java:122)
    at org.jboss.web.deployers.WebModule.start(WebModule.java:97)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:157)
    at org.jboss.mx.server.Invocation.dispatch(Invocation.java:96)

这是web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/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.config.SecurityConfig</param-value>
      </context-param>

    <servlet>
        <servlet-name>Dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <init-param>
            <param-name>dispatchOptionsRequest</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/config/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Dispatcher</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/application-context.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <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>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>test</realm-name>
    </login-config>
    <security-role>
        <role-name>ADMIN</role-name>
    </security-role>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>COMUN</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ADMIN</role-name>
        </auth-constraint>
    </security-constraint>
</web-app>

这是SecurityConfig class:

@Configuration
@EnableWebMvc
public class SecurityConfig extends WebMvcConfigurerAdapter implements ResourceLoaderAware{

   private ResourceLoader resourceLoader;


   @Bean
   public FilterChainProxy springSecurityFilterChain() throws Exception {

      AuthenticationManager am = authenticationManager();

      SecurityContextPersistenceFilter sif = getSecurityContextPersistenceFilter();
      J2eePreAuthenticatedProcessingFilter j2eePreAuthFilter = getJ2eePreAuthenticatedProcessingFilter(am);
      LogoutFilter logoutFilter = getLogoutFilter(); 
      ExceptionTranslationFilter etf = getExceptionTranslationFilter();
      FilterSecurityInterceptor fsi = getFilterSecurityInterceptor(am);

      FilterChainProxy fcp = new FilterChainProxy(new DefaultSecurityFilterChain(
         new AntPathRequestMatcher("/**"),
         sif, j2eePreAuthFilter, logoutFilter, etf, fsi
      ));

      return fcp;
   }

    private FilterSecurityInterceptor getFilterSecurityInterceptor(AuthenticationManager am) {

        AccessDecisionVoter<Object> roleVoter = new RoleVoter();
        List<AccessDecisionVoter> decisionVoters = new LinkedList<AccessDecisionVoter>();
        decisionVoters.add(roleVoter);

        AffirmativeBased httpRequestAccessDecisionManager = new AffirmativeBased(decisionVoters);
        httpRequestAccessDecisionManager.setAllowIfAllAbstainDecisions(false);

        FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
        filterSecurityInterceptor.setAuthenticationManager(am);
        filterSecurityInterceptor.setAccessDecisionManager(httpRequestAccessDecisionManager);

        LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
        List<ConfigAttribute> configs = new ArrayList<ConfigAttribute>();
        configs.add(new org.springframework.security.access.SecurityConfig("hasRole(ADMIN)"));
        requestMap.put(new AntPathRequestMatcher("/**"), configs);
        FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource = new ExpressionBasedFilterInvocationSecurityMetadataSource(
                requestMap, new DefaultWebSecurityExpressionHandler());
        filterSecurityInterceptor
                .setSecurityMetadataSource(filterInvocationSecurityMetadataSource);

        return filterSecurityInterceptor;
    }

    private LogoutFilter getLogoutFilter() {
        org.springframework.security.web.authentication.logout.LogoutFilter logoutFilter = new LogoutFilter("/", new SecurityContextLogoutHandler());
        return logoutFilter;
    }

    private ExceptionTranslationFilter getExceptionTranslationFilter() {
        ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
                new Http403ForbiddenEntryPoint());
        return exceptionTranslationFilter;
    }

    private SecurityContextPersistenceFilter getSecurityContextPersistenceFilter() {
        return new org.springframework.security.web.context.SecurityContextPersistenceFilter();
    }

    private J2eePreAuthenticatedProcessingFilter getJ2eePreAuthenticatedProcessingFilter(AuthenticationManager am) throws Exception {

        WebXmlMappableAttributesRetriever mappableRolesRetriever = new WebXmlMappableAttributesRetriever();
        mappableRolesRetriever.setResourceLoader(this.resourceLoader);
        mappableRolesRetriever.afterPropertiesSet();

        SimpleAttributes2GrantedAuthoritiesMapper userRoles2GrantedAuthoritiesMapper = new SimpleAttributes2GrantedAuthoritiesMapper();
        userRoles2GrantedAuthoritiesMapper.setConvertAttributeToUpperCase(true);

        J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource j2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 
            = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();
        j2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.setMappableRolesRetriever(mappableRolesRetriever);
        j2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.setUserRoles2GrantedAuthoritiesMapper(userRoles2GrantedAuthoritiesMapper);

        J2eePreAuthenticatedProcessingFilter j2eePreAuthenticatedProcessingFilter = new J2eePreAuthenticatedProcessingFilter();     
        j2eePreAuthenticatedProcessingFilter.setAuthenticationManager(am);
        j2eePreAuthenticatedProcessingFilter.setAuthenticationDetailsSource(j2eeBasedPreAuthenticatedWebAuthenticationDetailsSource);

        return j2eePreAuthenticatedProcessingFilter;
    }

   @Bean
   public AuthenticationManager authenticationManager() throws Exception {

      PreAuthenticatedGrantedAuthoritiesUserDetailsService preAuthenticatedGrantedAuthoritiesUserDetailsService = new PreAuthenticatedGrantedAuthoritiesUserDetailsService();;

      PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthenticatedAuthenticationProvider();
      preAuthenticatedAuthenticationProvider.setPreAuthenticatedUserDetailsService(preAuthenticatedGrantedAuthoritiesUserDetailsService);

      List<AuthenticationProvider> lProviders = new LinkedList<AuthenticationProvider>();

      lProviders.add(preAuthenticatedAuthenticationProvider);

      AuthenticationManager am = new ProviderManager(lProviders);

      return am;
   }

    @Override
    public void setResourceLoader(ResourceLoader arg0) {
        this.resourceLoader = arg0;
    }       
}

有什么帮助吗?

与示例略有不同并针对本例进行了简化,如果在组件扫描

上设置了其包,则会创建过滤器 class

web.xml 的以下部分 不需要:

<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.config.SecurityConfig</param-value>
  </context-param>

web.xml 定义将扫描 class 并创建过滤器实例的应用程序上下文。稍后该实例用于 web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/config/application-context-${environment}.xml</param-value>
</context-param>

如果 ${environment} 变量是 "DEV",它使用具有在 XML 中定义的安全性的应用程序上下文,如果 ${environment} 变量是 "PRO",它使用以下应用程序上下文,该上下文具有 Java 中定义的逻辑:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:sec="http://www.springframework.org/schema/security"
       xsi:schemaLocation="
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
          http://www.springframework.org/schema/security 
          http://www.springframework.org/schema/security/spring-security.xsd>
    <context:component-scan base-package="com.config" />
</beans>

过滤器只需要在 PRO 中创建,因此它只需要从 PRO 中使用的应用程序上下文中对其包进行组件扫描。