Spring MVC: Get the logged in UserDetails from your Spring Security application
So you've got your Spring Security-enabled application up and running. You can login, logout, and you're even authorizing based on roles and permissions. You're glad to progress past all that boring infrastructure coding and get onto the meat and potatoes of your application: the business logic. You crank out controller after controller, and life is great because you're using Spring MVC.
But then you begin needing to access the currently logged in user from your Web controllers. So you do something like this:
@Controller public class MyAwesomeController { @RequestMapping(value="/foo/bar", method=RequestMethod.GET) public String foobar() { User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); System.out.println("Currently logged in user is: " + activeUser.getFullName()); ... return "foo/bar"; } }
Since you're using your User domain object as your UserDetails implementation, this works out kind of nice. You're able to get your user by getting the principal. Peachy.
Of course, the first time you did this you grumbled, and even though you knew polluting your controllers with infrastructure was the Wrong Way™ you did it anyway thinking, "Hey, it's only a single line, and it's just this one time."
But now you're on the 6th or 7th iteration of this bad coding practice, and you feel it's time for a change. Or a cold shower. Or both.
The Common Way
First, let's take a look at the obvious "solution". I say "solution" because really it doesn't solve the problem of polluting your controller layer with your security layer.
@Controller public class MyAwesomeController { @RequestMapping(value="/foo/bar", method=RequestMethod.GET) public String foobar(Principal principal) { User activeUser = (User)((Authentication) principal).getPrincipal(); System.out.println("Currently logged in user is: " + activeUser.getFullName()); ... return "foo/bar"; } }
This method isn't so terrible because at least now Spring is injecting the principal into the method for you. It's still pretty darn nasty because you're still polluting your controller.
A Better Approach
Here's a much better approach:
@Controller public class MyAwesomeController { @Autowired ActiveUserAccessor activeUserAccessor; @RequestMapping(value="/foo/bar", method=RequestMethod.GET) public String foobar() { User activeUser = activeUserAccessor.getActiveUser(); System.out.println("Currently logged in user is: " + activeUser.getFullName()); ... return "foo/bar"; } }
Then you have the interface:
public interface ActiveUserAccessor { public User getActiveUser(); }
Then you have an implementation:
@Component("activeUserAccessor") public class ActiveUserAccessorImpl implements ActiveUserAccessor { public User getActiveUser() { return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); } }
But all we've done is move the original stuff into a utility class, you sneer. (Jeez, calm down... you're way too testy.)
At first glance, yes. We're still having to call a line of infrastructure code from our controller, but we now have the benefit of being able to swap out a different implementation of our ActiveUserAccessor at runtime. This is very useful for testing, or if we later decide to abandon Spring Security in favor of another authentication framework. What's important is that we've decoupled Spring Security from our application, and that's a Good Thing™.
The Best Way™
That's all good and well, you scoff, but we want something better. It sure would be nice not to have to call even a single line of infrastructure code from our controller. If only Spring would just inject our User into the controller. So let's try that:
@Controller public class MyAwesomeController { @RequestMapping(value="/foo/bar", method=RequestMethod.GET) public String foobar(@ActiveUser User activeUser) { System.out.println("Currently logged in user is: " + activeUser.getFullName()); ... return "foo/bar"; } }
I'm going to wait for you to finish clapping and cheering my name. ... Okay, so that's pretty awesome right? Let's find out how to make it work.
First, we need to create the custom @ActiveUser annotation:
@Target(ElementType.PARAMETER) @Retention(RententionPolicy.RUNTIME) @Documented public @interface ActiveUser {}
Then, we need to provide a WebArgumentResolver implementation to resolve the parameter:
public class ActiveUserWebArgumentResolver implements WebArgumentResolver { public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) { Annotation[] annotations = methodParameter.getParameterAnnotations(); if(methodParameter.getParameterType().equals(User.class)) { for(Annotation annotation : annotations) { if(ActiveUser.class.isInstance(annotation)) { Principal principal = webRequest.getUserPrincipal(); return (User)((Authentication) principal).getPrincipal(); } } } return WebArgumentResolver.UNRESOLVED; } }
The resolver essentially ensures that any method parameter that's annotated with our @ActiveUser is in fact a User object. If it is, it returns the principal which is, in our case, the currently logged in User.
Finally, to wrap it all together, the resolver's bean configuration:
<!-- Enables our custom @ActiveUser annotation. --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="customArgumentResolver" ref="activeUserResolver" /> </bean> <bean id="activeUserResolver" class="com.awnry.springexample.ActiveUserWebArgumentResolver" />
And there you have it. Elegant, flexible access to the active User from any Web request handler method in your application.















