3

I'm trying to figure out how to have Spring Security return a UserDetails object for anonymous users for this call:

SecurityContextHolder.getContext().getAuthentication().getPrincipal()

I know that without special configuration, that call will return a string instead of a UserDetails object you create with a custom UserDetailsService implementation, but I'd rather not constantly check for "if(principal instanceof String)" everywhere. Is there a way to do this with the Spring configuration - a way that will store the anonymous UserDetails object in the user's session context until they log in? Ostensibly, I'd like a unique anonymous UserDetails for each guest so I can track individual usage with it.

I've also noticed that methods I have secured with the "PreAuthorize" annotation don't seem to honour the hasRole check with anonymous users. I'm sure that is a symptom of whatever I'm doing wrong. Here's an example of that:

@RequestMapping(value = "/almanac/new", method = RequestMethod.GET)
@PreAuthorize("hasRole('ROLE_ADMIN')")
public String newSetup(ModelMap model) {

Here's my spring security context (complete except for the enclosing beans node). You can note that I tried enabling the "anonymous"

<debug />
<http pattern="/js/**"  security="none" />
<http pattern="/css/**"  security="none" />
<http pattern="/images/**"  security="none" />
<http pattern="/loggedout.jsp" security="none"/>
<http name="httpSiteMap" use-expressions="true">
    <custom-filter ref="almanacUsrPwdAuthProcFilter" before="FORM_LOGIN_FILTER"/>
    <intercept-url pattern="/login*" access="isAnonymous()" />
    <intercept-url pattern="/home/**" access="hasRole('ROLE_USER')" />
    <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
    <intercept-url pattern="/**" access="isAnonymous()" />
    <form-login  login-page="/login.jsp"
                 default-target-url="/home.htm"
                 always-use-default-target="false" />
    <logout logout-success-url="/loggedout.jsp" delete-cookies="JSESSIONID"/>
    <session-management invalid-session-url="/timeout.jsp">
        <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
    </session-management>
    <anonymous enabled="true" />
</http>

<global-method-security pre-post-annotations="enabled" secured-annotations="enabled" />

<authentication-manager alias="mainAuthMgr">
    <authentication-provider ref="almanacAuthenticationProvider"/>
</authentication-manager>

Any other suggestions based on the code you see here would be welcome.

ogradyjd
  • 3,971
  • 6
  • 23
  • 30
  • Could you explain what you want to use the anonymous `UserDetails` for? What data would it contain and how would you use it? You don't need `` as this is the default. You should see the `AnonymousAuthenticationFilter` being reported in the debug log as confirmation. – Shaun the Sheep Jan 29 '12 at 18:57
  • I am trying to avoid having the "getPrincipal()" call return a String. I'd like to have that call only return a UserDetails implementation, whether spring anonymously authenticates or the user logs in. I want to avoid having to test the return value of "getPrincipal()" for instanceof String. Of course, since the documentation is very ambiguous in certain areas and information for specific points of configuration is spread thinly over many chapters, it's hard to figure if this is a thing you should do, or just deal with the instanceof comparisons. – ogradyjd Jan 29 '12 at 19:28

1 Answers1

4

You haven't really given much detail on what data you want to extract from the UserDetails for an anonymous user. If you just want to check the user name, you can use Authentication.getName().

In any case, if you need more detailed access to the security context, it is a good idea to use a custom interface to decouple your application code from Spring Security classes (see my answer on using a security context accessor). That way you should only be checking the type of the principal in one place, rather than repeating the check "everywhere" that you want to access the user data.

If you absolutely want to always have the same principal type in the authentication object, regardless of whether the user is anonymous, then you would have to disable anonymous authentication via the namespace and add a custom filter to do the job, based on the existing AnonymousAuthenticationFilter.

Community
  • 1
  • 1
Shaun the Sheep
  • 22,353
  • 1
  • 72
  • 100
  • I must be misunderstanding something very basic then if I should not have to do what you said, because the method security is not working with the default anonymous authentication. You can see from my code that I enabled global-method-security, and yet I can request the URL that calls the "newSetup" method without logging in, and I'm pretty sure I did not specify anywhere that anonymous users should have ROLE_ADMIN (wouldn't know how to if I wanted to right now). I will implement the security accessor facade as you suggested. – ogradyjd Jan 29 '12 at 22:18
  • It's not clear from the second part of your question how it is related to anonymous authentication or the need to access the security context. Does the annnotation work at all for non-anonymous users? If not, it is probably an application context visibility issue - see [this FAQ](http://static.springsource.org/spring-security/site/faq/faq.html#faq-method-security-in-web-context) . – Shaun the Sheep Jan 29 '12 at 22:42
  • [This question](http://stackoverflow.com/questions/2114814/spring-security-spring-mvc-configuration) also discusses the issue of using security annotations with MVC controllers. – Shaun the Sheep Jan 29 '12 at 22:49
  • Never mind about the PreAuthorize problem. It turns out it has nothing to do with anonymous login and everything to do with Spring application context hierarchy voodoo. The exact description and solution for that problem is [here](http://static.springsource.org/spring-security/site/faq.html#faq-method-security-in-web-context) – ogradyjd Jan 30 '12 at 00:21
  • ...and yes, Luke - you were absolutely right. Thanks for the hint that led me to the FAQ and fix. – ogradyjd Jan 30 '12 at 00:23