I need to validate HttpSession (for Spring MVC Application) in a better way for my current Project.
Here is the Scenario:
1) Once user is successfully validated, userObject object is added to httpSession class
HttpSession session = req.getSession(true);
session.setAttribute(AppConstants.LOGGEDIN_PARAM, userDetail);
2) Then for each request, userObject is retrieved from HttpSession Class to validate user Session
#RequestMapping(value = "/apply", method = RequestMethod.GET)
public String getTourApplyPage(HttpServletRequest req, ModelMap map) {
UserDetailTO userDetail = (UserDetailTO) req.getSession().getAttribute(AppConstants.LOGGEDIN_PARAM);
Long employeeId = userDetail.getUserType() == 1 ? userDetail.getEmployeeId():userDetail.getUserId();
if (employeeId == 0) {
req.setAttribute(AppConstants.MSG_PARAM, "Invalid employee Id.");
return userDetail.getUserType() == 1 ? AppConstants.PIS_MESSAGE : AppConstants.ADMIN_PIS_MESSAGE;
}
...
}
There can be better approaches to set userDetail object inside HttpSession but I had a restriction to not change this implementation (Point 1).
Can it possible to change getting a better implementation for getting a userDetail object from HttpSession (Point 2)?
Is it possible to write a better implementation for getting a userDetail object from httpSession?
Working at such a high level of abstraction, as controllers are at, you don't necessarily need to inject neither an HttpServletRequest nor an HttpSession.
You can make your controller session-scoped and inject a session-scoped bean there. The bean can hold a userDetails and a message for failed validations.
#RestController
#Scope("session")
public class Controller {
#Autowired
private SessionDetails details;
#PostMapping(path = "/validate")
public void validate() {
details.setUserDetails(...);
}
#GetMapping(path = "/apply")
public String apply() {
final UserDetailTO userDetails = details.getUserDetails();
...
}
}
#Component
#Scope("session")
class SessionDetails {
private String message;
private UserDetailTO userDetails;
// getters & setters
}
Related
I`m creating my first Spring blog and trying to set logged user full name in navbar. Unfortunately sec:authentication="name" gives me user email and ${user.fullname) does not render anything. I figured out to put following code inside in an Article controller
#GetMapping("/")
public String index(Model model) {
List<Article> articles = this.articleRepository.findAll();
Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if ("anonymousUser".equals(currentUser.toString())) {
model.addAttribute("username","GUEST");
} else {
UserDetails user = (UserDetails) currentUser;
String name = this.userRepository.findByEmail(user.getUsername()).getFullName();
model.addAttribute("username",name);
}
model.addAttribute("view", "home/index");
model.addAttribute("articles", articles);
return "base-layout";
}
And it worked. However i want to use it everywhere and now I can use it only in "/". Please advice how can I modify the code, so I`ll be able to use username in all templates.
You should create a class annotated with #ControllerAdvice which indicates the annotated class assists your Controller. Then create a method #ModelAttribute which will set the common attributes, shared by all (or most) of your controllers.
For example:
#ControllerAdvice
public class UserControllerAdvice {
#ModelAttribute
public void addUserAttribute() {
Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if ("anonymousUser".equals(currentUser.toString())) {
model.addAttribute("username","GUEST");
} else {
UserDetails user = (UserDetails) currentUser;
String name = this.userRepository.findByEmail(user.getUsername()).getFullName();
model.addAttribute("username",name);
}
}
Based on my understanding, there are a number of different ways to retrieve the authenticated username in Spring Security.
I'm currently grabbing the username by included the Principal as a controller method argument:
#RequestMapping(value = "/dashboard", method = RequestMethod.GET)
public ModelAndView displayHomePage(ModelAndView modelAndView, Principal principal) {
modelAndView.addObject("email", principal.getName());
// Render template located at src/main/resources/templates/dashboard.html
modelAndView.setViewName("dashboard");
return modelAndView;
}
Does Spring Security offer an easy way for me to store the User object into the session so it can be easily retrieved by any controller method?
I want to avoid performing a DB lookup each time:
// Lookup user in database by e-mail
User user = userService.findUserByEmail(principal.getName());
I'm using Spring Security 4.2.
Spring Security provides you with a static method for quickly and easy access:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
Or
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = user.getUsername();
Maybe you would like do this in a base abstract class
public abstract class BaseController {
protected User getCurrentUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
...
public YourController extends BaseController {
...
}
Update
If you want to store the current authenticated user in session, then you need store only first time in a object as suggested by #gkatzioura.
#Component
#Scope("session")
public class MySessionInfo {
private User user;
protected User getCurrentUser() {
if (user == null) {
user = userService.findUserByEmail(SecurityContextHolder.getContext().getAuthentication().getPrincipal().getName());
}
return user;
}
}
You can inject this bean in yours controllers like
#Autowired
private MySessionInfo mySessionInfo;
You must take care about cases when user is not logged, but this is another problem.
You can always use the methods that spring security provides to get basic information such as name, authorities and everything provided by the Authentication.class.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
authentication.getAuthorities();
authentication.getName();
But if you want more information, using a session bean to store the information is also a good idea.
#Component
#Scope("session")
public class UserInfo { .. }
I'm trying to figure out the best way to handle httpsession in combination with pure EJB web services. I have created a utility class and I have a controller class. Where is the best place to instantiate a utility class inside the controller class? The plan is that each user that visits/calls a webservice i.e. using the website the first time should have a httpsession object assigned to them:
public class Utility {
#Resource
private WebServiceContext wsContext;
public MessageContext mc = wsContext.getMessageContext();
public HttpSession getSession(){
return ((HttpServletRequest)mc.get(MessageContext.SERVLET_CONTEXT)).getSession(true);
}
}
#Path("controller")
#Stateless
public class ControllerEJB {
#POST
public void registerUser(
#QueryParam("fornamn") String fornamn,
#QueryParam("efternamn") String efternamn,
#QueryParam("epost") String epost,
#QueryParam("epost2") String epost2,
#QueryParam("password") String password
){
User user = new User();
user.setEmail(epost);
user.setPassword(password);
user.setFornamn(fornamn);
user.setEfternamn(efternamn);
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public String firstMethod(){
User user = new User();
user.setEmail("sam.gholizadeh");
return "unfisnihedmethod";
}
}
I've followed this tutorial http://docs.oracle.com/cd/E12839_01/web.1111/e13734/stateful.htm but as metioned earlier I'm not sure how and where to implement the logic that keeps track if a visitor has been assigned a session id or not.
Edit: Should the controller class be stateful or stateless?
Since you are using JAX-RS, it's even easier to directly get the HttpServletRequest using JAX-RS:
#Path("controller")
#Stateless
public class ControllerEJB {
#POST
public void registerUser(
#QueryParam("fornamn") String fornamn,
#QueryParam("efternamn") String efternamn,
#QueryParam("epost") String epost,
#QueryParam("epost2") String epost2,
#QueryParam("password") String password,
#Context HttpServletRequest request){
HttpSession session = request.getSession(true);
...
}
}
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 want to read a domain object (UserVO) from session scope.
I am setting the UserVO in a controller called WelcomeController
#Controller
#RequestMapping("/welcome.htm")
public class WelcomeController {
#RequestMapping(method = RequestMethod.POST)
public String processSubmit(BindingResult result, SessionStatus status,HttpSession session){
User user = loginService.loginUser(loginCredentials);
session.setAttribute("user", user);
return "loginSuccess";
}
}
I am able to use the object in jsp pages <h1>${user.userDetails.firstName}</h1>
But I am not able to read the value from another Controller,
I am trying to read the session attribute as follows:
#Controller
public class InspectionTypeController {
#RequestMapping(value="/addInspectionType.htm", method = RequestMethod.POST )
public String addInspectionType(InspectionType inspectionType, HttpSession session)
{
User user = (User) session.getAttribute("user");
System.out.println("User: "+ user.getUserDetails().getFirstName);
}
}
The code you've shown should work - the HttpSession is shared between the controllers, and you're using the same attribute name. Thus something else is going wrong that you're not showing us.
However, regardless of whether or not it works, Spring provides a more elegant approach to keeping your model objects in the session, using the #SessionAttribute annotation (see docs).
For example (I haven't tested this, but it gives you the idea):
#Controller
#RequestMapping("/welcome.htm")
#SessionAttributes({"user"})
public class WelcomeController {
#RequestMapping(method = RequestMethod.POST)
public String processSubmit(ModelMap modelMap){
User user = loginService.loginUser(loginCredentials);
modelMap.addtAttribute(user);
return "loginSuccess";
}
}
and then
#Controller
#SessionAttributes({"user"})
public class InspectionTypeController {
#RequestMapping(value="/addInspectionType.htm", method = RequestMethod.POST )
public void addInspectionType(InspectionType inspectionType, #ModelAttribute User user) {
System.out.println("User: "+ user.getUserDetails().getFirstName);
}
}
However, if your original code isn't working, then this won't work either, since something else is wrong with your session.
#SessionAttributes works only in context of particular handler, so attribute set in WelcomeController will be visible only in this controller.
Use a parent class to inherit all the controllers and use SessionAttributes over there. Just that this class should be in the package scan of mvc.
May be you have not set your UserVO as Serializable.