Currently I am writing my own custom @PreAuthorize annotation. My case is as follows,

  1. I am running my authorization KeyCloak server that holds the user details, roles and permission
  2. After Validation, I have stored the permission details in GrantedAuthority as follows

"{rsname}:GET", "{rsname}:POST" ...

KeyCloak JWT permission structure:

"authorization": {
    "permissions": [
      {
        "scopes": [
          "GET",
          "DELETE",
          "POST"
        ],
        "rsid": "6ae9895f-3766-464f-82c4-44f598ec2a93",
        "rsname": "record"
      }
    ]
  }

while using @PreAuthorize annotation in controller instead of hardcoding the resource name and scopes, we have to generalize it by getting the details from application.yml we have achieved it as follows,

application.yml:

auth:
  data:
    name1: record
    name2: device 

Property Detail Component class:

@ConfigurationProperties(prefix = "auth")
@Component
public class SecurityProperty {
    private Map<String, String> data;
    ....
}

Controller:

@RequestMapping (method = RequestMethod.GET,value = "/api/records",
        produces = {"application/json"})
@PreAuthorize ("hasAuthority (@securityProperty.getData(). get('name1') "
    + "+ ': GET')")
ResponseEntity<List<SomeDTO>> getRecords() {
        ...Some Logic
}

@RequestMapping(method = RequestMethod.GET,value = "/api/devices",
        produces = { "application/json" })
@PreAuthorize("hasAuthority(@securityProperty.getResources().get('name2') "
    + "+ ':GET')")
ResponseEntity<List<SomeDTO>> getDevices() {
        ...Some Logic
}

So far this is working fine. Since we are creating big project, we don't want to write this lengthy @PreAuthorize(XXXX) annotation so decided to create custom annotation that uses the @PreAuthorize. We have created @CustomPreAuthorize as below

@Retention(RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@PreAuthorize("hasAuthority(@securityProperty.getResources().get(#resource)"
        + ".concat(':GET'))")
public @interface CustomPreAuthorize {
    String resource();
}

And used this in controller

@RequestMapping (method = RequestMethod.GET,value = "/api/devices",
        produces = {"application/json"})
@CustomPreAuthorize (resource = "name2")
ResponseEntity<List<SomeDTO>> getDevices() {
        ...Some Logic
}

Issue:

  1. When I used like this when the API is called I am getting the following error

Failed to evaluate expression 'hasAuthority(@securityProperty.getResources().get(#resource).concat(':GET'))"

  1. So far what I understood is like the resource and scope are not getting recognized in the @PreAuthorize annotation that mentioned in mu custom annotation level. Is it possible to read the values like this or is there any alternatives available?

Comment From: jzheaux

Thanks for getting in touch! It feels like this is a question that would be better suited to Stack Overflow. We prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add more detail if you feel this is a genuine bug.

Comment From: sridhar1495

@jzheaux This has been already posted in Stack Overflow but there is no reply from Spring Security team or anyone else. So thought this forum will help to get the quick solution.

Comment From: HashirLabs

I would add that the condition as mentioned by @sridhar1495 is expected to work but is not. Would be great if you can fix that. I see problem statement is very well explained. either take it as bug or a feature request.