我试图从ApplicationListener<AuthenticationSuccessEvent>成功登录后实现接口的类(Spring 3.2.2和Spring Security 3.2.0 M1)中调用受保护的方法。这是我以前的问题。
ApplicationListener<AuthenticationSuccessEvent>
该应用程序在以下环境下运行。
我已经将以下与Spring安全性相关的库添加到类路径中。
实现的类ApplicationListener<AuthenticationSuccessEvent>如下。
package loginsuccesshandler; import admin.dao.service.StateService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.stereotype.Service; @Service public final class AuthSuccessHandler implements ApplicationListener<AuthenticationSuccessEvent> { @Autowired private StateService stateService; @Override public void onApplicationEvent(AuthenticationSuccessEvent event) { System.out.println(event.getAuthentication()); System.out.println("rowCount = "+stateService.rowCount()); } }
这可以防止用户使用以下消息使用正确的凭据登录(这只是一个示例。完全不需要对成功认证时的状态数进行计数)。
在SecurityContext中找不到身份验证对象
事件引发。方法中的第一条语句onApplicationEvent()显示以下内容。
org.springframework.security.authentication.UsernamePasswordAuthenticationToken@45264a59: Principal: org.springframework.security.core.userdetails.User@586034f:Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@380f4: RemoteIpAddress: 127.0.0.1; SessionId: 88777A678DC5BB0272F84CA4BC61FAF2; Granted Authorities: ROLE_ADMIN
因此,似乎用户已通过身份验证,并且身份验证对象可用。
我的springSecurity.xml文件如下所示。
springSecurity.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-3.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <http pattern="/utility/Login.jsf*" security="none"/> <debug/> <http auto-config='true' use-expressions="true" disable-url-rewriting="true"> <session-management session-fixation-protection="newSession"> <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" /> </session-management> <intercept-url pattern="/admin_side/**" access="hasRole('ROLE_ADMIN')" requires-channel="any"/> <intercept-url pattern="/utility/Login.jsf" access="permitAll" requires-channel="any"/> <http-basic /> <anonymous /> <form-login login-processing-url="/j_spring_security_check" login-page="/utility/Login.jsf" authentication-success-handler-ref="loginSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler"/> <logout logout-success-url="/utility/Login.jsf" invalidate-session="true" delete-cookies="JSESSIONID"/> </http> <authentication-manager> <authentication-provider> <jdbc-user-service data-source-ref="dataSource" users-by-username-query="select email_id, password, enabled from user_table where lower(email_id)=lower(?)" authorities-by-username-query="select ut.email_id, ur.authority from user_table ut, user_roles ur where ut.user_id=ur.user_id and lower(ut.email_id)=lower(?)"/> </authentication-provider> </authentication-manager> <beans:bean id="loginSuccessHandler" class="loginsuccesshandler.LoginSuccessHandler"/> <beans:bean id="authenticationFailureHandler" class="loginsuccesshandler.AuthenticationFailureHandler" /> <global-method-security secured-annotations="enabled" pre-post-annotations="enabled" proxy-target-class="false"> <protect-pointcut expression="execution(* admin.dao.*.*(..))" access="ROLE_ADMIN"/> </global-method-security> </beans:beans>
当从spring-security.xml文件中省略以下XML行时,Spring安全性工作正常。
spring-security.xml
<global-method-security secured-annotations="enabled" proxy-target-class="false"> <protect-pointcut expression="execution(* admin.dao.*.*(..))" access="ROLE_ADMIN"/> </global-method-security>
可以从实现ApplicationListener<AuthenticationSuccessEvent>接口的类中调用受保护的方法(应用方法安全性)吗?如果是,那么我的情况缺少什么?到目前为止,我已经点击了成千上万的链接,但找不到任何线索。
该application-context.xml文件。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:component-scan base-package="admin.mangedbean loginsuccesshandler" use-default-filters="false"> <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/> <context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/> </context:component-scan> <mvc:annotation-driven/> <context:annotation-config/> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory" > <property name="jpaProperties"> <props> <prop key="hibernate.enable_lazy_load_no_trans">true</prop> </props> </property> <property name="jpaPropertyMap"> <map> <entry key="eclipselink.weaving" value="false"/> </map> </property> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/social_networking"/> </bean> <!--The bean shown in the beginning is configured here--> <bean id="authSuccessHandler" class="loginsuccesshandler.AuthSuccessHandler"/> <bean id="testService" class="admin.dao.TestDAO"/> <bean id="stateService" class="admin.dao.StateDAO"/> <bean id="sharableService" class="admin.dao.SharableDAO"/> </beans>
该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"> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/applicationContext.xml /WEB-INF/spring-security.xml </param-value> </context-param> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <!--<param-value>Development</param-value>--> <param-value>Production</param-value> </context-param> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/log4j.properties</param-value> </context-param> <context-param> <param-name>log4jExposeWebAppRoot</param-name> <param-value>false</param-value> </context-param> <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> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</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> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> <security-constraint> <display-name>Restrict direct access to XHTML files</display-name> <web-resource-collection> <web-resource-name>XHTML files</web-resource-name> <url-pattern>*.xhtml</url-pattern> </web-resource-collection> <auth-constraint /> </security-constraint> <session-config> <session-timeout> 120 </session-timeout> </session-config> <welcome-file-list> <welcome-file>/utility/Login.jsf</welcome-file> </welcome-file-list> <resource-ref> <res-ref-name>jdbc/social_networking</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> </web-app>
当尝试登录但最终失败时,可以在下面看到调试信息。
DEBUG [http-apr-8080-exec-55] (AntPathRequestMatcher.java:116) - Checking match of request : '/j_spring_security_check'; against '/utility/login.jsf*' DEBUG [http-apr-8080-exec-55] (AntPathRequestMatcher.java:116) - Checking match of request : '/j_spring_security_check'; against '/utility/login.jsf*' DEBUG [http-apr-8080-exec-55] (FilterChainProxy.java:337) - /j_spring_security_check at position 1 of 13 in additional filter chain; firing Filter: 'ChannelProcessingFilter' DEBUG [http-apr-8080-exec-55] (AntPathRequestMatcher.java:116) - Checking match of request : '/j_spring_security_check'; against '/admin_side/**' DEBUG [http-apr-8080-exec-55] (AntPathRequestMatcher.java:116) - Checking match of request : '/j_spring_security_check'; against '/utility/login.jsf' DEBUG [http-apr-8080-exec-55] (FilterChainProxy.java:337) - /j_spring_security_check at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' DEBUG [http-apr-8080-exec-55] (HttpSessionSecurityContextRepository.java:139) - HttpSession returned null object for SPRING_SECURITY_CONTEXT DEBUG [http-apr-8080-exec-55] (HttpSessionSecurityContextRepository.java:85) - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@1103da5. A new one will be created. DEBUG [http-apr-8080-exec-55] (FilterChainProxy.java:337) - /j_spring_security_check at position 3 of 13 in additional filter chain; firing Filter: 'ConcurrentSessionFilter' DEBUG [http-apr-8080-exec-55] (FilterChainProxy.java:337) - /j_spring_security_check at position 4 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' DEBUG [http-apr-8080-exec-55] (FilterChainProxy.java:337) - /j_spring_security_check at position 5 of 13 in additional filter chain; firing Filter: 'LogoutFilter' DEBUG [http-apr-8080-exec-55] (FilterChainProxy.java:337) - /j_spring_security_check at position 6 of 13 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' DEBUG [http-apr-8080-exec-55] (AbstractAuthenticationProcessingFilter.java:189) - Request is to process authentication DEBUG [http-apr-8080-exec-55] (ProviderManager.java:152) - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider DEBUG [http-apr-8080-exec-55] (JdbcTemplate.java:637) - Executing prepared SQL query DEBUG [http-apr-8080-exec-55] (JdbcTemplate.java:572) - Executing prepared SQL statement [select email_id, password, enabled from user_table where lower(email_id)=lower(?)] DEBUG [http-apr-8080-exec-55] (DataSourceUtils.java:110) - Fetching JDBC Connection from DataSource DEBUG [http-apr-8080-exec-55] (DataSourceUtils.java:327) - Returning JDBC Connection to DataSource DEBUG [http-apr-8080-exec-55] (JdbcTemplate.java:637) - Executing prepared SQL query DEBUG [http-apr-8080-exec-55] (JdbcTemplate.java:572) - Executing prepared SQL statement [select ut.email_id, ur.authority from user_table ut, user_roles ur where ut.user_id=ur.user_id and lower(ut.email_id)=lower(?)] DEBUG [http-apr-8080-exec-55] (DataSourceUtils.java:110) - Fetching JDBC Connection from DataSource DEBUG [http-apr-8080-exec-55] (DataSourceUtils.java:327) - Returning JDBC Connection to DataSource DEBUG [http-apr-8080-exec-55] (AbstractBeanFactory.java:246) - Returning cached instance of singleton bean 'authSuccessHandler' DEBUG [http-apr-8080-exec-55] (AbstractBeanFactory.java:246) - Returning cached instance of singleton bean 'org.springframework.security.core.session.SessionRegistryImpl#0' DEBUG [http-apr-8080-exec-55] (AbstractFallbackTransactionAttributeSource.java:106) - Adding transactional method 'rowCount' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; '' DEBUG [http-apr-8080-exec-55] (DelegatingMethodSecurityMetadataSource.java:65) - Caching method [CacheKey[admin.dao.StateDAO; public abstract java.lang.Long admin.dao.service.StateService.rowCount()]] with attributes [ROLE_ADMIN] DEBUG [http-apr-8080-exec-55] (AbstractBeanFactory.java:246) - Returning cached instance of singleton bean 'transactionManager' DEBUG [http-apr-8080-exec-55] (AbstractPlatformTransactionManager.java:366) - Creating new transaction with name [admin.dao.StateDAO.rowCount]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; '' DEBUG [http-apr-8080-exec-55] (JpaTransactionManager.java:369) - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl@84ff11] for JPA transaction DEBUG [http-apr-8080-exec-55] (JpaTransactionManager.java:408) - Not exposing JPA transaction [org.hibernate.ejb.EntityManagerImpl@84ff11] as JDBC transaction because JpaDialect [org.springframework.orm.jpa.DefaultJpaDialect@d9dbb8] does not support JDBC Connection retrieval DEBUG [http-apr-8080-exec-55] (AbstractSecurityInterceptor.java:194) - Secure object: ReflectiveMethodInvocation: public abstract java.lang.Long admin.dao.service.StateService.rowCount(); target is of class [admin.dao.StateDAO]; Attributes: [ROLE_ADMIN] DEBUG [http-apr-8080-exec-55] (AbstractBeanFactory.java:246) - Returning cached instance of singleton bean 'authSuccessHandler' DEBUG [http-apr-8080-exec-55] (AbstractBeanFactory.java:246) - Returning cached instance of singleton bean 'org.springframework.security.core.session.SessionRegistryImpl#0' DEBUG [http-apr-8080-exec-55] (AbstractPlatformTransactionManager.java:844) - Initiating transaction rollback DEBUG [http-apr-8080-exec-55] (JpaTransactionManager.java:534) - Rolling back JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@84ff11] DEBUG [http-apr-8080-exec-55] (JpaTransactionManager.java:594) - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@84ff11] after transaction DEBUG [http-apr-8080-exec-55] (EntityManagerFactoryUtils.java:338) - Closing JPA EntityManager DEBUG [http-apr-8080-exec-55] (AbstractAuthenticationProcessingFilter.java:346) - Authentication request failed: org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext DEBUG [http-apr-8080-exec-55] (AbstractAuthenticationProcessingFilter.java:347) - Updated SecurityContextHolder to contain null Authentication DEBUG [http-apr-8080-exec-55] (AbstractAuthenticationProcessingFilter.java:348) - Delegating to authentication failure handler loginsuccesshandler.AuthenticationFailureHandler@14883a3 DEBUG [http-apr-8080-exec-55] (DefaultRedirectStrategy.java:36) - Redirecting to '/SocialNetworking/utility/Login.jsf' DEBUG [http-apr-8080-exec-55] (HttpSessionSecurityContextRepository.java:269) - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. DEBUG [http-apr-8080-exec-55] (SecurityContextPersistenceFilter.java:97) - SecurityContextHolder now cleared, as request processing completed DEBUG [http-apr-8080-exec-49] (AntPathRequestMatcher.java:116) - Checking match of request : '/utility/login.jsf'; against '/utility/login.jsf*' DEBUG [http-apr-8080-exec-49] (AntPathRequestMatcher.java:116) - Checking match of request : '/utility/login.jsf'; against '/utility/login.jsf*' DEBUG [http-apr-8080-exec-49] (FilterChainProxy.java:180) - /utility/Login.jsf has an empty filter list
最后一件事情:
当我放弃该bean并从application-context.xml文件中注销时,登录成功,但是可以在服务器控制台上看到以下信息。
DEBUG [http-apr-8080-exec-165] (HttpSessionSecurityContextRepository.java:139) - HttpSession returned null object for SPRING_SECURITY_CONTEXT DEBUG [http-apr-8080-exec-165] (HttpSessionSecurityContextRepository.java:85) - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@b910c1. A new one will be created.
安全性的授权检查部分从中获取已认证的对象SecurityContext,当请求通过spring安全性过滤器时将进行设置。我在这里的假设是,登录后不久就没有设置它。你可能可以使用下面提供的hack来设置值。
SecurityContext
try { SecurityContext ctx = SecurityContextHolder.createEmptyContext(); SecurityContextHolder.setContext(ctx); ctx.setAuthentication(event.getAuthentication()); //Do what ever you want to do } finally { SecurityContextHolder.clearContext(); }