验证码防刷、校验
单点登录与社交登录
上一讲写到登录 验证吗的获取,这一节叙述下验证码的操作。验证码防刷、校验
一、验证码的操作
1.接口防刷(未实现)
2.验证码的再次校验-存redis(已经实现)
2.1).引入redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2).配置文件配置redis
#配置redis
spring.redis.host=192.168.56.10
spring.redis.port=6379
2.3).验证码关于redis的常量
/**
* @Description:验证码常量
**/
public class AuthServerConstant {
public static final String SMS_CODE_CACHE_PREFIX = "sms:code:";
}
2.4).loginController
@Controller
public class LoginController {
@Autowired
ThirdPartyFeignService thirdPartyFeignService;
@Autowired
StringRedisTemplate redisTemplate;
@GetMapping("/sms/sendcode")
@ResponseBody
public R sendcode(@RequestParam("phone") String phone){
//1.接口防刷
String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
if (!StringUtils.isEmpty(redisCode)){
long l=Long.parseLong(redisCode.split("_")[1]);
if(System.currentTimeMillis()-l<60000){
//60s内不能再发验证码
return R.error(BizCodeEnume.SMS_CODE_EXCEPTION.getCode(),BizCodeEnume.SMS_CODE_EXCEPTION.getMsg());
}
}
//2.验证码再次校验。redis。key-phone,value-code。sms:code:13213117432—>1234
// String code= UUID.randomUUID().toString().substring(0,5)+"_"+System.currentTimeMillis();
String code = UUIDGenrateUtil.getRandom(4);
String subString = code + "_" + System.currentTimeMillis();
//redis缓存验证码,防止同一个phone手机,60s内再次发送验证码
redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX+phone,subString ,10, TimeUnit.MINUTES);
thirdPartyFeignService.sendCode(phone,code);
return R.ok();
}
}
二、注册页面的表单
1.注册表单的操作
1.1).用户数据的封装
@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;
}
1.2).注册表单的controller
/**
* TODO:重定向携带数据,利用session原理。
* 将数据放入session,只要跳到下一页取出此数据,session的数据被删除
* RedirectAttributes:模拟重定向携带数据
* */
@PostMapping("/regist")
public String regist(@Valid UserRegisterVo vo, BindingResult result, RedirectAttributes redirectAttributes) {
if(result.hasErrors()){
// Map<String, String> map = new HashMap<>();
// Stream<Map<String, String>> mapStream = result.getFieldErrors().stream().map(fieldError -> {
// String field = fieldError.getField();
// String message = fieldError.getDefaultMessage();
// map.put(field, message);
// return map;
// });
Map<String, String> errors = result.getFieldErrors().stream()
.collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
// model.addAttribute("errors",errors);
redirectAttributes.addFlashAttribute("errors",errors);
//校验失败,转发发哦注册页面
return "redirect:http://auth.gulimall.com/reg.html";
}
//注册成功,返回登录页面
return "redirect:/login.html";
}
1.3).注册表单的页面渲染-
reg.html
1.4).注册表单的提示信息-reg.html
<div class="tips"
style="color:red"
th:text="${errors!=null?(#maps.containsKey(errors,'userName')?errors.userName:''):''}">
</div>
2.表单数据的验证
2.1).loginController的regist方法–auth项目
2.2).MemberController-member项目
2.2.1注册的表单数据封装-MemberRegisterVo-member项目
2.2.2 验证手机号、用户名数据库是否存在-member项目的MemberServiceImpl
2.2.3 异常类-member项目
2.2.4 查询会员等级-member项目
2.3).MemberServiceImpl-member项目
2.3.1 验证手机号、用户名数据库是否存在
2.3.2 查询会员等级
2.3.3 密码加密–盐值加密
2.3.4 其他默认配置(TODO)
2.4 密码加密
2.4.1 MD5–可破解
2.4.2 盐值加密1-需要数据库加入盐值字段(麻烦)
2.4.3 盐值加密2(**)–安全、可靠
相同的密码,每次的加密密码不同,因为盐值在加密密码中
@Test
public void testPassword() {
//2.盐值加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
//生成加密密码
String encode = passwordEncoder.encode("123456");
//验证密码
//$2a$10$5VJAGi6kEzZkCe0.RJaGdefej6VJWRAo7LNFpA7KOXaMBEWcLd8Bq
//$2a$10$G3f60GrG3Oc7r.maFdgcJOdPVW6O62gPHQRx7WvObBewaP87vE0wi
boolean b = passwordEncoder.matches("123456", "$2a$10$G3f60GrG3Oc7r.maFdgcJOdPVW6O62gPHQRx7WvObBewaP87vE0wi");
System.out.println("加密后密码:" + encode);
System.out.println("密码是否匹配:" + b);
}
3.注册完成
3.1. MemberFeignService–auth写member的feign接口
由于UserRegisterVo与MemberRegisterVo使用相同的属性名称,所以可以混合使用(故此处使用UserRegisterVo)
@FeignClient("gulimall-member")
public interface MemberFeignService {
//注册服务
@PostMapping("/member/member/regist")
// public R regist(@RequestBody MemberRegisterVo vo);
R regist(@RequestBody UserRegisterVo vo);
}
3.2. 调用member的注册功能–auth的loginController
@Autowired
MemberFeignService memberFeignService;
//todo 验证验证码通过。真正注册,远程调用member实现注册功能 ok
R r = memberFeignService.regist(vo);
if (r.getCode() == 0) {
System.out.println("注册功能,注册成功");
//成功
return "redirect:http://auth.gulimall.com/login.html";
} else {
System.out.println("注册功能,注册失败。失败原因:"+r.getData(new TypeReference<String>() {}));
Map<String, String> errors = new HashMap<>();
errors.put("msg", r.getData(new TypeReference<String>() {
}));
redirectAttributes.addFlashAttribute("errors", errors);
return "redirect:http://auth.gulimall.com/reg.html";
}