Alper Akture
Alper Akture

Reputation: 2465

Can you use SpEL in @PreAuthorize referencing a instance property?

Is there a way to use a local variable in a class (authorizedRoles below), that is set with all the roles to grant access to an endpoint for the hasAnyRole value? For example, I want to have a list of roles, defined in a config, and populate the hasAnyRole in @PreAuthorize like so:

@Controller("myController")
public class MyController {
private String authorizedRoles;

@Autowired
public MyController(ObjectMapper objectMapper, @Value("#{'${security.authorized-roles}'.split(',')}") String authorizedRoles) {
    this.objectMapper = objectMapper;
    this.request = request;
    this.authorizedRoles = authorizedRoles;
}

@RequestMapping(value = "/id", produces = { "application/json" }, consumes = { "application/json" }, method = RequestMethod.POST)
@PreAuthorize("hasAnyRole('#myController.authorizedRoles')")
public ResponseEntity<IdResponse> idPost(@RequestBody IdRequest body) {
  ...
}

Upvotes: 2

Views: 3180

Answers (1)

Gary Russell
Gary Russell

Reputation: 174729

You can't access a private field that way with SpEL; you need to add public String getAuthorizedRoles() and SpEL will call it when you refer to the authorizedRoles property. SpEL is aware of JavaBean conventions.

EDIT

hasAnyRole() takes a String[].

@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class So59419703Application extends GlobalAuthenticationConfigurerAdapter {

    public static void main(String[] args) {
        SpringApplication.run(So59419703Application.class, args);
    }

    @Autowired
    private Foo foo;

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            SecurityContext ctx = SecurityContextHolder.createEmptyContext();
            ctx.setAuthentication(new UsernamePasswordAuthenticationToken("foo", "bar"));
            SecurityContextHolder.setContext(ctx);
            System.out.println(foo.bar());
        };
    }

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("foo").password("bar").roles("baz");
    }

    public interface Foo {

        String bar();

        String[] getRoles();

    }

    @Component("foo")
    public static class FooImpl implements Foo {

        private final String[] roles = StringUtils.commaDelimitedListToStringArray("admin,user,baz");

        @Override
        @PreAuthorize("hasAnyRole(@foo.roles)")
        public String bar() {
            return "authOk";
        }

        @Override
        public String[] getRoles() {
            return this.roles;
        }

    }

}
authOk

Upvotes: 2

Related Questions