views:

1493

answers:

2

Hi all,

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.

A: 

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.

kgiannakakis
I agree with this, don't fight the framework.
JamesC
A: 

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;
    }
}
JamesC
Thanks for the suggestion. It seems that it would be a lot easier if the security context could be injected as then it could be mocked.. instead with this approach i must create a bunch of test dummys and stubs, including ones for Authentication and GrantedAuthority. But ultimately this is the approach I've had to take. :-)