Spring Security + MVC:相同的@RequestMapping,不同的@Secured

时间:2013-08-01 13:41:42

标签: java spring spring-mvc spring-security

假设我们使用Spring MVC和Spring Security配置了API端点。我们希望能够处理成对的@RequestMapping和@Secured注释,其中唯一的@Secured注释值因配对而异。这样,我们就可以根据同一请求的安全规则返回不同的响应主体。

这可以避免直接在方法体中检查安全规则,从而使我们的代码更易于维护。

有一个不成功的例子,我们想做的就是:

@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
    }
}

我们怎样才能做到这一点? 如果可以这样做:如何为同时具有ROLE_A和ROLE_B的用户管理优先级?

2 个答案:

答案 0 :(得分:4)

假设您正在使用Spring 3.1(或更高版本)以及RequestMappingHandlerMapping(和RequestMappingHandlerAdapter),您可以扩展请求映射机制。您可以通过创建自己的RequestCondition接口实现并扩展RequestMappingHandlerMapping来构建基于方法的@Secured注释来构建它。

您需要在RequestMappingHandlerMapping上覆盖'getCustomMethodCondition'方法,并基于Method和@Secured注释的存在构造您的RequestCondition的自定义实现。然后在将传入请求与方法匹配时考虑所有这些信息。

相关答案(虽然不是针对@Secured注释的具体但机制相同)也可以找到herehere

答案 1 :(得分:3)

我认为你不能在spring-mvc中做到这一点,因为两个路由完全相同@RequestMapping@Secured)没有被spring-mvc的路由引擎考虑在内。最简单的解决方案是:

@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;
    }
}

但是,我会更改您的设计,因为每个角色的响应不同,为什么没有2个单独的请求映射与略有不同的URL?如果在某些时候您同时拥有角色A和B的用户,则不能让用户选择要获取的响应(例如,想想LinkedIn的公共和私人配置文件)