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.