大型商城:认证服务-注册相关
环境搭建
1.新建一个moudle gulimall-auth-server
2.将登录注册页的静态文件放到Nginx中
3.登录注册页的html修改相应的静态文件链接
4.修改host文件 auth.mall.com 修改网关
5.在 gulimall-auth-server 中新建一个controller,写两个方法用来返回login页面与reg页面
验证码倒计时
$(function () {
$("#sendCode").click(function () {
//2、倒计时
if($(this).hasClass("disabled")) {
//正在倒计时中
} else {
//1、给指定手机号发送验证码
$.get("/sms/sendCode?phone=" + $("#phoneNum").val(),function (data) {
if(data.code != 0) {
alert(data.msg);
}
});
timeoutChangeStyle();
}
});
});
var num = 60;
function timeoutChangeStyle() {
$("#sendCode").attr("class","disabled");
if(num == 0) {
$("#sendCode").text("发送验证码");
num = 60;
$("#sendCode").attr("class","");
} else {
var str = num + "s 后再次发送";
$("#sendCode").text(str);
setTimeout("timeoutChangeStyle()",1000);
}
num --;
}
视图映射
@Controller
public class MallWebConfig implements WebMvcConfigurer {
// 视图映射
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login.html").setViewName("login");
registry.addViewController("/reg.html").setViewName("reg");
}
}
通过上面的视图映射,无需再controller中再单独写两个方法了
整合短信接口
照着接口文档写就可以了
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Data
@ToString
@Component
public class SmsComponent {
private String host;
private String path;
private String template_id;
private String appcode;
public void SendComponse(String phone,String code){
//String host = "https://dfsns.market.alicloudapi.com";
//String path = "/data/send_sms";
String method = "POST";
//String appcode = "3ca9af00619c414d9909fa528345c9df";
Map<String, String> headers = new HashMap<String, String>();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
headers.put("Authorization", "APPCODE " + appcode);
//根据API的要求,定义相对应的Content-Type
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
Map<String, String> querys = new HashMap<String, String>();
Map<String, String> bodys = new HashMap<String, String>();
bodys.put("content", "code:"+code);
bodys.put("phone_number", phone);
//bodys.put("template_id", "TPL_0000");
try {
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
System.out.println(response.toString());
//获取response的body
System.out.println(EntityUtils.toString(response.getEntity()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试的时候,在测试启动类的测试类上测试,没有效果,页读取不到yml中的值,原因:
GulimallThirdPartyApplicationTests.java 上 也要加全注解@RunWith(SpringRunner.class) @EnableDiscoveryClient
验证码防刷校验
验证码思路
在gulimall-third-party创建一个方法,sendCode,其他服务如果需要验证码,就用openfegin进行远程调用
sendCode方法:com.tinstu.gulimall.third.party.controller.SmsSendController
product进行调用
验证码再次校验
验证码再次校验 redis . 存key-phone value-code
redisTemplate.opsForValue().set(AuthServerCostant.SMS_CODE_CACHE_PREFI+phone,redis_code,10, TimeUnit.MINUTES);
验证码60秒校验
往Redis中存验证码的时候,带上当时的时间 code_time,再次发送之前,先看看Redis中有没有,如果有取出时间减去当前时间,小于60就报错
String redisCode = redisTemplate.opsForValue().get(AuthServerCostant.SMS_CODE_CACHE_PREFI + phone);
if(!StringUtils.isEmpty(redisCode)){
long l = Long.parseLong(redisCode.split("_")[1]);
if(System.currentTimeMillis() - l < 60000){
return R.error(BizCodeEnume.SMS_CODE_EXCEPTION.getCode(), BizCodeEnume.SMS_CODE_EXCEPTION.getMsg());
}
}
String code = UUID.randomUUID().toString().substring(0, 5);
String redis_code = code + "_" + System.currentTimeMillis();
用户注册
UserRegistVo封装注册页面提交的数据,并使用jsr303进行校验
/**
* TODO 重定向携带数据,利用session原理 将数据放在sessoin中 取一次之后删掉
*
* TODO 1. 分布式下的session问题
* 校验
* RedirectAttributes redirectAttributes : 模拟重定向带上数据
*/
@PostMapping("/register")
public String regist(@Valid UserRegistVo vo, BindingResult result,
RedirectAttributes redirectAttributes){
if(result.hasErrors()){
// 将错误属性与错误信息一一封装
Map<String, String> errors = result.getFieldErrors().stream().collect(
Collectors.toMap(FieldError::getField, fieldError -> fieldError.getDefaultMessage()));
// addFlashAttribute 这个数据只取一次
redirectAttributes.addFlashAttribute("errors",errors);
return "redirect:http://auth.mall.com/reg.html";
}
//真正的注册 调用远程服务进行注册
return "redirect:/login.html";
}
用户注册异常机制
校验验证码
从Redis中取出验证码,处理后和用户输入的进行对比
如果相同就删除Redis中的数据,并调用远程接口 进行用户信息的添加
用户注册
远程调用 gulimall-member的方法 register
对用户信息进行保存之前,判断用户名和手机号是否存在 两个方法 checkphoneUnique checkUsernameUnique
@Override
public void checkphoneUnique(String phone) {
if(this.baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone)) > 0){
throw new PhoneExsitException();
}
}
使用异常机制,如果存在抛出异常PhoneExsitException username同理
public class PhoneExsitException extends RuntimeException{
public PhoneExsitException(){
super("手机号已经存在!");
}
}
此外还涉及到设置用户名的默认登录,需要去数据库查默认的用户等级
密码加密 MD5 & 盐值 & BCrypt
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encode = passwordEncoder.encode("123456"); //每次加密后的结果都不一样
boolean matches = passwordEncoder.matches("123456","加密后的字符串"); //此方法可以对比加密前后,即使每次的加密的结果不一样
注册方法中对密码进行加密.
//密码加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encode = passwordEncoder.encode(vo.getPassword());
entity.setPassword(encode);
完整注册功能:com.tinstu.gulimallauthserver.controller.LoginController