Spring保护@RequestBody

8

如何使用Spring Security保护@RequestBody的正确方式?

例如:一个User可以拥有多个Blog,每个Blog可以拥有多个Entry。当用户要保存某个博客的条目时,请求将会像这样:

@RequestMapping(value="/api/entry", method=RequestMethod.POST)
@ResponseBody
public Entry save(@Valid @RequestBody Entry entry) {
    this.entryService.save(entry);
    return entry;
}

现在,传入的entry对象有一个Blog属性,用户可能已经篡改了请求并选择了别人的博客,实际上将条目发布到他们的博客中。虽然我可以通过验证来捕获这种情况(查询持久性层以验证Blog是否属于已登录的User),但我认为这应该由Spring Security处理。如果是这样,请问我该如何操作?
1个回答

10

我们曾经遇到过这种情况。

这里有两个解决方案。但我并不是很喜欢它们。

@RequestMapping(value="/api/entry", method=RequestMethod.POST)
@ResponseBody
@PreAuthorize("#entry.author.name == principal.name)"
public Entry save(@Valid @RequestBody Entry entry, Principal principal) {
    this.entryService.save(entry);
    return entry;
} 
或者
@RequestMapping(value="/api/entry", method=RequestMethod.POST)
    @ResponseBody
    @PreAuthorize("Decision.isOK(entry, principal)")
    public Entry save(@Valid @RequestBody Entry entry, Principal principal) {
        this.entryService.save(entry);
        return entry;
    }

//在这种情况下,Spring将从Decision类调用您的静态isOk()方法。它应该返回布尔值。

Spring为该方法注入Principal主体授权对象,您不必担心它。 使用@PreAuthorize注释启用

<security:global-method-security pre-post-annotations="enabled" />

第二种方法是使用Aspect。创建Aspect。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Protector {
}

@Aspect
@Component
public class MyAspect {
   @Before("@annotation(com.xyz.Protector)")
   public void before(JoinPoint joinPoint) throws Throwable {
        //u can get method object from joinPoint object, 
        Method method = ((MethodSignature)joinPoint.getMethodSignature()).getMethod();
        //As long as you have Method object you can read the parameter objects (Entry and Principal) with reflection. 
        //So Compare here: If entry.getOwner().getId().equal(principal.getName()) blah blah blah  
    }
}

@RequestMapping(value="/api/entry", method=RequestMethod.POST)
@ResponseBody
@Protector
public Entry save(@Valid @RequestBody Entry entry, Principal principal) {
    this.entryService.save(entry);
    return entry;
} 

如果你使用aspect,你可以在运行时拥有更多的控制权

此外,请参考这个链接


1
在尝试了两种方法后,我最喜欢的是@PreAuthorize。对于其他遇到类似问题的人来说,保护请求体对我来说有些复杂,但我已经能够连接一些服务并创建一个bean。当调用bean实例时,EL会稍微改变。例如:@PreAuthorize("@decision.isOK(#entry.blog.id, principal)") - Josh Johnson
1
我很高兴它有帮助 :) @PreAuthorize 正是为此目的而设,而 @Aspect 则更为通用 :) - Elbek
在@PreAuthorize中引用Spring-EL的方法参数应该以井号“#”为前缀。例如使用@PreAuthorize("Decision.isOK(#entry, #principal)")而不是@PreAuthorize("Decision.isOK(entry, principal)") - Saad Benbouzid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接