成功登录后:AuthenticationCredentialsNotFoundException:在SecurityContext中找不到Authentication对象

时间:2016-01-12 18:38:38

标签: spring spring-security

我有一个奇怪的问题,在我登录后,我可以发出一个请求,之后每个后续请求都会抛出一个Spring AuthenticationCredentialsNotFoundException。我不知道为什么会这样。

在我的LoginService中,我使用我的注入AdminAuthenticationProvider将用户登录到:

public class LoginService {

    private AuthenticationProvider adminAuthenticationProvider;     

    public LoginService(DSLContext ctx, AuthenticationProvider adminAuthenticationProvider) {       
        this.adminAuthenticationProvider = adminAuthenticationProvider;
    }

    @Transactional
    public void login(String userId, String password) {

        CustomUserDetails user = new CustomUserDetails(userId, password, true, true, true, true, new ArrayList<GrantedAuthority>());

        Authentication auth = new UsernamePasswordAuthenticationToken(user, password,
                new ArrayList<GrantedAuthority>());     

        try {
            auth = this.adminAuthenticationProvider.authenticate(auth);
        } catch(BadCredentialsException e) {
            throw e;
        }

        SecurityContext sc = new SecurityContextImpl();
        sc.setAuthentication(auth);

        SecurityContextHolder.setContext(sc);       
    }
}

AdminAuthenticationProvider实施:

public class AdminAuthenticationProvider implements AuthenticationProvider {

    private RestaurantAdminRepository restaurantAdminRepository;

    public AdminAuthenticationProvider(DSLContext ctx) {
        this.restaurantAdminRepository = new RestaurantAdminRepository(ctx);
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        CustomUserDetails user = (CustomUserDetails) authentication.getPrincipal();

        List<String> roles = new ArrayList<>();
        roles.add("ROLE_ADMIN");

        Authentication customAuthentication = new CustomUserAuthentication(roles, authentication);
        customAuthentication.setAuthenticated(true);

        return customAuthentication;
    }

    @Override
    public boolean supports(Class<? extends Object> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

如您所见,这里没有真正检查过,用户只是登录了。 但是,之后我总是得到例外。目前我只使用isAuthenticated()

@PreAuthorize("isAuthenticated()")
public List<StoreDTO> getAvailableStores() {        
    // ..
    return result;
}

这可能是什么问题?

这是我的配置 applicationContext-spring-acl.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:security="http://www.springframework.org/schema/security"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:jdbc="http://www.springframework.org/schema/jdbc"

    xmlns:sec="http://www.springframework.org/schema/security"

    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/security 
        http://www.springframework.org/schema/security/spring-security-4.0.xsd
        http://www.springframework.org/schema/jdbc 
        http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd">

    <!-- Imports -->
    <import resource="applicationContext-jooq.xml"/>

    <!-- See 15.3.2 Built-In Expression @http://static.springsource.org/spring-security/site/docs/3.0.x/reference/el-access.html#el-permission-evaluator -->
    <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <!-- To use hasPermission() in expressions, configure a PermissionEvaluator -->
        <property name="permissionEvaluator" ref="permissionEvaluator" />
        <property name="roleHierarchy" ref="roleHierarchy" />
    </bean>

    <bean class="com.mahlzeit.server.web.auth.permission.CustomAclPermissionEvaluator" id="permissionEvaluator">
        <constructor-arg ref="aclService" />
    </bean>

    <!-- Declare an acl service -->
    <bean class="org.springframework.security.acls.jdbc.JdbcMutableAclService"  id="aclService">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="lookupStrategy" />
        <constructor-arg ref="aclCache" />
    </bean>

    <!-- Declare a lookup strategy -->
    <bean id="lookupStrategy"
        class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="aclCache" />
        <constructor-arg ref="aclAuthorizationStrategy" />
        <constructor-arg ref="auditLogger" />
    </bean>

    <!-- Declare an acl cache -->
    <bean id="aclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
        <constructor-arg>
            <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
                <property name="cacheManager">
                    <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:shared="true"/>
                </property>
                <property name="cacheName" value="aclCache" />
            </bean>
        </constructor-arg>
        <constructor-arg>
            <bean
                class="org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy">
                <constructor-arg>
                    <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger" />
                </constructor-arg>
            </bean>
        </constructor-arg>
        <constructor-arg>
            <bean
                class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
                <constructor-arg>
                    <list>
                        <bean
                            class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                            <constructor-arg value="ROLE_ACL_ADMIN" />
                        </bean>
                    </list>
                </constructor-arg>
            </bean>
        </constructor-arg>      
    </bean>

    <!-- Declare an acl authorization strategy -->
    <bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
        <constructor-arg>
            <list>
                <bean
                    class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="ROLE_ADMIN" />
                </bean>
                <bean
                    class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="ROLE_ADMIN" />
                </bean>
                <bean
                    class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="ROLE_ADMIN" />
                </bean>
            </list>
        </constructor-arg>
    </bean>

    <!-- Declare an audit logger -->
    <bean id="auditLogger"
        class="org.springframework.security.acls.domain.ConsoleAuditLogger" />

    <!-- http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.html -->
    <bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
        <property name="hierarchy">
            <value>
                ROLE_ADMIN > ROLE_USER
                ROLE_USER > ROLE_VISITOR
            </value>
        </property>
    </bean>

    <sec:global-method-security authentication-manager-ref="authenticationManager" pre-post-annotations="enabled">   
        <sec:expression-handler ref="expressionHandler"/>       
    </sec:global-method-security>
</beans>

这是 applicationContext-spring-security.xml

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

    xmlns:sec="http://www.springframework.org/schema/security"

    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.0.xsd"    
    >

    <!-- Imports -->
    <import resource="applicationContext-spring-acl.xml"/>

    <sec:http pattern="/**" auto-config="true" use-expressions="true"/>

    <bean id="httpSessionSecurityContextRepository" class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
        <property name='allowSessionCreation' value='false' />
    </bean>

    <bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
        <constructor-arg ref="httpSessionSecurityContextRepository" />
    </bean>

    <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
        <constructor-arg>
            <list>
                <sec:filter-chain pattern="/**" filters="securityContextPersistenceFilter" />
            </list>
        </constructor-arg>
    </bean>

    <bean id="authenticationListener" class="com.mahlzeit.server.web.auth.CustomAuthenticationListener"/>

    <bean id="adminAuthenticationProvider" class="com.mahlzeit.server.web.auth.AdminAuthenticationProvider">
        <constructor-arg ref="dslContext" />
    </bean>

    <bean id="userDetailsService" class="com.mahlzeit.server.web.service.CustomUserDetailsService"/>

    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider ref="adminAuthenticationProvider"/>
    </sec:authentication-manager>

</beans>
[http-bio-8080-exec-3] DEBUG com.mz.server.web.servlet.StoreServletImpl - Requested available stores.
[http-bio-8080-exec-3] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List com.mz.server.web.service.StoreService.getAvailableStores(); target is of class [com.mz.server.web.service.StoreService]; Attributes: [[authorize: 'isAuthenticated()', filter: 'null', filterTarget: 'null']]
[http-bio-8080-exec-3] TRACE org.springframework.context.support.ClassPathXmlApplicationContext - Publishing event in org.springframework.context.support.ClassPathXmlApplicationContext@19c3e3e: org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent[source=ReflectiveMethodInvocation: public java.util.List com.mz.server.web.service.RestaurantService.getAvailableStores(); target is of class [com.mz.server.web.service.StoreService]]
Caused by: org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:378)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:222)
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
    at com.mahlzeit.server.web.service.RestaurantService$$EnhancerBySpringCGLIB$$9012bad4_3.getAvailableRestaurants(<generated>)
    at com.mahlzeit.server.web.servlet.RestaurantServletImpl.getAvailableRestaurants(RestaurantServletImpl.java:49)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:587)
    ... 25 more

2 个答案:

答案 0 :(得分:1)

更改这些行:

SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(auth);
SecurityContextHolder.setContext(sc);

为:

SecurityContextHolder.getContext().setAuthentication(auth);

如果HttpSessionSecurityContextRepository用法,您希望将此auth对象绑定到整个会话not only to the request

答案 1 :(得分:0)

我不知道您的LoginService如何与Spring Security集成。我会考虑自定义用户详细信息服务:

public class CustomUserDetailsService extends JdbcUserDetailsManager implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

然后将其集成到您的SecurityConfig中:

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    ....
    auth.userDetailsService(customUserDetailsService()).passwordEncoder(passwordEncoder);
    auth.authenticationProvider(authenticationProvider);
}