How to get Principal object in ModelAttribute method - java

I am trying to load some basic user information that should be available for every HTTP request. I read that I can do this with a #ControllerAdvice class with a #ModelAttribute method. For example:
#ControllerAdvice
public class DefaultController{
#ModelAttribute
public void load(ModelMap model){
}
}
However, I am having trouble accessing my Principal object managed by Spring Security. I have tried the following:
#ModelAttribute
public void load(ModelMap model, #AuthenticationPrincipal CustomUser user){
}
and
#ModelAttribute
public void load(ModelMap model, Principal user){
//Cast to CustomUser object
}
But both of these do not work. I am getting nulls for the user objects. I was wondering if anyone knew how to get the Principal object in #ModelAttribute methods

I just realized that when visiting my homepage for the first time, the user isn't authenticated so there wouldn't be a user object in spring security anyway.
I just needed to check if the object is null first like so
#ModelAttribute
public void load(ModelMap model, #AuthenticationPrincipal CustomUser user){
if(user != null){
long userId = user.getId();
//Query database with userId and add data to model
}
}

You can get an Authentication object using org.springframework.security.core.context.SecurityContextHolder
#ModelAttribute
public void addUserToModel(Model model) {
try {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
// do something
} catch (Exception ex) {
}
}
In fact, you can use SecurityContextHolder from anywhere in your application.

Related

Spring boot injection failure

I want to inject into the new object a class that contains the user.
#GetMapping
public String listTopic(Principal principal, Model model){
Optional<Users> user = usersService.findByUsername(principal.getName());
if (user.isPresent()){
Topics topic = new Topics();
topic.setUsers(user.get());
model.addAttribute("newTopic", topic);
model.addAttribute("topics", topicsService.listTopics());
return "forum/forum";
}
return "/error";
}
#PostMapping
public String addTopic(#Valid #ModelAttribute("newTopic") Topics topic, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "forum/forum";
}
topicsService.addTopic(topic);
System.out.println(topic);
return "redirect:/forum";
}
When I pass sysout after setting user obect or adding attribute at getmapping section it shows me the exact object, but when I want to see it at the postmapping it throws nullpointerexception.
Your model is a request scope object. After each request it is lost. You need to pass this information to a session object that is alive through different requests in the same session
https://stackoverflow.com/a/18795626/7237884

Getting the current logged in user name when using Spring Security

Its a coupon system app using Spring security, spring MVC,
now.
when the app starts, I need to somehow initialize the current logged in user into the controller.
Issue is:
If I try to get the current user via SecurityContextHolder it is impossible because it seems like spring is initializing the controllers before the security so I cannot get it in the controller.
Is there anything I'm missing? a different approach of getting the current logged in user after he logs in?
What you need is called #AuthenticationPrincipal.
You can inject it in controller method like this:
#GetMapping("/")
public void get(#AuthinticationPrincipal User user){ ... }
Here is documentation
Alternatively, you can create your own annotation and custom argument resolver, and inject whatever you want.
Solution 1: Principal principal
#RequestMapping(value = {"/", ""})
public String start(Principal principal, Model model) {
String currentUser = principal.getName();
return currentUser;
}
Solution 2: Authentication authentication
#RequestMapping(value = {"/", ""})
public String currentUserName(Authentication authentication) {
return authentication.getName();
}
Solution 3: SecurityContextHolder
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
More Here

Spring #SessionAttribute how to retrieve the session object in same controller

I am using Spring 3.2.0 MVC. In that I have to store one object to session.
Currently I am using HttpSession set and get attribute to store and retrieve the value.
It returns only the String not Object. I want to use #SessionAttribute when I tried it sets the object in session but I could not retrieve the session object
#RequestMapping(value = "/sample-login", method = RequestMethod.POST)
public String getLoginClient(HttpServletRequest request,ModelMap modelMap) {
String userName = request.getParameter("userName");
String password = request.getParameter("password");
User user = sample.createClient(userName, password);
modelMap.addAttribute("userObject", user);
return "user";
}
#RequestMapping(value = "/user-byName", method = RequestMethod.GET)
public
#ResponseBody
String getUserByName(HttpServletRequest request,#ModelAttribute User user) {
String fas= user.toString();
return fas;
}
Both methods are in same controller. How would I use this to retrieve the object?
#SessionAttributes annotation are used on the class level to :
Mark a model attribute should be persisted to HttpSession after handler methods are executed
Populate your model with previously saved object from HttpSession before handler methods are executed -- if one do exists
So you can use it alongside your #ModelAttribute annotation like in this example:
#Controller
#RequestMapping("/counter")
#SessionAttributes("mycounter")
public class CounterController {
// Checks if there's a model attribute 'mycounter', if not create a new one.
// Since 'mycounter' is labelled as session attribute it will be persisted to
// HttpSession
#RequestMapping(method = GET)
public String get(Model model) {
if(!model.containsAttribute("mycounter")) {
model.addAttribute("mycounter", new MyCounter(0));
}
return "counter";
}
// Obtain 'mycounter' object for this user's session and increment it
#RequestMapping(method = POST)
public String post(#ModelAttribute("mycounter") MyCounter myCounter) {
myCounter.increment();
return "redirect:/counter";
}
}
Also don't forget common noobie pitfall: make sure you make your session objects Serializable.

Overloading a spring controller method with the same request mapping

I have a session attribute : user, and I have a url that I want to be viewed by both logged in users and publically by people not logged in as a user.
So what I want to do is this :
#Controller("myController")
#SessionAttributes({"user"})
public class MyController {
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id) {
return modelandview1;
}
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id, #ModelAttribute User user){
return modelandview2;
}
However, I have a feeling its not going to work ... suggestions very welcome.
You only need the second method, the one that takes the User agument as well. When it's called without request attributes available to populate the User model, you'll just get a User instance with all null (or all default) field values, then in the body of the method you treat each situation accordingly
I don't think it's a right case for #SessionAttributes. This annotation is usually used to keep original instance of a form-backing object, to avoid passing irrelevant parts of its state via hidden form fields.
Your sceanrio is completely different, thus it would be better to use HttpSession explicitly:
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id, HttpSession session) {
User user = (User) session.getAttribute(...);
if (user != null) {
...
} else {
...
}
}
Also note that #ModelAttribute is a subject to data binding - user can change its fields by passing request parameters. You definitely don't want it in this case.

Update method in Rest like controllers

I want to write rest like method for entity update. In this case I retrieve entity id from url and data from request body. The issue is in binding id with bean. Because neither EntityManager nor Spring-Data Crud Repo haven't update(id, bean) method. So I can set it myself
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String update(#PathVariable("id") Long id, #Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
user.setId(id); //Very bad
return "usersEdit";
}
user.setId(id); //Bad
repository.save(user);
return "redirect:/users/" + id;
}
or dismiss DRY and put id in forms as private field to.
Is there are any other solutions?
In Spring 3.1 a #ModelAttribute will be instantiated from a path variable if the path variable and the model attribute names are the same and there is a converter to instantiate the model attribute from the path variable value:
#RequestMapping(value="/{account}", method = RequestMethod.PUT)
public String update(#Valid #ModelAttribute Account account, BindingResult result) {
if (result.hasErrors()) {
return "accounts/edit";
}
this.accountManager.saveOrUpdate(account);
return "redirect:../accounts";
}
The full example is available at:
https://github.com/rstoyanchev/spring-mvc-31-demo

Categories

Resources