环境:Tomcat 7.0.64,JSF 2.2.12,Spring框架4.3.3,Spring Security 4.1.3,Hibernate 5.2.2。
我正在尝试将security.xml
从我们的其他应用程序转换为Java Config以在我的项目中使用,但它仍然存在此错误:
找不到org.springframework.security.authentication.UsernamePasswordAuthenticationToken
的AuthenticationProvider
来自SPRING_SECURITY_LAST_EXCEPTION
。当应用程序重定向到SPRING_SECURITY_LAST_EXCEPTION
时,我有条件地显示login.xhtml?error=true
,否则,我在任何日志中都没有看到任何错误。
我们的Tomcat有一个处理身份验证的自定义领域。从我在doc中读到的内容,我需要使用J2EE过滤器来使用基于容器的身份验证。使用XML配置的另一个应用程序没有问题,但我不能让Java配置为我工作。
在调试期间,我可以看到authenticationProvider
设置为auth.authenticationProvider
,但auth.parentAuthenticationManager
为null
。我不确定这是否是问题的原因,但它应该是null
吗?我应该在哪里设置断点来调试这个没有身份验证提供程序的问题?
的web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">
<display-name>App1</display-name>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<persistence-unit-ref>
<persistence-unit-ref-name>jdbc/public_PostgreSQL</persistence-unit-ref-name>
<persistence-unit-name>AppPU</persistence-unit-name>
</persistence-unit-ref>
<resource-ref>
<res-ref-name>jdbc/public_PostgreSQL</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>login.xhtml</welcome-file>
</welcome-file-list>
<!-- =================================================================== -->
<!-- JSF Params -->
<!-- =================================================================== -->
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>
<!-- Use JSF view templates saved as *.xhtml, for use with Facelets -->
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
<!-- =================================================================== -->
<!-- Servlets -->
<!-- =================================================================== -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- =================================================================== -->
<!-- Servlet Mappings -->
<!-- =================================================================== -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.xhtml</form-login-page>
<form-error-page>/login.xhtml?error=true</form-error-page>
</form-login-config>
</login-config>
<!-- =================================================================== -->
<!-- Spring/Security Params -->
<!-- =================================================================== -->
<!-- Context Configuration locations for Spring XML files -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContext-resources.xml
classpath:/applicationContext-service.xml
</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<security-constraint>
<web-resource-collection>
<web-resource-name>Application</web-resource-name>
<url-pattern>/faces/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<!-- =================================================================== -->
<!-- Listeners -->
<!-- =================================================================== -->
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
</web-app>
要转换为Java Config的security.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.1.xsd">
<beans:bean id="entryPoint"
class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint">
</beans:bean>
<http auto-config="false" use-expressions="true" entry-point-ref="entryPoint" create-session="stateless" disable-url-rewriting="true" >
<form-login login-page="/login.xhtml" login-processing-url="/login" authentication-failure-url="/login.xhtml?error=true"/>
<anonymous />
<logout logout-success-url="/login.xhtml?logout=true"/>
<custom-filter position="PRE_AUTH_FILTER" ref="j2eeFilter" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref='preauthAuthProvider'/>
</authentication-manager>
<beans:bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
<beans:bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService"
ref="userDetailsServiceWrapper" />
<beans:property name="throwExceptionWhenTokenRejected"
value="true"/>
</beans:bean>
<beans:bean id="j2eeFilter" class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
<beans:property name="authenticationManager"
ref="authenticationManager" />
</beans:bean>
<beans:bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:constructor-arg ref="userDao"/>
</beans:bean>
<beans:bean id="authenticatedVoter" class="org.springframework.security.access.vote.AuthenticatedVoter">
</beans:bean>
<global-method-security pre-post-annotations="enabled" />
SecurityWebInitializer.java
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
// optionally override methods
// This method is needed.
// We need to pass in WebSecurityConfig.class since we are not using Spring MVC
public SecurityWebInitializer() {
super(WebSecurityConfig.class);
}
}
WebSecurityConfig.java
@Configuration
@EnableWebSecurity
@ImportResource({"classpath:applicationContext-service.xml", "classpath:applicationContext-resources.xml"})
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan(basePackages = "com.test.app1")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// Spring Security default header:
// Cache-Control: no-cache, no-store, max-age=0, must-revalidate
// Pragma: no-cache
// Expires: 0
// X-Content-Type-Options: nosniff
// Strict-Transport-Security: max-age=31536000 ; includeSubDomains
// X-Frame-Options: DENY
// X-XSS-Protection: 1; mode=block
http
.csrf().disable() // crsf is enabled by default
.headers()
.disable();
http
.addFilterAfter(j2eePreAuthenticatedProcessingFilter(), J2eePreAuthenticatedProcessingFilter.class)
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint());
http
.authorizeRequests()
.antMatchers("/login*", "/javax.faces.resource/**", "/resources/**", "*.css", "*.png", "*.gif", "*.js").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login")
.loginPage("/login.xhtml")
.defaultSuccessUrl("/index.xhtml")
.successHandler(new AuthenticationSuccessHandlerImpl())
.failureUrl("/login.xhtml?error=true")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login.xhtml?logout=true")
.clearAuthentication(true)
.invalidateHttpSession(true)
.permitAll();
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(preauthAuthProvider());
}
@Bean
public Http403ForbiddenEntryPoint authenticationEntryPoint() {
return new Http403ForbiddenEntryPoint();
}
@Bean
public PreAuthenticatedAuthenticationProvider preauthAuthProvider() throws Exception {
PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthenticatedAuthenticationProvider();
preAuthenticatedAuthenticationProvider.setPreAuthenticatedUserDetailsService(userDetailsServiceWrapper());
preAuthenticatedAuthenticationProvider.setThrowExceptionWhenTokenRejected(true);
return preAuthenticatedAuthenticationProvider;
}
@Autowired
@Bean
public UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> userDetailsServiceWrapper() throws Exception {
UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> userDetailsByNameServiceWrapper = new UserDetailsByNameServiceWrapper<>();
userDetailsByNameServiceWrapper.setUserDetailsService(new UserDaoHibernate());
return userDetailsByNameServiceWrapper;
}
@Autowired
@Bean
UserDetailsService userSecurityService() {
return new UserDetailsServiceImpl();
}
@Bean(name = "authenticationManager")
@Override
public AuthenticationManager authenticationManager() throws Exception {
List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>();
providers.add(preauthAuthProvider());
return new ProviderManager(providers);
}
@Bean
public J2eePreAuthenticatedProcessingFilter j2eePreAuthenticatedProcessingFilter() throws Exception {
J2eePreAuthenticatedProcessingFilter filter = new J2eePreAuthenticatedProcessingFilter();
filter.setAuthenticationManager(authenticationManager());
filter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
return filter;
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public AuthenticatedVoter authenticatedVoter() {
return new AuthenticatedVoter();
}
}