【大型电商项目开发】用户注册&异常处理&MD5盐值加密&用户登录-50

导读:本篇文章讲解 【大型电商项目开发】用户注册&异常处理&MD5盐值加密&用户登录-50,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一:编写注册数据校验接口

1.新建注册实体类对象

@Data
public class UserRegistVO {
    /**
     * 用户名
     */
    @NotEmpty(message="用户名必须提交")
    @Length(min = 6,max = 18,message="用户名必须是6到18位字符")
    private String userName;
    /**
     * 密码
     */
    @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;

}

2.编写Controller方法,实现用户注册的校验功能

 /**
     * 用户注册
     * @return
     */
    @PostMapping("/regist")
    public String register(@Validated UserRegistVO vo, BindingResult result, RedirectAttributes redirectAttributes){
        if(result.hasErrors()){
            Map<String, String> errors = result.getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField,FieldError::getDefaultMessage));
            redirectAttributes.addFlashAttribute("errors",errors);
            //request method post not supported
            //用户注册->/regist----->转发到reg.html,路径映射默认都是get方式访问
            //校验出错转发到注册页
            return "redirect:http://127.0.0.1:20000/reg.html";
        }
        //注册成功,回到登录首页,调用远程服务进行注册
        //todo
        //本服务的话,可以不需要加ip和端口
        return "redirect:/login.html";
    }
  • @Validated可以将实体类返回的错误信息进行校验
  • BindingResult 用来接收返回的错误信息
  • 然后通过model将错误信息返回到前台
  • FieldError::getField是stream表达式,等于 fieldError->fieldError.getField
  • redirectAttributes:模拟重定向视图,并且携带参数

二:注册功能实现

1.注册之前,先校验验证码

//1.校验验证码
        String code = vo.getCode();
        String s = stringRedisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
        if(!StringUtils.isEmpty(s)){
            if(code.equals(s.split("_")[0])){
                //校验完成以后删除验证码
                stringRedisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
                //验证码校验通过,真正注册
            } else {
                //验证码不存在的话,就说明过期了
                Map<String, String> errors =new HashMap<>();
                errors.put("code","验证码错误");
                redirectAttributes.addFlashAttribute("errors",errors);
                return "redirect:http://127.0.0.1:20000/reg.html";
            }
        } else {
            //验证码不存在的话,就说明过期了
            Map<String, String> errors =new HashMap<>();
            errors.put("code","验证码错误");
            redirectAttributes.addFlashAttribute("errors",errors);
            return "redirect:http://127.0.0.1:20000/reg.html";
        }

2.编写远程注册接口

1)会员服务controller编写注册功能

@Data
public class MemberRegistVO {

    /**
     * 用户名
     */
    private String userName;
    /**
     * 密码
     */
    private String password;
    /**
     * 手机号
     */
    private String phone;
}

     /**
     * 会员注册功能
     * @return
     */
    @PostMapping("/regist")
    public R regist( @RequestBody MemberRegistVO vo){
        try {
            memberService.regist(vo);
        } catch (PhoneExistException e) {
            return R.error(BizCodeEnume.PHONE_EXIST_EXCEPTION.getCode(),BizCodeEnume.PHONE_EXIST_EXCEPTION.getMsg());
        } catch (UserNameExistException e){
            return R.error(BizCodeEnume.USER_EXIST_EXCEPTION.getCode(),BizCodeEnume.USER_EXIST_EXCEPTION.getMsg());
        }
        return R.ok();
    }

2)校验用户名和手机号的唯一性

自定义异常:用户名存在异常和手机号存在异常

public class UserNameExistException extends RuntimeException {

    public UserNameExistException() {
        super("用户名存在异常");
    }
}

public class PhoneExistException extends RuntimeException{
    public PhoneExistException() {
        super("手机号存在异常");
    }
}

去数据库查询,如果已经保存用户名或者手机号,直接将异常抛出去

/**
     * 校验手机号唯一性
     * @param phone
     * @throws PhoneExistException
     */
    @Override
    public void checkPhoneUnique(String phone) throws PhoneExistException{
        MemberDao memberDao = this.baseMapper;
        QueryWrapper<MemberEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("mobile",phone);
        Integer mobileCount = memberDao.selectCount(wrapper);
        if(mobileCount>0){
            throw new PhoneExistException();
        }
    }
    /**
     * 校验用户名唯一性
     * @param username
     * @throws UserNameExistException
     */
    @Override
    public void checkUserNameUnique(String username) throws UserNameExistException{
        MemberDao memberDao = this.baseMapper;
        QueryWrapper<MemberEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        Integer userNameCount = memberDao.selectCount(wrapper);
        if(userNameCount>0){
            throw new UserNameExistException();
        }
    }
  • service的方法都是void,没有返回值,上层业务想要获取异常,就抛出去,然后再controller进行捕获

3.将密码加密存储——MD5

1) MD5-Message Digest algorithm 5,信息摘要算法

 • 压缩性:任意长度的数据,算出的MD5值长度都是固定的。 
 • 容易计算:从原数据计算出MD5值很容易。 
 • 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。 
 • 强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。 
 • 不可逆

2) 加盐

• 通过生成随机数与MD5生成字符串进行组合
• 数据库同时存储MD5值与salt值。验证正确性时使用salt进行MD5即可

3)代码实现

      //设置密码,MD5不能直接进行加密存储->需要盐值加密,随机值->BCryptPasswordEncoder
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode(vo.getPassword());
        entity.setPassword(encode);

注:使用BCryptPasswordEncoder就不需要在数据库维护salt盐值字段了,其可以在加密生成的代码中,融入盐值,完成校验。

4.验证码校验完成后,远程调用会员注册接口

1)新建MemberFeignService

@FeignClient("gulimail-member")
public interface MemberFeignService {
    /**
     * 会员注册功能
     * @return
     */
    @PostMapping("/member/member/regist")
    public R regist( @RequestBody UserRegistVO vo);
}

2)接口实现成功

/**
     * 用户注册
     * @return
     */
    @PostMapping("/regist")
    public String register(@Validated UserRegistVO vo, BindingResult result, RedirectAttributes redirectAttributes){
        if(result.hasErrors()){
            Map<String, String> errors = result.getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField,FieldError::getDefaultMessage));
            redirectAttributes.addFlashAttribute("errors",errors);
            //request method post not supported
            //用户注册->/regist----->转发到reg.html,路径映射默认都是get方式访问
            //校验出错转发到注册页
            return "redirect:http://127.0.0.1:20000/reg.html";
        }
        //注册成功,回到登录首页,调用远程服务进行注册
        //1.校验验证码
        String code = vo.getCode();
        String s = stringRedisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
        if(!StringUtils.isEmpty(s)){
            if(code.equals(s.split("_")[0])){
                //校验完成以后删除验证码
                stringRedisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
                //验证码校验通过,真正注册
                try {
                    R r = memberFeignService.regist(vo);
                    if(r.getCode() == 0){
                        //成功
                        return "redirect:/login.html";
                    } else {
                        //失败
                        Map<String, String> errors =new HashMap<>();
                        errors.put("msg",r.getData(new TypeReference<String>(){}));
                        redirectAttributes.addFlashAttribute("errors",errors);
                        return "redirect:http://127.0.0.1:20000/reg.html";
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                //验证码不存在的话,就说明过期了
                Map<String, String> errors =new HashMap<>();
                errors.put("code","验证码错误");
                redirectAttributes.addFlashAttribute("errors",errors);
                return "redirect:http://127.0.0.1:20000/reg.html";
            }
        } else {
            //验证码不存在的话,就说明过期了
            Map<String, String> errors =new HashMap<>();
            errors.put("code","验证码错误");
            redirectAttributes.addFlashAttribute("errors",errors);
            return "redirect:http://127.0.0.1:20000/reg.html";
        }
        //todo
        //本服务的话,可以不需要加ip和端口
        return "redirect:/login.html";
    }

三:登录接口实现

1.编写UserLoginVo

@Data
public class UserLoginVO {
    private String loginAccount;
    private String password;
}

2.编写会员服务controller

 /**
     * 会员登录功能
     * @return
     */
    @PostMapping("/login")
    public R login( @RequestBody MemberLoginVO vo){
        MemberEntity entity = memberService.login(vo);
        if(entity!=null){
            return R.ok();
        } else {
            return R.error(BizCodeEnume.LOGINACCOUNT_PASSWORD_INVAILD_EXCEPTION.getCode(),BizCodeEnume.LOGINACCOUNT_PASSWORD_INVAILD_EXCEPTION.getMsg());
        }
    }

3.编写会员服务service

 @Override
    public MemberEntity login(MemberLoginVO vo) {
        String loginAccount = vo.getLoginAccount();
        String password = vo.getPassword();
        //1.去数据库查询
        MemberDao memberDao = this.baseMapper;
        QueryWrapper<MemberEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("username",loginAccount).or().eq("mobile",loginAccount);
        MemberEntity memberEntity = memberDao.selectOne(wrapper);
        if(memberEntity == null){
            //登陆失败
            return null;
        } else {
            //1.获取数据库存储的密码
            String passwordDb = memberEntity.getPassword();
            //2.和前台传过来的密码进行对比
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            boolean matches = passwordEncoder.matches(password, passwordDb);
            if(matches){
                return memberEntity;
            } else {
                return null;
            }
        }
    }

4.编写远程调用接口

@FeignClient("gulimail-member")
public interface MemberFeignService {
    /**
     * 会员注册功能
     * @return
     */
    @PostMapping("/member/member/regist")
    R regist( @RequestBody UserRegistVO vo);

    /**
     * 会员登录功能
     * @return
     */
    @PostMapping("/member/member/login")
    R login( @RequestBody UserLoginVO vo);
}

5.auth模块调用会员模块

@PostMapping("/login")
    public String login(UserLoginVO vo){
        //远程登录
        R r = memberFeignService.login(vo);
        if(r.getCode() == 0){
            //成功就去首页
            return "redirect:http://127.0.0.1:10000";
        } else {
            //失败就去登录页
            return "redirect:http://127.0.0.1:20000/login.html";
        }
    }

在这里插入图片描述
登录成功!!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84096.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!