James Garrison (Migrated from SEC-3139) said:
Consider the following RequestMapping in a SpringMVC controller:
@RequestMapping("/index")
public String index(
Authentication auth, // <-- null if anonymous user
ModelMap model,
HttpServletRequest req,
HttpServletResponse resp)
{
SecurityContext ctx = SecurityContextHolder.getContext();
Authentication auth2 = ctx.getAuthentication(); // <-- always non-null (correct)
...
}
I got this (use of an Authentication controller parameter) from here.
If the user is "really" authenticated (i.e. logged in) then this works as advertised. The problem is that when anonymous authentication is allowed and no user is logged in the value of auth is null when it should be an AnonymousAuthenticationToken.
The alternate method of going through ctx.getAuthentication() returns the correct AnonymousAuthenticationToken regardless of whether the user is anonymous or a "real" logged in user.
What's interesting is that Authentication is not included in the list of Supported Method Argument Types. If it just didn't work at all I'd say Baeldung was just mistaken, but the fact that it works for "real" authentication but not for anonymous makes me wonder where the problem is.
I believe this may be a bug that has been around for a while (see a similar unanswered question).
If it should work as described by Baeldung then it needs to be fixed, and the documentation updated to include Authentication as an allowable parameter type.
Here's my Spring Security configuration:
<s:http use-expressions="false">
<s:anonymous />
<s:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<s:form-login login-page="/login" default-target-url='/index'/>
<s:logout logout-success-url="/login"/>
</s:http>
Comment From: spring-projects-issues
Rob Winch said:
The reason this happens is because Spring MVC resolves anything of type Principal to the HttpServletRequest.getUserPrincipal() result. Spring Security makes this null if the user is not authenticated to conform with the Servlet specification.
A workaround is to use something like this:
@RequestMapping("/index")
public String index(
@Value("#{T(org.springframework.security.core.context.SecurityContextHolder).context.authentication}") Authentication auth,
ModelMap model,
HttpServletRequest req,
HttpServletResponse resp)
{
SecurityContext ctx = SecurityContextHolder.getContext();
Authentication auth2 = ctx.getAuthentication(); // <-- always non-null (correct)
...
}
This is not ideal, but if I recall correctly, Spring Security cannot register a HandlerMethodArgumentResolver with a higher priority that those provided by Spring MVC.
cc [~rstoya05-aop]
Comment From: spring-projects-issues
James Garrison said:
I understand.
This probably deserves a paragraph in the documentation.
Comment From: jzheaux
Note that as of Security 5.2, you can use @CurrentSecurityContext to reduce some of the boilerplate. I'll add some documentation to this affect.