大型商城:认证服务-注册相关

环境搭建

1.新建一个moudle  gulimall-auth-server

2.将登录注册页的静态文件放到Nginx中

3.登录注册页的html修改相应的静态文件链接

4.修改host文件 auth.mall.com  修改网关

5.在 gulimall-auth-server 中新建一个controller,写两个方法用来返回login页面与reg页面

验证码倒计时

js代码

			$(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

 

阅读剩余
THE END