简体   繁体   English

参考Spring Spring内的安全配置 3.1 Java Config

[英]Referencing Spring Security configuration within Spring 3.1 Java Config

I recently switched a majority of my Spring configuration to use the code based config in Spring 3.1.我最近将大部分 Spring 配置切换为使用 Spring 3.1 中基于代码的配置。 However, now that I've switched, my Spring Security is not working correctly and throws the following error on Tomcat startup:但是,现在我已经切换了,我的 Spring 安全性无法正常工作,并在 Tomcat 启动时抛出以下错误:

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

I still have the Spring Security in an XML file, and know this can't be converted to Java config in Spring since it is a custom namespace, however, I am referencing it within my Java config file.我仍然在 XML 文件中有 Spring Security,并且知道这不能转换为 Spring 中的 Java 配置,因为它是一个自定义命名空间,但是,我在我的 Java 配置文件中引用它。 I also tried moving the applicationContext-security.xml configuration reference from the Java config to my web.xml without any luck.我还尝试将 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: 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 : 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>

Do not use security namespace shortcuts and migrate all spring configuration from XML to Java. It will make fine tuning of your security much easier.不要使用security命名空间快捷方式并将所有 spring 配置从 XML 迁移到 Java。这将使您的安全性微调更加容易。 I'm going to do it for our project soon after migrating to 3.1.迁移到 3.1 后,我将很快为我们的项目做这件事。 You can find non-trivial almost plain-bean security XML config example here .您可以在此处找到重要的几乎纯 bean 安全性 XML 配置示例。

edit: Finished config (linked above) migration.编辑:完成配置(上面链接)迁移。 All config was put into one method deliberately to make it shorter and to demonstrate, that you don't need separate spring bean for every filter.故意将所有配置放入一个方法中以使其更短并证明您不需要为每个过滤器单独的 spring bean。 Of course it's better to move complex init parts to separate methods (marked @Bean if necessary).当然,最好将复杂的 init 部分移动到单独的方法中(必要时标记为@Bean )。 You can find working example in the X509AnnotationTest.Config on the link above.您可以在上面链接的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);
}

For those still looking for a way to use SpringSecurity XML configuration with a Java config web application.对于那些仍在寻找一种方法来使用 SpringSecurity XML 配置和 Java 配置 web 应用程序的人。 I got this working using Spring 3.2.0.RELEASE and SpringSecurity 3.2.0.M1.我使用 Spring 3.2.0.RELEASE 和 SpringSecurity 3.2.0.M1 让这个工作正常。 Here's the important parts of the solution这是解决方案的重要部分

WebAppConfig.java 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 spring-security.xml
Note that I had to remove the default xmlns="..." from the <beans:beans> definition.请注意,我必须从 <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 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>

Thank you everyone for the above hints concerning a springSecurityFilterChain when Java configuration of Spring is used.感谢大家在使用 Spring 的 Java 配置时提供有关 springSecurityFilterChain 的上述提示。 As I've worked along to apply these hints to fix a comparable error in my own code, I noted that the Spring documentation for org.springframework.web.WebApplicationInitializer (the interface implemented by AbstractAnnotationConfigDispatcherServletInitializer) has the following relevant "Caveat":当我一直在努力应用这些提示来修复我自己的代码中的类似错误时,我注意到 org.springframework.web.WebApplicationInitializer(由 AbstractAnnotationConfigDispatcherServletInitializer 实现的接口)的 Spring 文档有以下相关的“警告”:

web.xml versioning web.xml 版本控制

WEB-INF/web.xml and WebApplicationInitializer use are not mutually exclusive; WEB-INF/web.xml和WebApplicationInitializer的使用并不互斥; for example, web.xml can register one servlet, and a WebApplicationInitializer can register another.例如,web.xml 可以注册一个 servlet,WebApplicationInitializer 可以注册另一个。 An initializer can even modify registrations performed in web.xml through methods such as ServletContext.getServletRegistration(String).初始化程序甚至可以通过 ServletContext.getServletRegistration(String) 等方法修改在 web.xml 中执行的注册。

However, if WEB-INF/web.xml is present in the application, its version attribute must be set to "3.0" or greater, otherwise ServletContainerInitializer bootstrapping will be ignored by the servlet container.但是,如果应用程序中存在 WEB-INF/web.xml,则其版本属性必须设置为“3.0”或更高版本,否则 servlet 容器将忽略 ServletContainerInitializer 引导。

I point this out because I noticed that your web.xml version noted above is still 2.5.我指出这一点是因为我注意到您上面提到的 web.xml 版本仍然是 2.5。

I got it working with Spring 3.2.3 and Servlet 3.0.我让它与 Spring 3.2.3 和 Servlet 3.0 一起使用。 The tick is to extend DelegatingFilterProxy class and named it SpringSecurityFilterChain. tick是对DelegatingFilterProxy class进行扩展,命名为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 {

}

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM