@Controller中的原型bean无法按预期工作

时间:2018-10-02 18:03:06

标签: java spring spring-boot scope captcha

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

@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
  • 的规定最终尝试使用范围代理

DataCaptcha

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

2 个答案:

答案 0 :(得分:0)

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

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

DataCaptcha dataCaptcha = ctx.getBean(DataCaptcha.class)

获取原型实例。

答案 1 :(得分:0)

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

正如您已经指出的那样,可以识别webApplicationContext,您需要在方法内部获取DataCaptcha的新本地实例。您可以尝试将唯一的ID添加到DataCaptcha并使用该ID来获取验证码,以进行验证。或者,您可以将其放在服务器端的userSession对象中,并在成功验证后将其清除。