Gotcha: Migrating from Spring Security 3.2.x to Spring Security 4.x

Here is a simple gotcha to keep in mind while migrating to newer Spring Security version 4.x from Spring Security 3.2.x

What’s the problem?

The Spring security configuration expressions hasRole('role_name') and hasAuthority('role_name') are no longer the same.

The catch is: hasAuthority checks for any role name passed to the expression without prepending ‘ROLE_’ (which is the default role prefix), where as hasRole checks for any role name passed to the expression by prepending ‘ROLE_’ to the role name.

Below is the snapshot of the class definition for SecurityExpressionRoot for both the versions of Spring Security which defines the methods hasRole and hasAuthority.

springsec-gotcha

In Spring Security 3.2.x hasAuthority and hasRole are checking for the presence of given role name in getAuthoritySet() [The getAuthoritySet() retrieves the GrantedAuthority list for the user]

In Spring Security 4.x hasAuthority is invoking the API hasAnyAuthorityName passing the prefix as null whereas hasRole is invoking the API hasAnyAuthorityName passing the default prefix which is ‘ROLE_’ (the same has been highlighted in the image above)

There is another interesting API in Spring Security 4.x (again highlighted in the image above) called getRoleWithDefaultPrefix() as shown in image below:

springsec4-2.png

Interesting to see above how the role name is being prefixed with the role prefix.

What is the fix?

  1. Either you append all your roles with ‘ROLE_’ prefix. OR
  2. Use hasAuthority as the replacement for hasRole expression without the need for changing the role names OR
  3. Override the defaultRolePrefix with null or empty string so that the same expression hasRole works with the same role names. [I need to figure out how to do this. It should be possible because the setter for the property is public]
Advertisements

New @RequestParam annotations in Spring Boot 1.4 (Spring Framework 4.3)

Earlier in Spring/Spring Boot to Map a GET or POST or DELETE or any HTTP method request handler we would write something like below:

@RestController
@RequestMapping("/api/books")
public class BookAPIController {
  @RequestMapping
  public ResponseEntity<?> getBooks(){
  
  }
  
  @RequestMapping("/{book_id}")
  public ResponseEntity<?> getBook(@PathVariable("book_id") String bookId){
  
  }
  
  @RequestMapping(method = RequestMethod.POST)
  public ResponseEntity<?> addNewBook(@RequestBody Map<String, Object> requestBody){
	
  }
  
  @RequestMapping(method = RequestMethod.POST, value="/{book_id}")
  public ResponseEntity<?> editBook(@PathVariable("book_id") String bookId){
	
  }
  
  @RequestMapping(method = RequestMethod.DELETE, value="/{book_id}")
  public ResponseEntity<?> deleteBook(@PathVariable("book_id") String bookId){
	
  }
}

But with Springframework 4.3 and Spring Boot 1.4 (which now uses Springframework 4.3) we have some new annotations to map the HTTP methods to request handlers for the following HTTP methods: GET, POST, PUT, PATCH, DELETE. These new annotations are namely: @GetMapping, @PostMapping, @PutMapping, @PatchMapping, @DeleteMapping. So the above code now looks like:

@RestController
@RequestMapping("/api/books")
public class BookAPIController {
  @GetMapping
  public ResponseEntity<?> getBooks(){}
  
  @GetMapping("/{book_id}")
  public ResponseEntity<?> getBook(
    @PathVariable("book_id") String bookId
  ){}
  
  @PostMapping
  public ResponseEntity<?> addNewBook(
    @RequestBody Map<String, Object> requestBody
  ){}
  
  @PostMapping("/{book_id}")
  public ResponseEntity<?> editBook(
    @PathVariable("book_id") String bookId
  ){}
  
  @DeleteMapping("/{book_id}")
  public ResponseEntity<?> deleteBook(
   @PathVariable("book_id") String bookId
  ){}
}

These new annotations aid in improving the code readability and also reducing the annotation text to some extent