In my controllers, when I need the active (logged in) user, I am doing the following to get my UserDetails implementation:
User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
It works fine, but I would think Spring could make life easier in a case like this. Is there a way to have the UserDetails autowired into either the controller or the method?
For example, something like:
public ModelAndView someRequestHandler(Principal principal) { ... }
But instead of getting the UsernamePasswordAuthenticationToken, I get a UserDetails instead?
I'm looking for an elegant solution. Any ideas?
Preamble: Since Spring-Security 3.2 there is a nice annotation #AuthenticationPrincipal described at the end of this answer. This is the best way to go when you use Spring-Security >= 3.2.
When you:
use an older version of Spring-Security,
need to load your custom User Object from the Database by some information (like the login or id) stored in the principal or
want to learn how a HandlerMethodArgumentResolver or WebArgumentResolver can solve this in an elegant way, or just want to an learn the background behind #AuthenticationPrincipal and AuthenticationPrincipalArgumentResolver (because it is based on a HandlerMethodArgumentResolver)
then keep on reading — else just use #AuthenticationPrincipal and thank to Rob Winch (Author of #AuthenticationPrincipal) and Lukas Schmelzeisen (for his answer).
(BTW: My answer is a bit older (January 2012), so it was Lukas Schmelzeisen that come up as the first one with the #AuthenticationPrincipal annotation solution base on Spring Security 3.2.)
Then you can use in your controller
public ModelAndView someRequestHandler(Principal principal) {
User activeUser = (User) ((Authentication) principal).getPrincipal();
...
}
That is ok if you need it once. But if you need it several times its ugly because it pollutes your controller with infrastructure details, that normally should be hidden by the framework.
So what you may really want is to have a controller like this:
public ModelAndView someRequestHandler(#ActiveUser User activeUser) {
...
}
Therefore you only need to implement a WebArgumentResolver. It has a method
Object resolveArgument(MethodParameter methodParameter,
NativeWebRequest webRequest)
throws Exception
That gets the web request (second parameter) and must return the User if its feels responsible for the method argument (the first parameter).
Since Spring 3.1 there is a new concept called HandlerMethodArgumentResolver. If you use Spring 3.1+ then you should use it. (It is described in the next section of this answer))
public class CurrentUserWebArgumentResolver implements WebArgumentResolver{
Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
if(methodParameter is for type User && methodParameter is annotated with #ActiveUser) {
Principal principal = webRequest.getUserPrincipal();
return (User) ((Authentication) principal).getPrincipal();
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
You need to define the Custom Annotation -- You can skip it if every instance of User should always be taken from the security context, but is never a command object.
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ActiveUser {}
In the configuration you only need to add this:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"
id="applicationConversionService">
<property name="customArgumentResolver">
<bean class="CurrentUserWebArgumentResolver"/>
</property>
</bean>
#See: Learn to customize Spring MVC #Controller method arguments
It should be noted that if you're using Spring 3.1, they recommend HandlerMethodArgumentResolver over WebArgumentResolver. - see comment by Jay
The same with HandlerMethodArgumentResolver for Spring 3.1+
public class CurrentUserHandlerMethodArgumentResolver
implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return
methodParameter.getParameterAnnotation(ActiveUser.class) != null
&& methodParameter.getParameterType().equals(User.class);
}
#Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
if (this.supportsParameter(methodParameter)) {
Principal principal = webRequest.getUserPrincipal();
return (User) ((Authentication) principal).getPrincipal();
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
In the configuration, you need to add this
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="CurrentUserHandlerMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
#See Leveraging the Spring MVC 3.1 HandlerMethodArgumentResolver interface
Spring-Security 3.2 Solution
Spring Security 3.2 (do not confuse with Spring 3.2) has own build in solution: #AuthenticationPrincipal (org.springframework.security.web.bind.annotation.AuthenticationPrincipal) . This is nicely described in Lukas Schmelzeisen`s answer
It is just writing
ModelAndView someRequestHandler(#AuthenticationPrincipal User activeUser) {
...
}
To get this working you need to register the AuthenticationPrincipalArgumentResolver (org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver) : either by "activating" #EnableWebMvcSecurity or by registering this bean within mvc:argument-resolvers - the same way I described it with may Spring 3.1 solution above.
#See Spring Security 3.2 Reference, Chapter 11.2. #AuthenticationPrincipal
Spring-Security 4.0 Solution
It works like the Spring 3.2 solution, but in Spring 4.0 the #AuthenticationPrincipal and AuthenticationPrincipalArgumentResolver was "moved" to an other package:
org.springframework.security.core.annotation.AuthenticationPrincipal
org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver
(But the old classes in its old packges still exists, so do not mix them!)
It is just writing
import org.springframework.security.core.annotation.AuthenticationPrincipal;
ModelAndView someRequestHandler(#AuthenticationPrincipal User activeUser) {
...
}
To get this working you need to register the (org.springframework.security.web.method.annotation.) AuthenticationPrincipalArgumentResolver : either by "activating" #EnableWebMvcSecurity or by registering this bean within mvc:argument-resolvers - the same way I described it with may Spring 3.1 solution above.
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
#See Spring Security 5.0 Reference, Chapter 39.3 #AuthenticationPrincipal
While Ralphs Answer provides an elegant solution, with Spring Security 3.2 you no longer need to implement your own ArgumentResolver.
If you have a UserDetails implementation CustomUser, you can just do this:
#RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(#AuthenticationPrincipal CustomUser customUser) {
// .. find messages for this User and return them...
}
See Spring Security Documentation: #AuthenticationPrincipal
Spring Security is intended to work with other non-Spring frameworks, hence it is not tightly integrated with Spring MVC. Spring Security returns the Authentication object from the HttpServletRequest.getUserPrincipal() method by default so that's what you get as the principal. You can obtain your UserDetails object directly from this by using
UserDetails ud = ((Authentication)principal).getPrincipal()
Note also that the object types may vary depending on the authentication mechanism used (you may not get a UsernamePasswordAuthenticationToken, for example) and the Authentication doesn't strictly have to contain a UserDetails. It can be a string or any other type.
If you don't want to call SecurityContextHolder directly, the most elegant approach (which I would follow) is to inject your own custom security context accessor interface which is customized to match your needs and user object types. Create an interface, with the relevant methods, for example:
interface MySecurityAccessor {
MyUserDetails getCurrentUser();
// Other methods
}
You can then implement this by accessing the SecurityContextHolder in your standard implementation, thus decoupling your code from Spring Security entirely. Then inject this into the controllers which need access to security information or information on the current user.
The other main benefit is that it is easy to make simple implementations with fixed data for testing, without having to worry about populating thread-locals and so on.
Implement the HandlerInterceptor interface, and then inject the UserDetails into each request that has a Model, as follows:
#Component
public class UserInterceptor implements HandlerInterceptor {
....other methods not shown....
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if(modelAndView != null){
modelAndView.addObject("user", (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal());
}
}
Starting with Spring Security version 3.2, the custom functionality that has been implemented by some of the older answers, exists out of the box in the form of the #AuthenticationPrincipal annotation that is backed by AuthenticationPrincipalArgumentResolver.
An simple example of it's use is:
#Controller
public class MyController {
#RequestMapping("/user/current/show")
public String show(#AuthenticationPrincipal CustomUser customUser) {
// do something with CustomUser
return "view";
}
}
CustomUser needs to be assignable from authentication.getPrincipal()
Here are the corresponding Javadocs of
AuthenticationPrincipal and AuthenticationPrincipalArgumentResolver
#Controller
public abstract class AbstractController {
#ModelAttribute("loggedUser")
public User getLoggedUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
And if you need authorized user in templates (e.g. JSP) use
<%# taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authentication property="principal.yourCustomField"/>
together with
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring-security.version}</version>
</dependency>
You can try this:
By Using Authentication Object from Spring we can get User details from it in the controller method . Below is the example , by passing Authentication object in the controller method along with argument.Once user is authenticated the details are populated in the Authentication Object.
#GetMapping(value = "/mappingEndPoint") <ReturnType> methodName(Authentication auth) {
String userName = auth.getName();
return <ReturnType>;
}
To get the Active Users Details you can use #AuthenticationPrincipal in your controller like this:-
public String function(#AuthenticationPrincipal UserDetailsImpl user,
Model model){
model.addAttribute("username",user.getName()); //this user object
contains user details
return "";
}
UserDetailsImpl.java
import com.zoom.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
public class UserDetailsImpl implements UserDetails {
#Autowired
private User user;
public UserDetailsImpl(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ADMIN");
return List.of(simpleGrantedAuthority);
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public String getRole(){
return user.getRole();
}
public String getName(){
return user.getUsername();
}
public User getUser(){
return user;
}
}
Related
How can I map custom/dunamic requests toa given controller, based on a repository lookup?
The use-case is a CMS-like feature in a web-platform, where certain URL patterns ("pages") stored in the DB should be handled by a separate controller PageController.java. These patterns are not necessarily known at compile-time, and they can also be added and modified while the app is deployed (thus, it cannot be annotation-driven).
I did try to map a controller to "**" (see below), but that did not work for 2 reasons: firstly all other requests resolved to that same controller method (I had hoped that it would use "**" as a fallback and try the others first), and it also ended up resolving all requests to my static/asset files to this controller (resulting in unwanted 404-responses).
#Controller
public class PageController {
#Inject
private PageService pageService;
#RequestMapping(value = "**", method = RequestMethod.GET)
public String getPage(Model model, HttpServletRequest request, #CurrentUser User user) {
String path = request.getRequestURI();
Page page = this.pageService.getByPath(path, user);
if (page == null) {
throw new NotFoundException();
}
model.addAttribute("page", page);
return "web/page";
}
}
The temporary work-around/modification to the above method has so far been to map a pre-defined URL-prefixes to this controller (eg. /page/**, /info/**, /news/** etc), but this is an inelegant solution that adds arbitrary limitations to the system which I now seek to eliminate.
I am currently using Spring Boot 2.0. In addition to the naive mapping to ** in a regular #Controller class (using the #RequestMapping -annotation), I have also tried configuring SimpleUrlHandlerMapping the following way:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Inject
private PageDao pageDao;
#Bean
public PageController pageController() {
return new PageController();
}
#Bean
public SimpleUrlHandlerMapping pageUrlHandlerMapping() {
SimpleUrlHandlerMapping pageUrlHandlerMapping = new SimpleUrlHandlerMapping();
PageController pageController = this.pageController();
Map<String, Object> urlMap = this.pageDao.findAll().stream()
.map(Page::getNormalizedSlug)
.collect(Collectors.toMap(Function.identity(),
slug -> pageController, (existing, duplicate) -> existing));
pageUrlHandlerMapping.setUrlMap(urlMap);
pageUrlHandlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE); // <- Cannot be LOWEST_PRECEDENCE for some reason...
return pageUrlHandlerMapping;
}
}
public class PageController implements Controller {
#Inject
private PageService pageService;
#Inject
private DmaWebControllerAdvice controllerAdvice;
#Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
User user = null;
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof User) {
user = (User) principal;
}
String path = request.getRequestURI();
Page page = this.pageService.getByPath(path, user);
if (page == null) {
throw new NotFoundException();
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("web/page");
modelAndView.addObject("page", page);
controllerAdvice.globalModelAttributes(modelAndView.getModel(), null);
return modelAndView;
}
}
This approach does technically work, but the list of pages will somehow have to be reloaded into the SimpleUrlHandlerMapping whenever one of the pages is changed (i am not quite sure how to do that). This also possibly overwrites some default Spring Boot-configuration, that I would ideally like to keep. It also has some drawbacks compared to resolving controllers using #Controller and #RequesMapping because I currently am injecting certain data into all views resolved that way (mainly model-data used in the overall design of the website, like menu, quicklinks etc). In the above attempt, I have had to set those via a separate call to controllerAdvice-globalModelAttributes().
What I am seeking is a solution where my repository is queried for potential page-matches in runtime, and if it is valid then the request will be handled by the proper page-controller. Is a custom HandlerMapping -implementation the way to do this? And if not, how should I solve this? And if making a separate HandlerMapping for pages, how do I add/register this in my configuration without overwriting the default provided by Spring?
Why don't you just implement a catch-all controller which parses your patterns as a parameter, does a db look-up and then use a forward to specific controllers (info, page, news etc.)? Seems like for a CMS, this look-up logic belongs into your code (e.g. service layer).
Easiest(but not the best) way to achieve what you need is creating custom HandlerMapping implementation:
public class PageMapper implements HandlerMapping, Ordered {
private HandlerMethod handlerMethod;
public CustomMapper(Object controller, Method method) {
this.handlerMethod = new HandlerMethod(controller, method);
}
#Override
public HandlerExecutionChain getHandler(HttpServletRequest httpServletRequest) throws Exception {
return new HandlerExecutionChain(handlerMethod);
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE; //you have to add the handler to the end
}
}
Now remove #Controller annotation from PageController because you don't need it to be detected automatically anymore. After that register controller and mapping to config:
#Configuration
public class AppWebConfig implements WebMvcConfigurer {
#Bean
public PageController pageController() {
return new PageController();
}
#Bean
public HandlerMapping pageMapping(PageController pageController) {
Method method = BeanUtils.resolveSignature("getPage", PageController.class);
return new PageMapping(pageController, method);
}
}
Now every request unrecognized by other HandlerMapping instances will be sent to your mapping hence to your controller. But this approach has obvious disadvantage. Since your mapping is the last in the chain of mappings you never get 404 error. Therefor you never know about something wrong with you resources (e.g. if some of them are missing).
I would prefer let application to distinguish paths by prefix (just like you do it already), where prefix is operation application is going to do with a page. For example if you need to show or edit the page:
#Controller
public class PageController {
private final static String SHOW = "/show";
private final static String EDIT = "/edit";
#Inject
private PageService pageService;
GetMapping(value = SHOW + "/**")
public String getPage(Model model, HttpServletRequest request, #CurrentUser User user) {
String path = request.getRequestURI().substring(SHOW.length());
Page page = this.pageService.getByPath(path, user);
...
model.addAttribute("page", page);
return "web/page";
}
//the same for EDIT operation
}
I've been having some trouble figuring this one out.
I've got an multitenant system where users are organized into organizations. Within that organization the usernames must be unique. Otherwise, two organizations can have the same user name.
I've got spring security hooked up with the jdbc-user-service and all that works fine. My problems start when I'm trying to get the current user.
I took a look at a link that leverages spring 3 and the Principal object as a method parameter. This works great, except a Principal doesn't have enough information! First, usernames aren't unique in my usecase, also having easy access to the organization that the user belongs to would be great.
A little more searching dug up this awesome answer. The problem with this is the same problem as before. It relies on the Principal object, which just doesn't have enough info.
(here's the magic)
#Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
if (this.supportsParameter(methodParameter)) {
Principal principal = webRequest.getUserPrincipal();
return (User) ((Authentication) principal).getPrincipal();
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
Is there a good way to override the User object to use my own? Am i stuck writing a custom UserDetailService? Is there a better approach than continuing to walk down this path?
Thanks
Make your User object inherit UserDetails(or use a wrapper) and use it as principal.
e.g.
public class MyCustomUser implements UserDetails {
// ..
}
Make a custom UserDetailsService that returns your User object:
#Service
public class MyCustomUserDetailsServiceImpl implements UserDetailsService {
#Autowired
private MyCustomUserDAO userDAO;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userDAO.getByUsername(username);
}
}
Now you can extract your user:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object principal = auth.getPrincipal();
MyCustomUser user = (MyCustomUser)principal;
user.myCustomMethod();
In Spring MVC, it is easy to bind request parameter to method paramaters handling the request. I just use #RequestParameter("name"). But can I do the same with request attribute? Currently, when I want to access request attribute, I have to do following:
MyClass obj = (MyClass) request.getAttribute("attr_name");
But I really would like to use something like this instead:
#RequestAttribute("attr_name") MyClass obj
Unfortunately, it doesn't work this way. Can I somehow extend Spring functionality and add my own "binders"?
EDIT (what I'm trying to achieve): I store currently logged user inside request attribute. So whenever I want to access currently logged user (which is pretty much inside every method), I have to write this extra line user = (User) request.getAttribute("user");. I would like to make it as short as possible, preferably inject it as a method parameter. Or if you know another way how to pass something across interceptors and controllers, I would be happy to hear it.
Well, I finally understood a little bit how models work and what is #ModelAttribute for. Here is my solution.
#Controller
class MyController
{
#ModelAttribute("user")
public User getUser(HttpServletRequest request)
{
return (User) request.getAttribute("user");
}
#RequestMapping(value = "someurl", method = RequestMethod.GET)
public String HandleSomeUrl(#ModelAttribute("user") User user)
{
// ... do some stuff
}
}
The getUser() method marked with #ModelAttribute annotation will automatically populate all User user parameters marked with #ModelAttribute. So when the HandleSomeUrl method is called, the call looks something like MyController.HandleSomeUrl(MyController.getUser(request)). At least this is how I imagine it. Cool thing is that user is also accessible from the JSP view without any further effort.
This solves exactly my problem however I do have further questions. Is there a common place where I can put those #ModelAttribute methods so they were common for all my controllers? Can I somehow add model attribute from the inside of the preHandle() method of an Interceptor?
Use (as of Spring 4.3) #RequestAttribute:
#RequestMapping(value = "someurl", method = RequestMethod.GET)
public String handleSomeUrl(#RequestAttribute User user) {
// ... do some stuff
}
or if the request attribute name does not match the method parameter name:
#RequestMapping(value = "someurl", method = RequestMethod.GET)
public String handleSomeUrl(#RequestAttribute(name="userAttributeName") User user) {
// ... do some stuff
}
I think what you are looking for is:
#ModelAttribute("attr_name") MyClass obj
You can use that in the parameters for a method in your controller.
Here is a link a to question with details on it What is #ModelAttribute in Spring MVC?
That question links to the Spring Documentation with some examples of using it too. You can see that here
Update
I'm not sure how you are setting up your pages, but you can add the user as a Model Attribute a couple different ways. I setup a simple example below here.
#RequestMapping(value = "/account", method = RequestMethod.GET)
public ModelAndView displayAccountPage() {
User user = new User(); //most likely you've done some kind of login step this is just for simplicity
return new ModelAndView("account", "user", user); //return view, model attribute name, model attribute
}
Then when the user submits a request, Spring will bind the user attribute to the User object in the method parameters.
#RequestMapping(value = "/account/delivery", method = RequestMethod.POST)
public ModelAndView updateDeliverySchedule(#ModelAttribute("user") User user) {
user = accountService.updateDeliverySchedule(user); //do something with the user
return new ModelAndView("account", "user", user);
}
Not the most elegant, but works at least...
#Controller
public class YourController {
#RequestMapping("/xyz")
public ModelAndView handle(
#Value("#{request.getAttribute('key')}") SomeClass obj) {
...
return new ModelAndView(...);
}
}
Source : http://blog.crisp.se/tag/requestattribute
From spring 3.2 it can be done even nicer by using Springs ControllerAdvice annotation.
This then would allow you to have an advice which adds the #ModelAttributes in a separate class, which is then applied to all your controllers.
For completeness, it is also possible to actually make the #RequestAttribute("attr-name") as is.
(below modified from this article to suit our demands)
First, we have to define the annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface RequestAttribute {
String value();
}
Then we need a [WebArgumentResolver] to handle what needs to be done when the attribute is being bound
public class RequestAttributeWebArgumentResolver implements WebArgumentResolver {
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest nativeWebRequest) throws Exception {
// Get the annotation
RequestAttribute requestAttributeAnnotation = methodParameter.getParameterAnnotation(RequestAttribute.class);
if(requestAttributeAnnotation != null) {
HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
return request.getAttribute(requestAttributeAnnotation.value);
}
return UNRESOLVED;
}
}
Now all we need is to add this customresolver to the config to resolve it:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customArgumentResolver">
<bean class="com.sergialmar.customresolver.web.support.CustomWebArgumentResolver"/>
</property>
</bean>
And we're done!
Yes, you can add your own 'binders' to the request attribute - see spring-mvc-3-showcase, or use #Peter Szanto's solution.
Alternatively, bind it as a ModelAttribute, as recommended in other answers.
As it's the logged-in user that you want to pass into your controller, you may want to consider Spring Security. Then you can just have the Principle injected into your method:
#RequestMapping("/xyz")
public String index(Principal principle) {
return "Hello, " + principle.getName() + "!";
}
In Spring WebMVC 4.x, it prefer implements HandlerMethodArgumentResolver
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(RequestAttribute.class) != null;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return webRequest.getAttribute(parameter.getParameterAnnotation(RequestAttribute.class).value(), NativeWebRequest.SCOPE_REQUEST);
}
}
Then register it in RequestMappingHandlerAdapter
I want to access the current logged in user I am doing it like this (from a static method)
public static User getCurrentUser() {
final Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof User) {
return (User) principal;
}
}
or injecting and casting like this :
#RequestMapping(value = "/Foo/{id}", method = RequestMethod.GET)
public ModelAndView getFoo(#PathVariable Long id, Principal principal) {
User user = (User) ((Authentication) principal).getPrincipal();
..
Where user implements userdetails, both seem a bit lame is there a better way in Spring 3.2 ?
I don't think that it has something new in spring 3.2 for that purpose. Have you thought about using a custom annotation?
Something like this :
The controller with the custom annotation :
#Controller
public class FooController {
#RequestMapping(value="/foo/bar", method=RequestMethod.GET)
public String fooAction(#LoggedUser User user) {
System.out.print.println(user.getName());
return "foo";
}
}
The LoggedUser annotation :
#Target(ElementType.PARAMETER)
#Retention(RententionPolicy.RUNTIME)
#Documented
public #interface LoggedUser {}
The WebArgumentResolver :
public class LoggedUserWebArgumentResolver implements WebArgumentResolver {
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
Annotation[] annotations = methodParameter.getParameterAnnotations();
if (methodParameter.getParameterType().equals(User.class)) {
for (Annotation annotation : annotations) {
if (LoggedUser.class.isInstance(annotation)) {
Principal principal = webRequest.getUserPrincipal();
return (User)((Authentication) principal).getPrincipal();
}
}
}
return WebArgumentResolver.UNRESOLVED;
}
}
Beans configuration :
<bean id="loggedUserResolver" class="com.package.LoggedUserWebArgumentResolver" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customArgumentResolver" ref="loggedUserResolver" />
</bean>
I created a method much like your static, but put it in a new spring bean and inject that bean into the controllers (or objects at other layers) in which I need to get information about the user. That way I avoid the difficulties in testing code that depends on a static and all the code messing with the SecurityContext is nicely encapsulated into my bean (as you have in your static)
I'm trying to get my feet wet with Spring MVC 3.0, and while I can get it to work, I can't seem to handle this particular scenario efficiently.
I have a controller with that handles "/{studyName}/module" prefix, and it looks something like this:-
#Controller
#RequestMapping(value = "/{studyName}/module")
public class ModuleController {
#RequestMapping(...)
public ModelAndView getA(#PathVariable String studyName, ...) {
if (!validStudy(studyName)) { return bad request; }
...
}
#RequestMapping(...)
public ModelAndView getB(#PathVariable String studyName, ...) {
if (!validStudy(studyName)) { return bad request; }
...
}
#RequestMapping(...)
public ModelAndView getC(#PathVariable String studyName, ...) {
if (!validStudy(studyName)) { return bad request; }
...
}
#RequestMapping(...)
public ModelAndView getD(#PathVariable String studyName, ...) {
if (!validStudy(studyName)) { return bad request; }
...
}
}
The problem with this code is, I have the studyName validation scattered all over the methods and possibly in other Controllers' methods too. Is there a way I can perform validation on studyName path variable all in one spot without using something like AOP? How do you handle validation like this?
Thanks.
Right now, it's a little tricky to make this happen automatically, but it is possible. You should use a Bean validation (JSR-303) provider that implements appendix C. Currently that's Apache BeanValidation or Hibernate Validator 4.2 (which is in beta).
Add your chosen bean validation implementation to the classpath. This will be the implementation of JSR-303 that Spring MVC uses.
Second, annotate the method parameter with #Valid and any constraint annotations, like #NonNull.
This will look something like:
public ModelAndView getB(#Valid #NonNull #PathVariable String studyName, ...) {
That should work. You'd then need to check your Spring errors for any problems.
Alternatively, if you don't make use of any other Spring parameters, you can register a validator with an InitBinder like so:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setValidator(new StudyNameValidator());
}
Create a class StudyName then have a WebArgumentResolver registered for StudyName and have your validation take place there.
public ModelAndView getA(#PathVariable StudyName studyName){
...
}
public class StudyNameResolver implements WebArgumentResolver{
//have resolveArgument method do validation if resolved to a StudyName
}
I am starting to use spring 3 and I do like your solution of validating in this way:
public ModelAndView getB(#Valid #NonNull #PathVariable String studyName, ...) {
However, once the pathvariable is invalid (in this case studyName = null) how do you catch and display that error?
I have tried to use binding result but it just doesn't work. In addition, do you know how to display the error on the jsp?
Thanks
Create a simple validation class:
public class StudyValidator {
public boolean validateStudy(String studyName) {
//your validate logic here
}
}
then inject it into the ModuleController:
class ModuleController {
private StudyValidator sv = new StudyValidator(); //use spring injection to populate.
boolean validStudy(String studyName) {
return sv.validateStudy(studyName);
}
}
Simples.
Hmmm, not sure if it would work, but you might be able to the #Valid annotation as briefly mentioned in this link on validators.
Good Luck!