Reputation: 581
I have a Spring Boot application that uses spring security for both authentication and authorisation. The authorisation phase, requires a custom AccessDecisionVoter on top of standard "authority based" authorisation. Here is my setup:
@Override
protected void configure(HttpSecurity http) throws Exception {
...
http.authorizeRequests()
.requestMatchers(getMatcherForAuthority1Urls())
.hasAuthority("AUTHORITY1")
.and().authorizeRequests()
.requestMatchers(getMatcherForAuthority2Urls())
.hasAuthority("AUTHORITY1")
.and().authorizeRequests()
.requestMatchers(getMatcherForAuthorities1and2Urls())
.hasAnyAuthority("AUTHORITY1", "AUTHORITY1");
http.authorizeRequests()
.regexMatchers(REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION).authenticated()
.accessDecisionManager(myAccessDecisionManager());
http.authorizeRequests()
.antMatchers(LOGIN_URL).permitAll();
}
@Bean
public AccessDecisionManager myAccessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters
= Arrays.asList(
new WebExpressionVoter(), // votes for authorities
myAccessVoter); // my custom voter
return new UnanimousBased(decisionVoters);
}
My expectations are that:
What is really happening when running the application, is that the custom voter is always executed, no matter the URL of the request. However, if I remove the initial part of the configure() method, so that no "hasAuthority()" authorisation is setup, then the custom voter only gets executed on the URLs that match the regex.
The same type of unwanted behaviour occurs if I use:
http.authorizeRequests().anyRequest().authenticated();
before setting up the custom AccessDecisionManager.
I am clearly making some mistakes in setting this up. Shouldn't the custom voter always run ONLY on the URLs that match the "REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION" pattern?
Thanks!
Upvotes: 0
Views: 624
Reputation: 2220
The AccessDecisionManager
bean that you register in .accessDecisionManager(...)
is shared across your entire Spring Security application, so anytime an authorization decision must be made, the same UnanimousBased
decision manager will be invoked, which in turn calls your custom voter.
So this:
authorizeRequests()
.requestMatchers(getMatcherForAuthorities1and2Urls())
.hasAnyAuthority("AUTHORITY1", "AUTHORITY1");
will call the same AccessDecisionManager
for the "matcherForAuthorities1and2Urls"
as REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION
:
authorizeRequests()
.regexMatchers(REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION).authenticated()
.accessDecisionManager(myAccessDecisionManager());
Similarly, including http.authorizeRequests().anyRequest().authenticated()
will register a matcher that matches every request, and asks the same AccessDecisionManager
to decide access.
Possible solution
Perhaps one way to achieve the desired behavior is to pass the REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION
into your custom voter via its constructor and use it to create a RequestMatcher
. Then, your voter can decide whether or not it will "vote" on a per-request basis:
public int vote(Authentication authentication, FilterInvocation fi,
Collection<ConfigAttribute> attributes) {
if(!regexPatternMatcher.matches(fi.getRequest())) {
return ACCESS_ABSTAIN;
}
// ...
}
Upvotes: 1