【问题标题】:Prototype bean inside @Controller not working as expected@Controller 中的原型 bean 未按预期工作
【发布时间】:2019-03-07 21:50:46
【问题描述】:

我有一个包含两种方法的控制器。第一个生成一个随机验证码值,第二个将其与用户编写的输入进行比较。问题是当多个用户尝试验证验证码值时,最后生成的值已正确验证,以便为其他用户预览生成的值。

@Controller
@RequestMapping
public class CaptchaController {

    private Producer captchaProducer = null;

    @Autowired
    private DataCaptcha dataCaptcha;

    @Autowired
    public void setCaptchaProducer(Producer captchaProducer) {
        this.captchaProducer = captchaProducer;
    }

    @RequestMapping(value = "/generate-captcha.json", method = RequestMethod.GET, produces = "application/json")
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {

        String captchaText = captchaProducer.createText();

        dataCaptcha.setCaptcha(captchaText);
        dataCaptcha.setPasoCaptcha(false);

        System.out.println("$$$$$$$$$$$$$$$$ "+ dataCaptcha.getCaptcha()); // output: null

        BufferedImage bi = captchaProducer.createImage(captchaText);
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(bi, "jpg", out);

        out.flush();
        out.close();
        return null;
    }

    @RequestMapping(value = "/validate-captcha.json", method = RequestMethod.POST, produces = "application/json")
    public @ResponseBody Map<String, Object> validarCaptcha(HttpServletRequest request,
            @RequestParam(value = "valueCaptcha", defaultValue = "") String valueCaptcha) {

        String captchaId = dataCaptcha.getCaptcha();
        Boolean rpta = StringUtils.equalsIgnoreCase(captchaId, valueCaptcha);
        String message = "";
        String messageType = "OK";
        Map<String, Object> response = new HashMap<String,Object>();

        if (!rpta) {
            message = "incorrect captcha";
            messageType = "ERROR";
            dataCaptcha.setPasoCaptcha(false);
        } else {
            dataCaptcha.setPasoCaptcha(true);
        }
        response.put("messageType", messageType);
        response.put("message", message);
        response.put("object", rpta);
        return response;
    }
}

该错误是由于@Controller bean 单例造成的,我需要在我的 bean 中使用 Prototype 范围。所以我尝试了不同的方法来做到这一点:

  • 使控制器能够感知 webApplicationContext,如 here 所述
  • 使用@Lookup,例如here
  • 终于按照here 的描述尝试了范围代理

数据验证码

import lombok.Getter;
import lombok.Setter;

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Getter
@Setter
public class DataCaptcha {
    private String captcha;
    private boolean pasoCaptcha;
}

他们都没有工作。尝试调试并在控制器上的此特定行中

String captchaText = captchaProducer.createText();
dataCaptcha.setCaptcha(captchaText);

captchaText 有一个值,但是在使用 setCaptcha 并检查对象 dataCaptcha 之后,captcha 字段是空。

我正在使用 Spring Boot 2.0.3

【问题讨论】:

  • dataCaptcha 可能需要成为会话范围的 bean - this 可能会有所帮助。

标签: java spring spring-boot scope captcha


【解决方案1】:

我认为您的 DataCaptcha 变量不应该是控制器中的类变量,而是您的 handleRequest 方法中的变量。

然后,使用 spring 上下文(如您所暗示的),执行一个

DataCaptcha dataCaptcha = ctx.getBean(DataCaptcha.class)

获取原型实例。

【讨论】:

    【解决方案2】:

    Singleton Bean 在其整个生命周期中都持有对同一个原型 bean 的引用。没错,您需要在原型范围内拥有 DataCaptcha,否则它将在 threadContexts 之间共享,并且多个用户将能够使用相同的验证码。而且,你需要把它放在方法内部,不能放在类级别,因为如果是类级别,那么控制器类(单例 bean)只会创建一个对象,并且其关联的 DataCaptcha 将是相同的。

    正如您已经指出的,webApplicationContext 感知,您需要在您的方法中获取 DataCaptcha 的新本地实例。您可以尝试将唯一 ID 添加到 DataCaptcha 并使用该 ID 检索验证码以进行验证。或者你可以把它放在服务器端的userSession对象中,一旦验证成功就清除它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-20
      • 1970-01-01
      • 2018-09-01
      • 1970-01-01
      • 2013-11-24
      • 2014-01-28
      • 2017-04-18
      相关资源
      最近更新 更多