谷粒商城实战笔记-214~219-商城业务-认证服务-验证码防刷校验
文章目录
- 一,验证码防刷校验
- 1,第三方服务提供发送短信的接口
- 2,登录服务提供给前端的接口
- 二,215-商城业务-认证服务-一步一坑的注册页环境
- 三,商城业务-认证服务-异常机制
- 四,217-商城业务-认证服务-MD5&盐值&BCrypt
- 五,218-商城业务-认证服务-注册完成
- 六,219-商城业务-认证服务-账号密码登录完成
- 总结
包含:
- 214-商城业务-认证服务-验证码防刷校验
- 215-商城业务-认证服务-一步一坑的注册页环境
- 216-商城业务-认证服务-异常机制
- 217-商城业务-认证服务-MD5&盐值&BCrypt
- 218-商城业务-认证服务-注册完成
- 219-商城业务-认证服务-账号密码登录完成
一,验证码防刷校验
1,第三方服务提供发送短信的接口
短信服务可以给多方提供服务,所以要提供一个接口,接受电话号码和code,调用接口将code发送给指定的电话号码。
@Controller
@RequestMapping(value = "/sms")
public class SmsSendController {@Resourceprivate SmsComponent smsComponent;/*** 提供给别的服务进行调用* @param phone* @param code* @return*/@GetMapping(value = "/sendCode")public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code) {//发送验证码smsComponent.sendCode(phone,code);return R.ok();}}
2,登录服务提供给前端的接口
@Controller
public class LoginController {@Resourceprivate ThirdPartFeignService thirdPartFeignService;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@ResponseBody@GetMapping(value = "/sms/sendCode")public R sendCode(@RequestParam("phone") String phone) {//1、接口防刷String redisCode = stringRedisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);if (StrUtil.isNotEmpty(redisCode)) {//活动存入redis的时间,用当前时间减去存入redis的时间,判断用户手机号是否在60s内发送验证码long currentTime = Long.parseLong(redisCode.split("_")[1]);if (System.currentTimeMillis() - currentTime < 60000) {//60s内不能再发return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(),BizCodeEnum.SMS_CODE_EXCEPTION.getMessage());}}//2、验证码的再次效验 redis.存key-phone,value-codeint code = (int) ((Math.random() * 9 + 1) * 100000);String codeNum = String.valueOf(code);String redisStorage = codeNum + "_" + System.currentTimeMillis();//存入redis,防止同一个手机号在60秒内再次发送验证码stringRedisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX+phone,redisStorage,10, TimeUnit.MINUTES);thirdPartFeignService.sendCode(phone, codeNum);return R.ok();}}
这段代码用于共前端调用,发送短信验证码,其主要作用如下:
-
接口防刷机制:首先检查Redis中是否已经为提供的手机号码
phone
存储了验证码。如果存在,并且当前时间与存储验证码时的时间差小于60秒,则认为请求过于频繁,返回错误信息,阻止用户在短时间内重复发送验证码。 -
生成验证码:如果用户请求发送验证码的频率正常,代码将生成一个六位数的随机验证码。
-
存储验证码到Redis:将生成的验证码和当前时间戳拼接,存储到Redis中,其中键为手机号码,值为验证码和时间戳的组合。这用于验证验证码的有效性,并防止同一个手机号在60秒内再次发送验证码。
-
调用第三方服务发送验证码:通过Feign服务调用第三方服务(例如短信服务提供商)发送验证码到用户的手机上。
-
返回操作结果:如果验证码发送成功,方法返回一个表示操作成功的响应。
关于发送验证的两个要点:
- 1,因为要验证用户输入的验证码是否正确,需要后台将生成的验证码进行保存,因为不需要持久化,所以保存在redis中。
- 2,为了防止恶意调用,所以要在后台进行验证,60秒内只能调用一次,超过一次的返回错误,不允许重发。
二,215-商城业务-认证服务-一步一坑的注册页环境
这一节的主要内容是:
- 校验前端登录参数是否符合标准
- 如果不符合校验规则,将错误信息返回给前端,由前端回显
封装前端参数的VO,VO使用了JSR303校验,简化代码。
@Data
public class UserRegisterVo {@NotEmpty(message = "用户名不能为空")@Length(min = 6, max = 19, message="用户名长度在6-18字符")private String userName;@NotEmpty(message = "密码必须填写")@Length(min = 6,max = 18,message = "密码必须是6—18位字符")private String password;@NotEmpty(message = "手机号不能为空")@Pattern(regexp = "^[1]([3-9])[0-9]{9}$", message = "手机号格式不正确")private String phone;@NotEmpty(message = "验证码不能为空")private String code;}
这里有个小细节,前端登录界面登录请求会携带参数,校验不通过,需要把错误信息返回给前端,因为使用了了模板引擎,课程中采用请求转发的方式,实际上在直接返回错误信息应该也是可以的,只要前端做好适配。
三,商城业务-认证服务-异常机制
登录请求的参数校验通过后,需要验证验证码是否正确,从redis中取出存储的验证码,然后校验即可。
如果验证码校验通过,下一步要调用member会员服务的接口,注册会员。
会员服务提供的这个接口中,需要做一些校验:
- 用户名要唯一
- 手机号要唯一
如果校验不通过,通过抛异常的方式将信息抛给controller层。
四,217-商城业务-认证服务-MD5&盐值&BCrypt
对于用户的密码,不能存储明文,必须对其进行加密。
加密有两种方式:
- 不可逆的加密,不能根据密文推出明文
- 可逆的加密,可以根据密文推出密文
为了防止相同的密码有相同的密文,通常采用加盐的方式,这样即使多个用户的密码相同,存储在数据库的密文也不是一样的
课程中采用不可逆的加密,在实际工作中一般也采用不可逆的加密。
MD5是常用的不可逆加密算法,我们使用Spring提供的工具类BCryptPasswordEncoder
对密码进行md5加密。
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();String encode = bCryptPasswordEncoder.encode(vo.getPassword());memberEntity.setPassword(encode);
MD5(Message Digest Algorithm 5,信息摘要算法5)是一种广泛使用的加密算法,用于生成数据的哈希值。
-
压缩性:无论输入数据的长度如何,MD5算法都会生成一个固定长度(128位,即16字节)的哈希值。
-
易于计算:从原始数据计算出MD5值是一个相对简单和快速的过程。
-
抗修改性:对原始数据进行任何微小的改动,哪怕是修改一个字符,都会导致生成的MD5值发生显著的变化。
-
强抗碰撞性:找到两个不同的数据输入,它们具有相同的MD5值,被认为是非常困难的。但是,这并不意味着MD5没有碰撞,实际上,MD5的抗碰撞性已经被证明是不足够的,尤其是在密码学领域。
-
加盐(Salting):为了增加MD5的安全性,可以通过添加一个随机生成的盐值(Salt)与原始数据组合,然后进行MD5加密。数据库中存储加密后的MD5值和盐值。在验证数据时,使用相同的盐值对输入数据进行MD5加密,然后与存储的MD5值进行比较以验证正确性。
五,218-商城业务-认证服务-注册完成
这一节主要调试注册服务的完成。
六,219-商城业务-认证服务-账号密码登录完成
这一节生完成登录的后台接口,主要的逻辑:
- 权限认知服务接收前端页面的参数
- 校验通过后调用会员服务的接口,接口根据参数查询数据库,判断用户名和密码是否正确
总结
-
错误处理:首先检查表单验证结果
result
是否有错误。如果有错误,将错误信息收集并存储到errors
映射中,然后重定向回注册页面。 -
验证码验证:从前端获取用户输入的验证码
code
,然后从Redis中获取存储的验证码redisCode
。如果Redis中有对应的验证码,并且用户输入的验证码与Redis中的验证码匹配,则进行下一步。 -
删除验证码:如果验证码匹配,从Redis中删除该验证码,防止重复使用。
-
远程服务注册:调用远程服务
memberFeignService.register(vos)
进行用户注册。 -
注册结果处理:
- 如果注册成功(
register.getCode() == 0
),重定向到登录页面。 - 如果注册失败,收集错误信息,存储到
errors
映射中,然后重定向回注册页面。
- 如果注册成功(
-
验证码不匹配或不存在处理:如果用户输入的验证码与Redis中的不匹配,或者Redis中没有对应的验证码,收集错误信息,存储到
errors
映射中,然后重定向回注册页面。
整体上,这段代码实现了用户注册时的验证码验证、错误处理和注册结果反馈,确保了注册流程的安全性和用户体验。