spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.34k stars 38.03k forks source link

@PreAuthorize Controller invalidates @Inject in @InitBinder ? [SPR-13580] #18157

Closed spring-projects-issues closed 5 years ago

spring-projects-issues commented 8 years ago

smallufo opened SPR-13580 and commented

In a simple controller :

@PreAuthorize("hasAuthority('USER')")
@Controller
@RequestMapping("/test")
public class TestController {

  private Logger logger = LoggerFactory.getLogger(getClass());

  @Inject
  private MyValidator myValidator;

  @InitBinder("myObj")
  private void initBinder(WebDataBinder binder) {
    logger.info("myValidator = {}", myValidator);
    binder.initDirectFieldAccess();
    binder.setValidator(myValidator);
  }

  @RequestMapping(value = "/doPost", method = RequestMethod.POST)
  public String doPost(MyObj myObj , BindingResult br ) throws IOException {
    logger.info("myObj = {} , bindingResult = {}" , myObj , br);
    return "redirect:/test/form";
  }
}

I noticed the injected validator is always null in the initBinder method , the logger is even null (and throws NPE) , this is weird.

If I totally remove the @InitBinder initBinder() method , the myValidator is available (not null) again in each method.

After eliminating many factors , I found the culprit is the @PreAuthorize("hasAuthority('USER')") . After removing this @PreAuthorize , everything works fine.

Is it a bug ? I feel it seems like a bug ... Does something conflicts with SpringSecurity and SpringValidation and SpringMVC ?

environments :

<spring.version>4.2.1.RELEASE</spring.version>
<springboot.version>1.3.0.M5</springboot.version>
<spring-security.version>4.0.2.RELEASE</spring-security.version>

Thanks in advanced.


Affects: 4.2.1

Reference URL: http://stackoverflow.com/questions/33174428/preauthorize-controller-invalidates-inject-in-initbinder

0 votes, 6 watchers

spring-projects-issues commented 8 years ago

Rob Winch commented

Thanks for the report smallufo!

This seems to be a general issue with proxies on the Spring MVC Controllers. I create a sample that uses @Transactional rather than @PreAuthorize in spring-framework-issues/SPR-13580. For your convenience, the following TestController also reproduces the issue:

@Controller
@RequestMapping("/test")
@Transactional
public class TestController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Inject
    private MyValidator myValidator;

    @InitBinder("myObj")
    private void initBinder(WebDataBinder binder) {
        logger.info("myValidator = {}", myValidator);
        binder.initDirectFieldAccess();
        binder.setValidator(myValidator);
    }

    @RequestMapping(value = "/doPost", method = RequestMethod.POST)
    public String doPost(@Valid MyObj myObj, BindingResult br) throws IOException {
        logger.info("myObj = {} , bindingResult = {}", myObj, br);
        if(br.hasErrors()) {
            return "error";
        }
        return "redirect:/test/form";
    }
}

At this point, I'm not sure if this is an issue with Spring framework or Spring Boot but it does not appear to be isolated to Spring Security.

spring-projects-issues commented 8 years ago

Rossen Stoyanchev commented

I cannot fully explain the behavior but I'm pretty sure this has to do with the fact that initBinder is a private method and CGLib cannot proxy private (nor final) methods.

spring-projects-issues commented 5 years ago

Bulk closing outdated, unresolved issues. Please, reopen if still relevant.