Spring MVC validator: validate using current session object - java

I want to validate my MyAccount form using a custom Spring Validator.
Basic validation rules are no problem.
I now have this requirement, which seems pretty obvious and common:
If the user (currently logged in) changes his username, I'll have to check if it's already in use. If it's not been changed, I'm fine (it would definitely be in use when checked, that's why I don't want to check it in that case). The problem is, that the validator is a Spring managed Singleton and I don't seem to have access to the current session (where I store my login context - i.e. not Spring Security). But I need the currently logged in user, in order to compare its e-mail with the one entered in the form.
This is my scenario, but the question is actually about how to validate using an object from the user's session, in general.
The ways I was thinking about solving this:
Do only basic validation in the Validator and do the rest, that I need the session for, in the Controller. Doesn't seem to be a nice solution.
#RequestMapping(value="/myaccount", method=RequestMethod.GET)
public String myAccount(#Valid MyAccountForm form, BindingResult result, HttpSession session)
{
boolean hasUsernameChanged = // check using session
if (hasUsernameChanged && CustomerService.customerAlreadyExists(form.getUsername()))
result.rejectValue("username", "my.error.code");
if (result.hasErrors()) {
// handle errors
} else {
// proceed
}
}
Adding a second validate method to the Validator like so
public void validateWithCurrentCustomer(Customer current) {
...
}
and call it explicitly from the controller, with the appropriate object. Not much better, but at least the validation logic is in one class, although separated in two methods, one of which is not standard.
Not having the Validator a Spring managed (singleton) bean, but create it everytime in the initBinder method. Instead of:
#Autowired
public MyAccountController(MyAccountFormValidator validator)
{
this.validator = validator;
}
#InitBinder
protected void initBinder (WebDataBinder binder)
{
binder.setValidator(validator);
}
do
#InitBinder
protected void initBinder (WebDataBinder binder, HttpSession session)
{
Customer current = ...// get from session
binder.setValidator(new MyAccountFormValidator(current));
}
But here, the problem is, that since the validator is not Spring managed, it's hard to get a service (like CustomerService) injected into the validator, for checking if an e-mail is available or not.
Am I missing something, any other ways to achieve what I want? This seems to be a common problem, but I couldn't find any pattern on Google or SO.
Thanks for your hints.

You may try to access any Spring bean from your validator. Maybe this answer can help.
Where should I place validation code that requires access to database?
Basically, you can make a SpringBeanUtil singleton, which gives you access to any bean you want.

Related

Spring REST Api -- access User details in repository

The setup of the RESPApi project is:
SpringBoot
Spring's OAuth2
In the project we have many clients, so SQL queries almost always have "... and clientId = ?" in the where clause.
We store clientId in the SecurityContext with other user details (we extend Spring's User class).
The question is: how to get the User object in the #Repository?
Possible solutions we can think of:
In every repository implementation add
SecurityContextHolder.getContext().getAuthentication()
cast the result to our custom UserDetails implementation and use it.
Cons: somehow I feel there's a better solution.
Add #AuthenticationPrincipal annotated parameters to the controllers and then pass the parameters to the service layer and then to the repository layer.
Cons: passing the paremeter though 2 layers only to obtain clientId doesn't seem reasonable.
I thought about #Autowired paramter MyUser user in the #Repository class. The first try was to create #Configuration annotated class in which there will be a method
#Bean
public MyUser getUser() {
SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
return (MyUser) authentication.getPrincipal();
}
}
return null;
}
But the bean is null and I cannot use it.
For now we've ended up with solution nr 1 but I feel there must be a better way.
Any ideas how to solve this problem?
If you're using Spring Data (or have the time to switch to using it), you can use the SecurityEvaluationContextExtension and use principal directly in your queries:
https://stackoverflow.com/a/29692158/1777072
If not, you could hide the static access if it offends (or if you want more control over it changing in future):
#Component
public class AuthenticationHelper {
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
Then inject that class into your Repository.
Or your Service. That's probably a better fit than the Repository.
I like to keep Repositories dumb (ultimately using Spring Data to avoid writing them entirely).
And I like to think of Services as being separated out of the web layer, running on separate boxes (even if they aren't). In that situation, you would never pass the Authentication details over HTTP from Controller to Service. The service would obtain authentication details for itself, rather than just trusting what the web layer sent it.
So I think the Service should get the details itself, rather than the Controller passing them through.
Your bean is null because by default beans are singleton and they are created when the application starts, and as you can imagine, you are not going to have a SecurityContext at that point.
Try declaring your bean with request scope, in this way:
#Bean
#Scope(value=WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.TARGET_CLASS)
public MyUser getUser() {
.....
}

Spring security check if user has access to mentioned url

I have started using spring security, and after a lot of research I am not able to find an answer for:
If I explicitly want to check if user A have access to stuff B. I can check this with JSP tag support Spring Security - check if web url is secure / protected like
<sec:authorize url="stuff/B">
But what if I want to check the same thing in the controller(java class). I am not finding any spring function here to check if a login user has access to mentioned url(https://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html)
Hint from the javadoc:
to use this tag there must also be an instance of WebInvocationPrivilegeEvaluator in your application context. If you are using the namespace, one will automatically be registered. This is an instance of DefaultWebInvocationPrivilegeEvaluator,"
And in the javadoc of DefaultWebInvocationPrivilegeEvaluator, we can see a isAllowed method that should do the job:
// privilegeEvaluator is a WebInvocationPrivilegeEvaluator "autowired"
boolean allowed = privilegeEvaluator.isAllowed("/stuff/B", yourAuthentication);
Why not to use annotations like this:
#PreAuthorize("hasRole('ROLE_USER')")
public void create(Contact contact);
Annotations are standard way for Spring 3+
You are looking at the right place, the link you attached mentions what you need. Since you want access-control on your controller and check per user (not role) you can use the '#PreAuthorize' annotation with "hasPermission" expression or similar.
You could check here for expression-based access control and here for examples of custom security expression example in case you want to customize the solution.
1) First we need to know whether the user may enter the URL at all. This can be very easily achieved using WebInvocationPrivilegeEvaluator.
privilegeEvaluator.isAllowed(contextPath, url, "GET", currentUser);
2) Now we need to identifying whether the user may access the handler method
private boolean isAllowedByAnnotation(Authentication currentUser, HandlerMethod method) {
PreInvocationAuthorizationAdvice advice = new ExpressionBasedPreInvocationAdvice();
PreInvocationAuthorizationAdviceVoter voter = new PreInvocationAuthorizationAdviceVoter(advice);
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PrePostInvocationAttributeFactory factory = new ExpressionBasedAnnotationAttributeFactory(expressionHandler);
PrePostAnnotationSecurityMetadataSource metadataSource = new PrePostAnnotationSecurityMetadataSource(factory);
Class<?> controller = method.getBeanType();
MethodInvocation mi = MethodInvocationUtils.createFromClass(controller, method.getMethod().getName());
Collection<ConfigAttribute> attributes = metadataSource.getAttributes(method.getMethod(), controller);
return PreInvocationAuthorizationAdviceVoter.ACCESS_GRANTED == voter.vote(currentUser, mi, attributes);
}
We can create a custom PermissionEvaluator and use
hasPermission(Authentication authentication, Object domainObject,
Object permission).
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
final DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
return expressionHandler;
}
#Bean
public aclServiceImpl aclService() {
final AclServiceImpl mutableAclService = new AclServiceImpl
(authorizationStrategy(), grantingStrategy());
return mutableAclService;
}
AclServiceImpl is the implementation of MutableAclService
The most obviously useful annotation is #PreAuthorize which decides whether a method can actually be invoked or not. For example (from the"Contacts" sample application)
#PreAuthorize("hasRole('USER')")
public void create(Contact contact);
which means that access will only be allowed for users with the role "ROLE_USER". Obviously the same thing could easily be achieved using a traditional configuration and a simple configuration attribute for the required role. But what about:
#PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);
Here we’re actually using a method argument as part of the expression to decide whether the current user has the "admin"permission for the given contact. The built-in hasPermission() expression is linked into the Spring Security ACL module through the application context.
For More Detailed Explanation please refer this Link

Spring MVC3 #SessionAttributes and #ModelAttribute will automatically get value from request

I’m trying to develop a Spring MVC application, now I encounter a question. When login successful I add the User entity to session and call http://localhost:8080/user to get the session user. Everything is OK here. But if I call the URL like this http://localhost:8080/user?username=testuser then the session user's username will change to testuser. What should I do that just get current user from session?
The code likes below
Entity:
#Entity
public class User {
private Long id;
private String username;
// ...Getter and Setter...
}
Controller:
#Controller
#RequestMapping("user")
#SessionAttributes("current_user")
public class UserController {
#RequestMapping(method=RequestMethod.GET)
#ResponseBody
public User testSession(#ModelAttribute("current_user") User user) {
return user;
}
}
Response of http://localhost:8080/user
[{"id":1,"username":"aaa111"}]
Response of http://localhost:8080/user?username=testuser; it should be same as above, but is
[{"id":1,"username":"testuser"}]
The #SessionAttributes annotation isn't intended for this. Its intend is to store objects in the session during http requests. Imagine a lengthy database call to retrieve an object you don't want to retrieve this object each time but probably reuse an existing one. The object is to be intended to be used as a #ModelAttribute, this annotation indicates that you want to use this object for binding (i.e. you have a form to change attributes of the object). When you are finished with the editing of the object you should make this clear by calling setComplete() on the SessionStatus object. See also here.
You want to store an object in the session and retrieve it when you need it. For this use the HttpSession in the normal way of calling setAttribute and getAttribute. To obtain the current HttpSession you can simply add a method argument of the type HttpSession and it will be injected for you. (See here for a list of supported method arguments).
public void myRequestHandlingMethod(HttpSession session) {
User currentUser = (User) session.getAttribute("currentUser");
}
Or as you are already using Spring you could use the WebUtils for convenience. You can use the getSessionAttribute or getRequiredSessionAttribute methods to obtain the value from the session.
public void myRequestHandlingMethod(HttpServletRequest request) {
User currentUser = (User) WebUtils.getSessionAttribute("currentUser", request)
}
Another solution would be to extend Spring MVC. Spring MVC uses a HandlerMethodArgumentResolver to handle all the different types of method arguments. This mechanism is pluggable. You could create an annotation #CurrentUser and create a CurrentUserHandlerMethodArgumentResolver that will retrieve the user from the session and injects it in that place. You could then simply add your current user to your method signature.
public void myRequestHandlingMethod(#CurrentUser User user) { ... }
Configure the custom argument resolver
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.yourcomponany.app.web.CurrentUserHandlerMethodArgumentResolver />
</mvc:argument-resolvers>
</mvc:annotation-driven>
It also looks like you are rolling your own security framework, which I would advice against. Instead I would suggest using Spring Security instead. Advantage of this is that this provides integration with the Servlet API allowing for retrieval of the current Principal by either doing it yourself (request.getUserPrincipal()) or simply adding a method argument of the type java.security.Principal. It also comes with a custom HandlerMethodArgumentResolver which allows you to obtain the current Spring Security Authentication object.
try to get session value in controller from servlet request like below
#Controller
#RequestMapping("user")
#SessionAttributes("current_user")
public class UserController{
#RequestMapping(method=RequestMethod.GET)
#ResponseBody
public User testSession(HttpServletRequest request){
//false means do not create new session
HttpSession session = request.getSession(false);
return session != null?session.getAttribute("current_user"):null;
}
}

Spring MVC 3 Controller annotation for onBind() - how to?

I am upgrading from Spring 2.5 to Spring 3.2. I have a MVC Controller that previously extended CancellableFormController. It declared,separately, initBinder() and onBind() methods. I have refactored the Controller to use #Controller annotation, and switched the overridden initBinder() method to use Spring MVC 3 annotation #initBinder.
My specific question is, in Spring MVC 3, upgrading from Spring 2.5, how to refactor the overridden onBind() method to use an annotation equivalent? The signature of the existing method is:
#Override
protected void onBind(HttpServletRequest request, Object command, BindException errors) throws Exception {
MyCommand cmd = (MyCommand) command;
....
}
I thought about using #initBinder() and put the code previously in onBind() inside this annotated method. But my confusion here is:
Doing this, would the code be called at the same time in the overall framework process as before?
How to get a handle on the Command object from #initBinder annotated method.
Can I just declare it as another parameter in the signature of the method and Spring MVC framework will ensure I get a copy? It seems that in the old onBind() method the Command object has already been created (by formBackingObject method). Can I safely assume that this is also the case when using #initBinder method?
Thank you anyone for some insights. I am trying to get up to speed with the Spring MVC process flow!
My existing #initBinder method signature is something like:
#InitBinder
protected void initBinder(WebDataBinder binder) {
// register custom editor here.. etc
}
I am hoping I can do something like:
#InitBinder
protected void initBinder(WebDataBinder binder, MyCommand cmd) {
// register custom editor here.. etc
}
Is this the standard best practice approach for upgrading cancellableformcontroller onBind() method using annotations?
Attempt based on answer marked correct, still not working:
#InitBinder("myCommand")
protected void onBind(WebDataBinder binder) throws Exception {
MyCommand cmd = (MyCommand) binder.getTarget();
.... // do whatever was required here...
}
Work around
Please see my comments below to zeroflag. Create a private method with same logic as contained in onBind(), and then after validation of command object in onSubmit() annotated method (POST / GET) make a callout to the now defunct onBind() method passing your Command object in as a parameter. Something like the following:
#RequestMapping(method=RequestMethod.POST)
public ModelAndView onSubmit(#ModelAttribute("myCommand") MyCommand cmd, BindingResult result, <any other params> {
new MyCommandValidator().validate(cmd, result);
if (result.hasErrors()) {
return new ModelAndView("context");
}
onBind(cmd, <any other params>);
... // do business stuff
}
It appears to be an ugly workaround that was ok for my particular case.
You can access the command object with binder.getTarget(). #InitBinder is called for every model parameter of the controller method. Let's assume the following code
#RequestMapping("/somePath")
public String myMethod(#ModelAttribute("user") User user,
#ModelAttribute("info") Info info) {
Then your initBinder method is called at least twice: For the Userobject and for the Infoobject. To have it called only for a specific object add the model name:
#InitBinder("user")
That way it will only be called for the Userobject. Be aware, though, that the method might still be called more than once, the first time even with the target object being null.
If you want to ensure that some fields are not set automatically, then you can use setDisallowedFields() in your initBindermethod.
If you want to do some validation then let JSR 303 (Bean Validation) do that job.
Depending on what you want to do an #ModelAttribute method in your controller could be a solution:
#ModelAttribute("user")
public User createUser(HttpServletRequest request /*or whatever*/) {
User user = new User();
//check some parameters and set some attributes
return user;
}
Creates a user object before binding happens.
One last solution can be a message or type converter for your command object, that creates an object from the request. That's actually similar to the example above, but independent of the controller and there is no binding for an object created with a converter.

Spring MVC Session Attribute Access

Is there any way under spring 3.0 to access the HttpSession without including it in the method signature? What I really want to do is be able to pass in values from an HttpSession that CAN BE null.
Something like this:
#RequestMapping("/myHomePage")
public ModelAndView show(UserSecurityContext ctx) {}
instead of this:
#RequestMapping("/myHomePage")
public ModelAndView show(HttpSession session) {
UserSecurityContext ctx = (UserSecurityContext) session.getAttribute("userSecurityCtx");
}
The #SessionAttribute annotation mentioned by #uthark is not suitable for this task - I thought it was too, but a bit of reading shows otherwise:
Session attributes as indicated using
this annotation correspond to a
specific handler's model attributes,
getting transparently stored in a
conversational session. Those
attributes will be removed once the
handler indicates completion of its
conversational session. Therefore, use
this facility for such conversational
attributes which are supposed to be
stored in the session temporarily
during the course of a specific
handler's conversation.
For permanent session attributes, e.g.
a user authentication object, use the
traditional session.setAttribute
method instead. Alternatively,
consider using the attribute
management capabilities of the generic
WebRequest interface.
In other words, #SessionAttribute is for storing conversation MVC-model objects in the session (as opposed to storing them as request attributes). It's not intended for using with arbitrary session attributes. As you discovered, it only works if the session attribute is always there.
I'm not aware of any other alternative, I think you're stuck with HttpSession.getAttribute()
You can use a RequestContextHolder:
class SecurityContextHolder {
public static UserSecurityContext currentSecurityContext() {
return (UserSecurityContext)
RequestContextHolder.currentRequestAttributes()
.getAttribute("userSecurityCtx", RequestAttributes.SCOPE_SESSION));
}
}
...
#RequestMapping("/myHomePage")
public ModelAndView show() {
UserSecurityContext ctx = SecurityContextHolder.currentSecurityContext();
}
For cross-cutting concerns such as security this approach is better because you doesn't need to modify your controller signatures.
Yes, you can.
#SessionAttributes("userSecurityContext")
public class UserSecurityContext {
}
#RequestMapping("/myHomePage")
public String show(#ModelAttribute("userSecurityContext") UserSecurityContext u) {
// code goes here.
}
See for details:
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/SessionAttributes.html
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html

Categories