it-swarm.com.de

Im SecurityContext - Spring 3.2.2 wurde kein Authentifizierungsobjekt gefunden

Ich versuche, eine geschützte Methode aus einer Klasse aufzurufen, die die ApplicationListener<AuthenticationSuccessEvent>-Schnittstelle bei erfolgreicher Anmeldung implementiert (Spring 3.2.2 und Spring Security 3.2.0 M1). Dies ist meine vorige Frage.

Die Anwendung wird in der folgenden Umgebung ausgeführt.

  • Frühling 3.2.2
  • Spring Security 3.2.0
  • JPA 2.0
  • JSF 2.1.9
  • MySQL 5.6.11
  • JDK-7u11
  • NetBeans 7.2.1

Ich habe dem Klassenpfad die folgenden Bibliotheken hinzugefügt, die sich auf die Spring-Sicherheit beziehen.

  • spring-security-core-3.2.0.M1.jar
  • spring-security-config-3.2.0.M1.jar
  • spring-security-web-3.2.0.M1.jar

Die Klasse, die ApplicationListener<AuthenticationSuccessEvent> implementiert, lautet wie folgt.

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());
    }
}

Dies verhindert, dass ein Benutzer auch mit den richtigen Anmeldeinformationen mit der folgenden Meldung angemeldet wird (dies ist nur ein Beispiel. Es ist nicht erforderlich, die Anzahl der Status bei erfolgreicher Authentifizierung zu zählen).

Im SecurityContext wurde kein Authentifizierungsobjekt gefunden


Die Veranstaltung wird ausgelöst. Die erste Anweisung in der onApplicationEvent()-Methode zeigt Folgendes an.

org.springframew[email protected]45264a59: Principal: [email protected]:Username: admin; 
Password: [PROTECTED]; 
Enabled: true; 
AccountNonExpired: true; 
credentialsNonExpired: true; 
AccountNonLocked: true; 
Granted Authorities: ROLE_ADMIN; 
Credentials: [PROTECTED]; 
Authenticated: true; 
Details: org.sprin[email protected]380f4: RemoteIpAddress: 127.0.0.1; 
SessionId: 88777A678DC5BB0272F84CA4BC61FAF2;
Granted Authorities: ROLE_ADMIN

Es scheint also, dass der Benutzer authentifiziert ist und das Authentifizierungsobjekt verfügbar ist.


Meine springSecurity.xml-Datei sieht einfach wie folgt aus.

<?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>

Die Spring-Sicherheit funktioniert einwandfrei, wenn die folgenden XML-Zeilen aus der spring-security.xml-Datei weggelassen werden.

<global-method-security secured-annotations="enabled" proxy-target-class="false">
     <protect-pointcut expression="execution(* admin.dao.*.*(..))" access="ROLE_ADMIN"/>
</global-method-security>

Kann eine geschützte Methode (mit angewandter Methodensicherheit) von einer Klasse aufgerufen werden, die die ApplicationListener<AuthenticationSuccessEvent>-Schnittstelle implementiert? Wenn ja, was fehlt in meinem Fall? Ich habe bisher Tausende von Links angeklickt, konnte aber keinen einzigen Hinweis finden.


Die application-context.xml-Datei.

<?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>

Die web.xml-Datei.

<?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>

Die Debug-Informationen sind unten zu sehen, wenn versucht wird, sich anzumelden, was letztendlich fehlschlägt.

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: [email protected] 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 [[email protected]] for JPA transaction
DEBUG [http-apr-8080-exec-55] (JpaTransactionManager.Java:408) - Not exposing JPA transaction [[email protected]] as JDBC transaction because JpaDialect [[email protected]] 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 [[email protected]]
DEBUG [http-apr-8080-exec-55] (JpaTransactionManager.Java:594) - Closing JPA EntityManager [[email protected]] 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 [email protected]
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

Das letzte Ding:

Wenn ich diese Bean aufhöre und die Registrierung der application-context.xml-Datei aufhöre, wird die Anmeldung erfolgreich durchgeführt. Die folgenden Informationen sind jedoch auf der Serverkonsole zu sehen.

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: [email protected] A new one will be created.
33
Tiny

Der Berechtigungsprüfungsabschnitt der Sicherheit erhält das authentifizierte Objekt von SecurityContext, das gesetzt wird, wenn eine Anforderung den Federsicherheitsfilter durchläuft. Meine Vermutung hier ist, dass dies kurz nach dem Login nicht gesetzt wird. Sie können wahrscheinlich einen Hack wie unten angegeben verwenden, um den Wert festzulegen.

try {
    SecurityContext ctx = SecurityContextHolder.createEmptyContext();
    SecurityContextHolder.setContext(ctx);
    ctx.setAuthentication(event.getAuthentication());

    //Do what ever you want to do

} finally {
    SecurityContextHolder.clearContext();
}

Aktualisieren:

Sie können sich auch das InteractiveAuthenticationSuccessEvent anschauen, das aufgerufen wird, wenn SecurityContext gesetzt ist.

25
Arun P Johny

Dies kann auch passieren, wenn Sie einen @PreAuthorize oder @PostAuthorize in ein Bean bei der Erstellung eingeben. Ich würde empfehlen, solche Anmerkungen auf interessierende Methoden zu verschieben.

5
EliuX

Wie bereits von @Arun P Johny angedeutet, besteht die Hauptursache des Problems darin, dass AuthenticationSuccessEvent zum Zeitpunkt der Verarbeitung von SecurityContextHolder nicht mit dem Authentifizierungsobjekt gefüllt wird. Daher werden deklarative Berechtigungsprüfungen (die Benutzerrechte von SecurityContextHolder erhalten müssen) nicht funktionieren. Ich gebe Ihnen eine andere Idee, wie Sie dieses Problem lösen können. Es gibt zwei Möglichkeiten, wie Sie Ihren benutzerdefinierten Code nach erfolgreicher Authentifizierung sofort ausführen können:

  1. AuthenticationSuccessEvent hören
  2. Stellen Sie Ihre benutzerdefinierte AuthenticationSuccessHandler-Implementierung bereit.

AuthenticationSuccessHandler hat gegenüber dem ersten Weg einen wichtigen Vorteil: SecurityContextHolder wird bereits ausgefüllt. Verschieben Sie einfach Ihren stateService.rowCount()-Aufruf in die loginsuccesshandler.LoginSuccessHandler#onAuthenticationSuccess(...)-Methode, und das Problem wird behoben.

3
Maksym Demidas

Es gibt ein ähnliches Problem. Ich habe den hier angegebenen Hörer hinzugefügt

https://stackoverflow.com/questions/3145936/spring-security-j-spring-security-logout-problem

Es hat für mich funktioniert, indem ich unten in web.xml Zeilen hinzufügte ... Wenn ich es sehr spät poste, sollte es jemandem helfen, der nach einer Antwort sucht.

<listener>
    <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
3
Mallik

Für mich war das Problem ein ContextRefreshedEvent-Handler. Ich habe eine Dateninitialisierung durchgeführt, aber zu diesem Zeitpunkt in der Anwendung war die Authentifizierung noch nicht festgelegt. Es war ein Haken 22, da das System eine Authentifizierung zur Autorisierung benötigte und es eine Autorisierung benötigte, um die Authentifizierungsdetails abzurufen :). Am Ende habe ich die Autorisierung von einer Klassenebene auf eine Methodenebene gelockert.

1
Miguel Pereira