8

What is the proper way to secure the @RequestBody with Spring Security?

For example: A User can have multiple Blogs and each Blog can have multiple Entrys. A user goes to save an entry to a certain blog and the request would come in like this:

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

Now, the incoming entry has a Blog, the user could have doctored up the request and chosen someone else's blog, effectively posting the entry to their blog. Though I could catch this in validation (query the persistence layer to verify that the Blog belongs to the logged in User) I feel that this should be handled by Spring Security. If so, how do I go about doing this?

Josh Johnson
  • 10,729
  • 12
  • 60
  • 83

1 Answers1

10

We had this kind of situation.

Here is the two solution. I did not like much

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

or

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

//In that case Spring will call your static isOk() method from Decision class. It should return boolean.

Spring injects Principal principal authorized object for the method, you do not have to worry about it. Enable @PreAuthorize annotation with

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

Second Using Aspect. Create 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;
} 

If you have aspect you can have more owning on runtime

Also refer to this ulr

Community
  • 1
  • 1
Elbek
  • 3,434
  • 6
  • 37
  • 49
  • 1
    After trying both approaches I was most happy with `@PreAuthorize`. For anyone else coming across this, securing the req body was somewhat complex for me and I had able to wire up a bean with some services. The EL changes slightly when calling a bean instance. Example: `@PreAuthorize("@decision.isOK(#entry.blog.id, principal)")` – Josh Johnson Oct 02 '12 at 00:44
  • 1
    I am happy that it helped :) `@PreAuthorize` is exactly for this purpose, while @Aspect is more general :) – Elbek Oct 02 '12 at 00:45
  • Referring method parameters from a Spring-EL in @PreAuthorize should be prefixed with a sharp ````#````. ````@PreAuthorize("Decision.isOK(#entry, #principal)")```` instead of ````@PreAuthorize("Decision.isOK(entry, principal)")````. – Saad Benbouzid May 09 '17 at 08:21