图片验证码在注册、登录、交易、交互等各类场景中都发挥着巨大作用,能够防止操作者利用机器进行暴力破解、恶意注册、滥用服务、批量化操作和自动发布等行为。
创建一个实体类封装,给前端返回的验证码数据:
@Data
public class ValidateCodeVo {
private String codeKey ; // 验证码的key
private String codeValue ; // 图片验证码对应的字符串数据
}
业务层代码实现:
public interface ValidateCodeService {
// 获取验证码图片
public abstract ValidateCodeVo generateValidateCode();
}
@Service
public class ValidateCodeServiceImpl implements ValidateCodeService {
@Autowired
private RedisTemplate<String , String> redisTemplate ;
@Override
public ValidateCodeVo generateValidateCode() {
// 使用hutool工具包中的工具类生成图片验证码
//参数:宽 高 验证码位数 干扰线数量
CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(150, 48, 4, 20);
String codeValue = circleCaptcha.getCode();
String imageBase64 = circleCaptcha.getImageBase64();
// 生成uuid作为图片验证码的key
String codeKey = UUID.randomUUID().toString().replace("-", "");
// 将验证码存储到Redis中
redisTemplate.opsForValue().set("user:login:validatecode:" + codeKey , codeValue , 5 , TimeUnit.MINUTES);
// 构建响应结果数据
ValidateCodeVo validateCodeVo = new ValidateCodeVo() ;
validateCodeVo.setCodeKey(codeKey);
validateCodeVo.setCodeValue("data:image/png;base64," + imageBase64);
// 返回数据
return validateCodeVo;
}
}
在Controller中添加获取验证码接口方法:
@Autowired
private ValidateCodeService validateCodeService;
@GetMapping(value = "/generateValidateCode")
public Result<ValidateCodeVo> generateValidateCode() {
ValidateCodeVo validateCodeVo = validateCodeService.generateValidateCode();
return Result.build(validateCodeVo , ResultCodeEnum.SUCCESS) ;
}
在登录的业务层实现验证码校验:
/**
* 用户登录
* @param loginDto
* @return
*/
@Override
public LoginVo login(LoginDto loginDto) {
//获取输入的验证码和存储到redis的key名称
String captcha = loginDto.getCaptcha();
String key = loginDto.getCodeKey();
//根据获取的redis的key 查询redis里面存储的验证码
String redisCode = redisTemplate.opsForValue().get("user:validate" + key);
// 比较输入的和redis存储验证码是否一致
if(StrUtil.isEmpty(redisCode) || !StrUtil.equalsIgnoreCase(redisCode,captcha)){
//提示用户,校验失败
throw new GuiguException(ResultCodeEnum.VALIDATECODE_ERROR);
}
// 如果一致,删除redis里面验证码
redisTemplate.delete("user:validate" + key);
// 1.获取提交的用户名
String userName = loginDto.getUserName();
// 2.根据用户名查询用户表
SysUser sysUser = sysUserMapper.selectUserInfoByUserName(userName);
// 3.如果根据用户名查不到对应的信息,用户不存在,返回错误信息
if(sysUser == null){
//throw new RuntimeException("用户名不存在");
throw new GuiguException(ResultCodeEnum.LOGIN_ERROR);
}
// 4.根据用户名查询用户信息,用户存在
// 5.获取输入的密码,比较输入的密码和数据库的密码是否一致
String database_assword = sysUser.getPassword();
// 把输入的密码进行加密 再比较数据库的密码
String input_password = DigestUtils.md5DigestAsHex(loginDto.getPassword().getBytes());
//比较
if(!input_password.equals(database_assword)){
// throw new RuntimeException("密码不正确");
throw new GuiguException(ResultCodeEnum.LOGIN_ERROR);
}
// 6.如果密码一致,登陆成功,如果你密码不一致登陆失败
// 7.登陆成功,生产用户的唯一标识token
String token = UUID.randomUUID().toString().replaceAll("-", "");
// 8.把登陆成功的用户信息放到redis里面
// key:token value:用户信息
redisTemplate.opsForValue().set("user:login"+token,JSON.toJSONString(sysUser),7, TimeUnit.DAYS);
// 9.返回loginvo对象
LoginVo loginVo = new LoginVo();
loginVo.setToken(token);
return loginVo;
}