Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security + MVC : same @RequestMapping, different @Secured

Let say we have an API endpoint configured using Spring MVC and Spring Security. We would like to be able to handle pairs of @RequestMapping and @Secured annotations where the only @Secured annotation values differ from pair to pair. This way, we would be able to return a different response body depending on security rules for the same request.

This may allow our code to be more maintainable by avoiding to check for security rules directly into the method body.

With a not working example, here is what we would like to do :

@Controller
@RequestMapping("/api")
public class Controller {

    @Secured ({"ROLE_A"})
    @RequestMapping(value="{uid}", method=RequestMethod.GET)
    @ResponseBody
    public Response getSomething(@PathVariable("uid") String uid) {
        // Returns something for users having ROLE_A
    }

    @Secured ({"ROLE_B"})
    @RequestMapping(value="{uid}", method=RequestMethod.GET)
    @ResponseBody
    public Response getSomethingDifferent(@PathVariable("uid") String uid) {
        // Returns something different for users having ROLE_B
    }
}

How can we achieve this ? And if this can be done: How the priority should be managed for a user who has both ROLE_A and ROLE_B ?

like image 640
Julien Mellerin Avatar asked Aug 01 '13 13:08

Julien Mellerin


People also ask

Can two controllers have same request mapping?

You cannot. A URL can only be mapped to a single controller. It has to be unique.

Does Spring support multiple handler mapping?

@RequestMapping with Request Parameters element, you can have multiple handler methods handling requests to the same URL, but with different parameters.

Can we use Spring Security with Spring MVC?

To enable Spring Security integration with Spring MVC add the @EnableWebSecurity annotation to your configuration. Spring Security provides the configuration using Spring MVC's WebMvcConfigurer.

Which of these are valid usage of the @RequestMapping annotation in Spring MVC?

The @RequestMapping annotation can be applied to class-level and/or method-level in a controller. The class-level annotation maps a specific request path or pattern onto a controller. You can then apply additional method-level annotations to make mappings more specific to handler methods.


2 Answers

Assuming you are using Spring 3.1 (or up) together with the RequestMappingHandlerMapping (and RequestMappingHandlerAdapter) you can extend the request mapping mechanism. You can do this by creating your own implementation of the RequestCondition interface and extend the RequestMappingHandlerMapping to construct this based on the @Secured annotation on your method.

You would need to override the 'getCustomMethodCondition' method on the RequestMappingHandlerMapping and based on the Method and the existence of the @Secured annotation construct your custom implementation of the RequestCondition. All that information is then taken into account when matching incoming requests to methods.

Related answers (although not specific for @Secured annotations but the mechanism is the same) is also to be found here or here

like image 171
M. Deinum Avatar answered Oct 21 '22 20:10

M. Deinum


I don't think you can do this in spring-mvc, since both routes have exactly the same @RequestMapping (@Secured) is not taken into account by the route engine of spring-mvc. The easiest solution would be to do this:

@Secured ({"ROLE_A", "ROLE_B"})
@RequestMapping(value="{uid}", method=RequestMethod.GET)
@ResponseBody
public Response getSomething(@PathVariable("uid") String uid, Principal p) {
    // Principal p gets injected by spring
    // and you need to cast it to check access roles.
    if (/* p.hasRole("ROLE_A") */) {
        return "responseForA";
    } else if (/* p.hasRole("ROLE_B") */) {
        return "responseForB";
    } else {
        // This is not really needed since @Secured guarantees that you don't get other role.
        return 403;
    }
}

However, I would change your design, since the response is different per role, why not have 2 separate request mappings with slightly different URLs? If at some point you have users with role A and B at the same time, you can't let the user choose what response to get (think, for example, of the public and private profiles of LinkedIn)

like image 33
Alberto Avatar answered Oct 21 '22 21:10

Alberto