【问题标题】:AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContextAuthenticationCredentialsNotFoundException:在 SecurityContext 中找不到 Authentication 对象
【发布时间】:2016-03-11 08:46:00
【问题描述】:

我在我的服务器上实现了这个登录功能,基本上遵循this post

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

    LOGGER.debug("Login for " + userId);

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

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

    try {
        auth = this.authenticationProvider.authenticate(auth);
    } catch(BadCredentialsException e) {
        LOGGER.debug("Bad credentials ..");
        throw new RuntimeException(e.getMessage());
    }

    LOGGER.debug("User successfully authenticated [userId="+userId+"]");

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

    SecurityContextHolder.setContext(sc);       
}

到目前为止,这似乎没有任何问题。但是,如果我在登录后立即执行此请求

@PreAuthorize("hasPermission('ADMIN')")
public void test(String msg) {
    LOGGER.debug("test: " + msg);
}

结果是这里的异常:

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:653)
    at com.mz.server.web.service.LoginService$$EnhancerBySpringCGLIB$$caf0e62d_2.test(<generated>)
    at com.mz.server.web.servlet.LoginServletImpl.test(LoginServletImpl.java:99)

阅读SecurityContextHolder.setContext() 上面写着:

将新的SecurityContext 与当前执行线程相关联。

所以我想我的问题是我不明白 Spring 是如何召回刚刚登录到我的系统并且现在想要继续进行身份验证的请求的用户。

为什么我不只是被重定向到/login


春天上下文:

<!-- //////////////////////////////////////////////////////////////////////////////// -->
<!-- // BEGIN Spring Security -->

<sec:http 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.mz.server.web.auth.CustomAuthenticationListener"/>

<bean id="authenticationProvider" class="com.mz.server.web.auth.CustomAuthenticationProvider"/>

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

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

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

<!-- // END Spring Security -->
<!-- //////////////////////////////////////////////////////////////////////////////// -->

web.xml

<!-- //////////////////////////////////////////////////////////////////////////////// -->
<!-- // BEGIN Filters -->

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

<!-- // END FILTERS -->
<!-- //////////////////////////////////////////////////////////////////////////////// -->

【问题讨论】:

    标签: spring gwt spring-security


    【解决方案1】:

    我知道你的痛苦,我花了很长时间弄清楚 Spring Security。真的在争论是否值得。但是您可能需要添加 SecurityContextPersistenceFilter。这通常会使用 JSessionID 中的 HttpSession 自动将您的凭据添加到 SecurityContext。我有一个自定义身份验证处理程序,因此我的代码中有一些不相关的额外部分,但我认为这可能会让您指向正确的方向。

    <?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:sec="http://www.springframework.org/schema/security"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
    
    
    <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="/rest/**"
                    filters="securityContextPersistenceFilter" />
            </list>
        </constructor-arg>
    </bean>
    
    <sec:global-method-security
        authentication-manager-ref="authenticationManager"
        secured-annotations="enabled" />
    

    【讨论】:

    • 您可以尝试使用@Secured。我将尝试使用 (@)PreAuthorize 来查看它是否适合我。从文档 hasPermission 看起来它需要两个参数。 (@)PreAuthorize("hasPermission(#contact, 'admin')") 。 docs.spring.io/spring-security/site/docs/3.0.x/reference/… 。你也可以检查 hasRole。我认为这对你也有用。
    • 知道了!你的回答让我在这里走上正轨。我还使用了this tutorial,它向我展示了如何实现权限评估部分。非常感谢! :)
    • 快速更新,一切正常后。由于性能开销,我删除了所有弹簧。我转而使用 Guice 进行 DI 和 Shiro 以确保安全。我发现编写自定义身份验证处理程序和启用基于注释的安全性非常容易。
    • Spring Security 值得。最好自己写。至少你会知道它在做什么。我再也不会选择在另一个 Spring 应用程序上工作了。
    猜你喜欢
    • 2013-05-09
    • 2013-05-18
    • 2022-01-22
    • 2013-03-08
    • 2014-04-04
    • 2016-11-12
    • 1970-01-01
    • 2019-10-28
    • 2013-09-20
    相关资源
    最近更新 更多