一、验证码的获取
- 查看其项目源码,在跳转到登录页面后,调用created钩子函数,而调用的这个getCaptcha(),方法就是请求获取验证码的方法。而且注意,在其请求验证码的时候,携带了uuid参数,并且将这个uuid参数赋值给了this.dataForm.uuid。此时vue对象的data属性上的dataForm登录表单对象就有了这个uuid值,这个参数怎么用下面再说。
created () {
this.getCaptcha()
},
getCaptcha () {
this.dataForm.uuid = getUUID()
this.captchaPath = this.$http.adornUrl(`/captcha.jpg?uuid=${this.dataForm.uuid}`)
}
- 再看其后端代码
@GetMapping("captcha.jpg")
public void captcha(HttpServletResponse response, String uuid)throws IOException {
response.setHeader("Cache-Control", "no-store, no-cache");
response.setContentType("image/jpeg");
//获取图片验证码
BufferedImage image = sysCaptchaService.getCaptcha(uuid);
ServletOutputStream out = response.getOutputStream();
ImageIO.write(image, "jpg", out);
IOUtils.closeQuietly(out);
}
我们找到后端的接口,也就是captcha方法,这个方法就是设置响应类型,然后调用sysCaptchaService的getCaptcha这个方法。
public BufferedImage getCaptcha(String uuid) {
if(StringUtils.isBlank(uuid)){
throw new RRException("uuid不能为空");
}
//生成文字验证码
String code = producer.createText();
SysCaptchaEntity captchaEntity = new SysCaptchaEntity();
captchaEntity.setUuid(uuid);
captchaEntity.setCode(code);
//5分钟后过期
captchaEntity.setExpireTime(DateUtils.addDateMinutes(new Date(), 5));
this.save(captchaEntity);
return producer.createImage(code);
}
继续看这个方法,这个方法生成了code,过期时间,并和uuid一起组成了一个 验证码对象,保存到了数据库当中。当做完这些操作后,将生成的图片响应给前端。
验证码的逻辑总结如下图:
二、登录逻辑
- 登录按钮触发的是dataFormSubmit 方法,我们看这个方法的处理逻辑。
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl('/sys/login'),
method: 'post',
data: this.$http.adornData({
'username': this.dataForm.userName,
'password': this.dataForm.password,
'uuid': this.dataForm.uuid,
'captcha': this.dataForm.captcha
})
}).then(({data}) => {
if (data && data.code === 0) {
this.$cookie.set('token', data.token)
this.$router.replace({ name: 'home' })
} else {
this.getCaptcha()
this.$message.error(data.msg)
}
})
}
})
},
从这个方法可以看出,在请求/sys/login
接口时,携带的参数是
username:对应我们输入的用户名
password:对应我们输入的密码
uuid:注意这个uuid就是当时请求验证码时携带的uuid
captcha:我们输入的验证码
- 查看后端登录接口
@PostMapping("/sys/login")
public Map<String, Object> login(@RequestBody SysLoginForm form)throws IOException {
boolean captcha = sysCaptchaService.validate(form.getUuid(), form.getCaptcha());
if(!captcha){
return R.error("验证码不正确");
}
//用户信息
SysUserEntity user = sysUserService.queryByUserName(form.getUsername());
//账号不存在、密码错误
if(user == null || !user.getPassword().equals(new Sha256Hash(form.getPassword(), user.getSalt()).toHex())) {
return R.error("账号或密码不正确");
}
//账号锁定
if(user.getStatus() == 0){
return R.error("账号已被锁定,请联系管理员");
}
//生成token,并保存到数据库
R r = sysUserTokenService.createToken(user.getUserId());
return r;
}
分析下面这个方法
boolean captcha = sysCaptchaService.validate(form.getUuid(), form.getCaptcha());
查看这个方法的实现
@Override
public boolean validate(String uuid, String code) {
SysCaptchaEntity captchaEntity = this.getOne(new QueryWrapper<SysCaptchaEntity>().eq("uuid", uuid));
if(captchaEntity == null){
return false;
}
//删除验证码
this.removeById(uuid);
if(captchaEntity.getCode().equalsIgnoreCase(code) && captchaEntity.getExpireTime().getTime() >= System.currentTimeMillis()){
return true;
}
return false;
}
看到这里,uuid的作用应该就明白了,根据这个uuid去查找数据库保存的验证码,然后比对数据库中的验证码和用户输入的是否一致,如果一致且未过期,则返回true。
- 总结登录流程图如下: