我最近将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>
答案 0 :(得分:8)
不要使用security
命名空间快捷方式,并将所有spring配置从XML迁移到Java。它将使您的安全性更加轻松。迁移到3.1后,我很快就会为我们的项目做这件事。
您可以找到非平凡的几乎普通的bean安全性XML配置示例here。
修改强>
完成配置(上面链接)迁移。故意将所有配置放入一个方法中以使其更短并且演示,每个过滤器不需要单独的spring bean。当然,最好将复杂的init部分移动到单独的方法(必要时标记为@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);
}
答案 1 :(得分: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
}
弹簧的security.xml
请注意,我必须从&lt; beans:beans&gt;中删除默认的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>
答案 2 :(得分:1)
感谢大家在使用Spring的Java配置时有关springSecurityFilterChain的上述提示。由于我已经努力应用这些提示来修复我自己的代码中的类似错误,我注意到org.springframework.web.WebApplicationInitializer的Spring文档(由AbstractAnnotationConfigDispatcherServletInitializer实现的接口)具有以下相关的“警告”: / p>
web.xml版本控制
WEB-INF / web.xml与WebApplicationInitializer的使用并不相互排斥;例如,web.xml可以注册一个servlet,WebApplicationInitializer可以注册另一个servlet。初始化程序甚至可以通过ServletContext.getServletRegistration(String)等方法修改web.xml中执行的注册。
但是,如果应用程序中存在WEB-INF / web.xml,则其version属性必须设置为“3.0”或更高,否则Servlet容器将忽略ServletContainerInitializer引导。 / strong>
我指出这一点是因为我注意到上面提到的web.xml版本仍然是2.5。
答案 3 :(得分: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 {
}
}