Spring Security:身份验证返回 null

发布于 2024-07-29 02:31:04 字数 820 浏览 5 评论 0原文

我按如下方式配置了一个 Spring bean 以返回 SecurityContext:

<bean id="securityContext" class="org.springframework.security.context.SecurityContextHolder"
    factory-method="getContext">
</bean>  

当我使用此 bean 时,Authentication 对象返回 null。

Authentication authentication = securityContext.getAuthentication();
GrantedAuthority[] authorities = authentication.getAuthorities();

上面的第二行导致 NPE。 这对我来说似乎很奇怪,因为以下代码按预期返回权限:

GrantedAuthority[] authorities =   
SecurityContextHolder.getContext().getAuthentication().getAuthorities();

基本上我试图消除对 SecurityContextHolder.getContext() 的静态调用以使我的代码更易于测试。

关于如何解决这个问题有什么想法吗? 为什么 Spring 返回的 SecurityContext 无法返回权限,而我自己的代码中的静态调用却可以?

仅供参考,我正在 Struts 2 Action 中执行代码。

I have configured a Spring bean as follows to return a SecurityContext:

<bean id="securityContext" class="org.springframework.security.context.SecurityContextHolder"
    factory-method="getContext">
</bean>  

When I use this bean the Authentication object returns null.

Authentication authentication = securityContext.getAuthentication();
GrantedAuthority[] authorities = authentication.getAuthorities();

The second line above causes an NPE. Which seems odd to me, as the following code returns the authorities as expected:

GrantedAuthority[] authorities =   
SecurityContextHolder.getContext().getAuthentication().getAuthorities();

Basically I'm trying to eliminate the static call to SecurityContextHolder.getContext() to make my code more testable.

Any thoughts on how to remedy this? Why is the SecurityContext returned by Spring not able to return the authorities while a static call from within my own code can?

FYI I am executing the code from within a Struts 2 Action.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清风挽心 2024-08-05 02:31:04

SecurityContextHolder.getContext() 返回与当前线程关联的上下文。 在 bean 实例化中,存储在 bean 中的上下文与应用程序运行时所需的上下文不同。 我认为不可能将上下文存储在 bean 中并一直使用它。

SecurityContextHolder.getContext() returns the context associated with the current thread. In bean instantiation the context stored in your bean is different than the context you need when your application is running. I don't think that it is possible to store the context in a bean and use this all the time.

梦中的蝴蝶 2024-08-05 02:31:04

您可以使用静态方法使代码可测试。 您只需要创建自己的 org.springframework.security.Authentication 实现,

因此在您的 JUnit 测试中...

//Assuming you've loaded the user, create your stub
Authentication authentication = new TestAuthentication(userDetails);
//Update the context
SecurityContextHolder.getContext().setAuthentication(authentication);

在上面的示例中,“userDetails”是实现“UserDetails”的类,并且通常包装您的域 User 对象。

我的 TestAuthentication 类 - 希望这有帮助

public class TestAuthentication implements Authentication {

    private UserDetails userDetails;
    private boolean authentication = true;

    public TestAuthentication(UserDetails userDetails){
        NullArgumentException.assertNotNull(userDetails, "userDetails");
        this.userDetails = userDetails;
    }

    public TestAuthentication(UserDetails userDetails, boolean authentication){
        NullArgumentException.assertNotNull(userDetails, "userDetails");
        this.userDetails = userDetails;
        this.authentication = authentication;
    }


    public GrantedAuthority[] getAuthorities() {
        return userDetails.getAuthorities();
    }


    public Object getCredentials() {
        return null;
    }


    public Object getDetails() {
        return null;
    }


    public Object getPrincipal() {
        return this.userDetails;
    }


    public boolean isAuthenticated() {
        return authentication;
    }


    public void setAuthenticated(boolean arg0)
            throws IllegalArgumentException {
        this.authentication = arg0;
    }


    public String getName() {
        return null;
    }
}

You can make your code testable using the static approach. You simply need to create your own implementation of org.springframework.security.Authentication

So in your JUnit Test...

//Assuming you've loaded the user, create your stub
Authentication authentication = new TestAuthentication(userDetails);
//Update the context
SecurityContextHolder.getContext().setAuthentication(authentication);

In the example above 'userDetails' is the class which implements 'UserDetails' and typically wraps your domain User object.

My TestAuthentication class - hope this helps

public class TestAuthentication implements Authentication {

    private UserDetails userDetails;
    private boolean authentication = true;

    public TestAuthentication(UserDetails userDetails){
        NullArgumentException.assertNotNull(userDetails, "userDetails");
        this.userDetails = userDetails;
    }

    public TestAuthentication(UserDetails userDetails, boolean authentication){
        NullArgumentException.assertNotNull(userDetails, "userDetails");
        this.userDetails = userDetails;
        this.authentication = authentication;
    }


    public GrantedAuthority[] getAuthorities() {
        return userDetails.getAuthorities();
    }


    public Object getCredentials() {
        return null;
    }


    public Object getDetails() {
        return null;
    }


    public Object getPrincipal() {
        return this.userDetails;
    }


    public boolean isAuthenticated() {
        return authentication;
    }


    public void setAuthenticated(boolean arg0)
            throws IllegalArgumentException {
        this.authentication = arg0;
    }


    public String getName() {
        return null;
    }
}
故笙诉离歌 2024-08-05 02:31:04

发生这种情况是因为您创建的 bean 没有定义范围,所以它基本上是一个单例。
要使其按您希望的方式工作,您需要将其设置为请求/会话范围。

It happens since the bean you created does not define scope- so it is basically a singleton.
To make it work as you wish you need to make it request/session scoped.

诗酒趁年少 2024-08-05 02:31:04

使用一个您可以注入默认供应商的供应商。

public class UserDetailsArgumentResolver implements HandlerMethodArgumentResolver {

    private final Supplier<SecurityContext> contextSupplier;

    public UserDetailsArgumentResolver(Supplier<SecurityContext> contextSupplier) {
        this.contextSupplier = contextSupplier;
    }

    public UserDetailsArgumentResolver() {
        this.contextSupplier = SecurityContextHolder::getContext;
    }

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return UserDetails.class.isAssignableFrom(methodParameter.getParameterType());
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        Authentication authentication = contextSupplier.get().getAuthentication();
        return new UserDetails(authentication.getName(), authentication.getAuthorities());
    }
}

Use a supplier that you could inject with a default one perhaps.

public class UserDetailsArgumentResolver implements HandlerMethodArgumentResolver {

    private final Supplier<SecurityContext> contextSupplier;

    public UserDetailsArgumentResolver(Supplier<SecurityContext> contextSupplier) {
        this.contextSupplier = contextSupplier;
    }

    public UserDetailsArgumentResolver() {
        this.contextSupplier = SecurityContextHolder::getContext;
    }

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return UserDetails.class.isAssignableFrom(methodParameter.getParameterType());
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        Authentication authentication = contextSupplier.get().getAuthentication();
        return new UserDetails(authentication.getName(), authentication.getAuthorities());
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文