Issue
Right now, with Spring Security's HttpSecurity
, we're able to restrict wildcard paths to specific roles/authorities:
.mvcMatchers(POST, "/users").hasAuthority("create:users")
.mvcMatchers(PUT, "/users/{id}").hasAuthority("update:users")
is there an easy way to do:
.mvcMatchers(POST, "/{whateverGoesHere}").hasAuthority("create:${whateverGoesHere}")
.mvcMatchers(PUT, "/{whateverGoesHere}/{id}").hasAuthority("update:${whateverGoesHere}")
?
It doesn't have to be a solution using the configure(HttpSecurity http)
API specifically, I'm just looking for an easy way to generify authorization rules for multiple REST entities at once.
Solution
This is obviously a more advanced scenario, to say the least. However, improvements in Spring Security 5.5 have introduced the new AuthorizationManager
interface and the http.authorizeHttpRequests()
method for configuring authorization rules that utilize it. See The AuthorizationManager in the reference docs for more info. It is extremely powerful! I believe this is probably the best option for your use case.
There are numerous implementations available in Spring Security that can be used to build composite and/or delegating implementations. Here's an example that uses your convention:
public final class ResourceAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
private final String action;
public ResourceAuthorizationManager(String action) {
this.action = action;
}
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
AuthorizationManager<RequestAuthorizationContext> delegate =
AuthorityAuthorizationManager.hasAuthority(createAuthority(context));
return delegate.check(authentication, context);
}
private String createAuthority(RequestAuthorizationContext context) {
String resource = context.getVariables().get("resource");
return String.format("%s:%s", this.action, resource);
}
}
The action
can be create
, read
, update
, delete
or anything you like as part of your authority string. This implementation relies on URI variables provided through the RequestAuthorizationContext
. As it happens, there's an existing implementation (RequestMatcherDelegatingAuthorizationManager
) that handles that scenario. It is actually the one handling .mvcMatchers()
authorization rules in the Spring Security DSL. Here's an example that uses it to delegate to the convention-based AuthorizationManager
above:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests
.mvcMatchers(HttpMethod.POST, "/{resource}").access(new ResourceAuthorizationManager("create"))
.mvcMatchers(HttpMethod.PUT, "/{resource}/{id}").access(new ResourceAuthorizationManager("update"))
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
Answered By - Steve Riesenberg
Answer Checked By - Candace Johnson (JavaFixing Volunteer)