Affects: \<5.3.6>
When register a customer AccessDecisionVoter
in a project that using springboot 2.4.5, I found that HttpServletRequest
doesn't contain attribute UrlPathHelper.PATH_ATTRIBUTE
in AccessDecisionVoter.vote()
method.
public class AbacAccessVoter implements AccessDecisionVoter<FilterInvocation> {
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
@Override
public int vote(Authentication authentication, FilterInvocation object, Collection<ConfigAttribute> attributes) {
FilterInvocation fi = (FilterInvocation) object;
System.out.println(fi.getHttpRequest().getAttribute(UrlPathHelper.PATH_ATTRIBUTE)); // this code get result : null
return AccessDecisionVoter.ACCESS_GRANTED;
}
}
But HttpServletRequest
can get value in the Controller method, For example:
@Controller
@RequestMapping("/test")
public class TestController {
@GetMapping("/demo")
@ResponseBody
public String demo(HttpServletRequest request, Model model) throws Exception {
System.out.println(request.getAttribute(UrlPathHelper.PATH_ATTRIBUTE)); // this code can get result : /test/demo
return "true";
}
}
Comment From: poutsma
This is due to the fact that Spring Security, and any associated AccessDecisionVoter
instances, operate before Spring MVC gets invoked. Spring Security is implemented in terms of a Servlet Filter
, which are invoked before Servlet
instances, such as Spring MVC's DispatcherServlet
.
So this is expected behavior.
Comment From: litao11046
The backgourd for this issue is :
In spring boot 2.3, I used the method RequestMappingInfo.getMatchingCondition()
to match the request in my custom Voter
. But when upgrade to 2.4.*, it comes error “Expected lookupPath in request attribute”. The reason for the error is UrlPathHelper.getResolvedLookupPath()
this method will try to get UrlPathHelper.PATH_ATTRIBUTE
in request object.
I can avoid this error by setting this attribute explicitly, but I think this is not elegant.
Comment From: poutsma
Any idea what might be causing this change in behavior, @rstoyanchev ?
Comment From: rstoyanchev
@litao11046 this might be expected behavior that this attribute is not present, but what I really need to see is what you're trying to accomplish. What does the code that you have that fails look like? It's okay to show a representative sample instead of the actual code, but something that shows the set of steps in the voter that run into the problem.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: litao11046
I am sorry so late to reply. The function I want to accomplish is : I define a AccessDecisionVoter in my project using spring boot and spring security, and in this AccessDecisionVoter I have a function to match a request object. Different request objects will get different results by the AccessDecisionVoter. I'm using the belowing code to implement this function. ```` public boolean matches(HttpServletRequest request) { // in SpinngBoot 2.4.*, must have this code, or else will get a RuntimeException request.setAttribute("org.springframework.web.util.UrlPathHelper.PATH", request.getRequestURI());
if(createRequestMappingInfo().getMatchingCondition(request)!=null) {
return true;
}else {
return false;
}
}
private RequestMappingInfo createRequestMappingInfo() {
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(this.url);
if(StringUtils.isNotBlank(this.method)) {
builder.methods(RequestMethod.valueOf(this.method.toUpperCase()));
}
if(!CollectionUtils.isEmpty(this.params)) {
builder.params(this.params.stream().toArray(String[]::new));
}
if(!CollectionUtils.isEmpty(this.headers)) {
builder.headers(this.headers.stream().toArray(String[]::new));
}
if(StringUtils.isNotBlank(this.contentType)) {
builder.consumes(this.contentType);
}
if(!CollectionUtils.isEmpty(this.accepts)) {
builder.produces(this.accepts.stream().toArray(String[]::new));
}
return builder.build();
}
``
Comment From: rstoyanchev
The PATH_ATTRIBUTE happened to be set as a side effect of a internal Spring Framework component used by Spring Security to make decisions pattern matching. You can use UrlPathHelper#resolveAndCacheLookupPath
.