22.认证服务-auth-server-注册功能

环境搭建

选择模块thymeleaf,web,openFeign,lombok,spring-boot-devTools

加入common模块(有注册中心之类的很多依赖)排除jdbc依赖

  <dependency>
            <groupId>com.wuyimin.gulimall</groupId>
            <artifactId>gulimall-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

配置yml文件

spring: #配置nacos
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

  application:
    name: gulimall-auth-server
server:
  port: 20000

 放入登录页面和注册页面,并且把静态资源转给nginx,login后改名为index

 网关配置

 添加域名

 登录准备

以前我们需要写一个空方法转发请求到登录注册页面

    @GetMapping("/login.html")
    public String loginPage(){
        return "login";
    }
    @GetMapping("/reg.html")
    public String regPage(){
        return "reg";
    }

现在使用映射配置类

@Configuration
public class MyWebConfig implements WebMvcConfigurer {
    //视图映射
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login.html").setViewName("login");
        registry.addViewController("/reg.html").setViewName("reg");
    }
}

整合短信验证码

https://market.aliyun.com/products/?keywords=短信验证码

去买一个免费的,复制一下测试代码,然后把appid换成自己的

正常来说是要用@ConfigurationProperties配置到配置文件里的,这里直接抽取不做配置

@Component
@Data
public class SmsComponent {
    private String host;
    private String path;
    private String templateId="908e94ccf08b4476ba6c876d13f084ad";
    private String smsSignId="2e65b1bb3d054466b82f0c9d125465e2";
    private String appCode="78442b1006ae490da40cedda6826c7b5";
    public void sendSmsCode(String phone,String code){
        String host = "https://gyytz.market.alicloudapi.com";
        String path = "/sms/smsSend";
        String method = "POST";
        String appcode = appCode;
        Map<String, String> headers = new HashMap<String, String>();
        //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
        headers.put("Authorization", "APPCODE " + appcode);
        Map<String, String> querys = new HashMap<String, String>();
        querys.put("mobile", phone);
        querys.put("param", "**code**:"+code+"**minute**:5");
        querys.put("smsSignId", smsSignId);
        querys.put("templateId", templateId);
        Map<String, String> bodys = new HashMap<String, String>();
        try {
            /**
             * HttpUtils请从
             * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
             * 下载
             */
            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();
        }
    } 
}

第三方模块远程被调用的类

@RestController
@RequestMapping("/sms")
public class SmsSendController {
    @Autowired
    SmsComponent smsComponent;
    //提供给别的服务进行调用
    @GetMapping("/sendcode")
    public R sendCode(@RequestParam("phone") String phone, @RequestParam("code")String code){
        smsComponent.sendSmsCode(phone,code);
        return R.ok();
    }
}

auth模块远程调用接口:

@FeignClient("gulimall-third-party")
public interface ThirdPartyFeignService {
    @GetMapping("/sms/sendcode")
    public R sendCode(@RequestParam("phone") String phone, @RequestParam("code")String code);
}

细化验证码:

1.接口防刷(每次只要刷新后就可以重发验证码)

2.验证码校验--存入redis中

接口防刷

redis依赖导入

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

配置redis的地址以及具体实现逻辑

spring:  
 redis:
    host: 192.168.116.128
@Slf4j
@Controller
public class LoginController {
    @Autowired
    ThirdPartyFeignService thirdPartyFeignService;
    @Autowired
    StringRedisTemplate redisTemplate;
    @ResponseBody
    @GetMapping("/sms/sendcode")
    public R sendCode(@RequestParam("phone") String phone){
        //TODO 接口防刷
        String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CAHE_PREFIX + phone);
        if(!StringUtils.isEmpty(redisCode)){
            long l=Long.parseLong(redisCode.split("_")[1]);//拿到时间
            if(System.currentTimeMillis()-l<60000){
                //60秒内不能再发
                return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(),BizCodeEnum.SMS_CODE_EXCEPTION.getMsg());
            }
        }
        String code = UUID.randomUUID().toString().substring(0, 5)+"_"+System.currentTimeMillis();//加上系统时间
        //验证码的再次校验,存入redis key-手机号 value-code
        redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CAHE_PREFIX+phone,code,10, TimeUnit.MINUTES);
        try {
            thirdPartyFeignService.sendCode(phone,code);//第三方服务
        } catch (Exception e) {
            log.warn("远程调用不知名错误 [无需解决]");
        }
        return R.ok();
    }
}

 注册页环境

踩坑

校验注解不生效是因为少了这个依赖

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

如果在controller类上添加了@Validated注解,错误会以500的响应模式出来

校验实体类

@Data
public class UserRegistVo {
    @NotEmpty(message = "用户名必须提交")
    @Length(min = 6,max = 18,message = "长度必须在6-18")
    private String userName;
    @NotEmpty(message = "密码必须提交")
    @Length(min = 6,max = 18,message = "长度必须在6-18")
    private String password;
    //第一个数组必须是1,第二个数字在3-9剩下9个数字在0-9,一共11位
    @NotEmpty(message = "手机号必须填写")
    @Pattern(regexp = "^[1]([3-9])[0-9]{9}$",message = "手机号格式不正确")
    private String phone;
    @NotEmpty(message = "验证码必须填写")
    private String code;
}

//TODO 重定向携带数据,利用session原理,将数据放在session中,只要跳到下一个页面,取出这个数据以后session里的数据就会删掉
    //TODO 分布式下的session问题
    //post请求不支持--我们配置的路径映射默认都是get方式才能访问,所以不能直接return“forward:/reg.html”,这样会直接把post请求发给页面
    @PostMapping("/regist")
    public String register(@Valid UserRegistVo vo, BindingResult result, RedirectAttributes attributes){//第三个参数是专门用来重定向携带数据的
        //注册成功会到登录页
        //1.判断校验是否通过
        Map<String, String> errors = new HashMap<>();
        if (result.hasErrors()){
            //1.1 如果校验不通过,则封装校验结果
            result.getFieldErrors().forEach(item->{
                // 获取错误的属性名和错误信息
                errors.put(item.getField(), item.getDefaultMessage());
                //1.2 将错误信息封装到session中
                attributes.addFlashAttribute("errors", errors);
            });
            //校验出错,重定向到注册页
            return "redirect:http://auth.gulimall.com/reg.html";//防止刷新的时候表单重复提交,采用重定向
        }else{
            return "redirect:http://auth.gulimall.com/login.html";
        }
    }
}

异常机制

远程的Member服务

    @PostMapping("/regist")
    public R regist(@RequestBody MemberRegisterVo vo){//远程服务必须要获得json对象
        try{
            memberService.regist(vo);
        }catch (Exception e){
            //对于不同的异常有不同的处理方式
        }
        return R.ok();
    }

查出默认等级的方法

    <select id="getDefaultLevel" resultType="com.wuyimin.gulimall.member.entity.MemberLevelEntity">
        select * from ums_member_level where default_status=1
    </select>

自定义异常,用于判断手机号与用户名是否已经存在

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

检查手机和用户名的函数

@Override
    public void checkPhone(String phone) throws PhoneExistException {
        Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone));
        if (count > 0) {
            throw new PhoneExistException();
        }
    }

    @Override
    public void checkUserName(String userName) throws UsernameExistException {
        Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("username", userName));
        if (count > 0) {
            throw new UsernameExistException();
        }
    }

现阶段的regist方法

 @Override
    public void regist(MemberRegisterVo vo) {
        MemberEntity memberEntity = new MemberEntity();
        MemberLevelEntity memberLevelEntity = memberLevelDao.getDefaultLevel();
        memberEntity.setLevelId(memberLevelEntity.getId());//默认等级为1普通会员
        //设置其他的默认信息
        //检查用户名和手机号的唯一性 为了让controller能感知异常,我们使用异常机制
        checkPhone(vo.getPhone());
        checkUserName(vo.getUserName());
        //设置用户名和手机
        memberEntity.setMobile(vo.getPhone());
        memberEntity.setUsername(vo.getUserName());
        //设置密码(密码需要进行加密存储)
        baseMapper.insert(memberEntity);
    }

MD5,盐值和BCrypt

MD5:信息摘要算法,是不可逆的算法--》但是利用其抗修改性,使用彩虹表可以暴力破解,所以不能直接存储

加盐:通过生产随机数和MD5字符串进行组合,数据库同时存储MD5值和盐值,验证正确的时候使用salt进行MD5即可

 @Override
    public void regist(MemberRegisterVo vo) {
        MemberEntity memberEntity = new MemberEntity();
        MemberLevelEntity memberLevelEntity = memberLevelDao.getDefaultLevel();
        memberEntity.setLevelId(memberLevelEntity.getId());
        checkPhone(vo.getPhone());
        checkUserName(vo.getUserName());
        memberEntity.setMobile(vo.getPhone());
        memberEntity.setUsername(vo.getUserName());
        //设置密码(密码需要进行加密存储)
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode(vo.getPassword());
        memberEntity.setPassword(encode);
        //其他的默认信息。。
        //保存
        baseMapper.insert(memberEntity);
    }

远程注册功能完善

 @PostMapping("/regist")
    public R regist(@RequestBody MemberRegisterVo vo){//远程服务必须要获得json对象
        try{
            memberService.regist(vo);
        }catch (PhoneExistException e){
           return R.error(BizCodeEnum.PHONE_EXIST_EXCEPTION.getCode(),BizCodeEnum.PHONE_EXIST_EXCEPTION.getMsg());
        }catch (UsernameExistException e){
            return R.error(BizCodeEnum.USER_EXIST_EXCEPTION.getCode(),BizCodeEnum.USER_EXIST_EXCEPTION.getMsg());
        }
        return R.ok();
    }

auth模块远程调用member模块的接口

@FeignClient("gulimall-member")
public interface MemberFeignService {
    @PostMapping("/member/member/regist")
    R regist(@RequestBody MemberRegisterVo vo);
}

 //TODO 重定向携带数据,利用session原理,将数据放在session中,只要跳到下一个页面,取出这个数据以后session里的数据就会删掉
    //TODO 分布式下的session问题
    //post请求不支持--我们配置的路径映射默认都是get方式才能访问,所以不能直接return“forward:/reg.html”,这样会直接把post请求发给页面
    @PostMapping("/regist")
    public String register(@Valid UserRegistVo vo, BindingResult result, RedirectAttributes attributes){//第三个参数是专门用来重定向携带数据的
        //注册成功会到登录页
        //1.判断校验是否通过
        Map<String, String> errors = new HashMap<>();
        if (result.hasErrors()){
            //1.1 如果校验不通过,则封装校验结果
            result.getFieldErrors().forEach(item->{
                // 获取错误的属性名和错误信息
                errors.put(item.getField(), item.getDefaultMessage());
                //1.2 将错误信息封装到session中
                attributes.addFlashAttribute("errors", errors);
            });
            //校验出错,重定向到注册页
            return "redirect:http://auth.gulimall.com/reg.html";//防止刷新的时候表单重复提交,采用重定向
        }else{
            //真正的注册
            //1.校验验证码
            String code=vo.getCode();
            String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CAHE_PREFIX + vo.getPhone());
            if(!StringUtils.isEmpty(redisCode)){
                if(code.equals(redisCode.split("_")[0])){
                    //删除验证码
                    redisTemplate.delete(AuthServerConstant.SMS_CODE_CAHE_PREFIX + vo.getPhone());
                    //验证码通过,调用远程接口进行服务注册
                    R r = memberFeignService.regist(vo);
                    if(r.getCode()==0){
                        //成功
                        return "redirect:http://auth.gulimall.com/login.html";
                    }else{
                        //调用失败,返回注册页并显示错误信息
                        String msg = (String) r.get("msg");
                        errors.put("msg", msg);
                        attributes.addFlashAttribute("errors", errors);
                        log.error("远程调用会员服务失败");
                        return "redirect:http://auth.gulimall.com/reg.html";
                    }
                }else{
                    //验证码没有匹配
                    errors.put("code","验证码错误");
                    attributes.addFlashAttribute("errors",errors);
                    return "redirect:http://auth.gulimall.com/reg.html";
                }
            }else{
                //没有验证码
                errors.put("code","验证码错误");
                attributes.addFlashAttribute("errors",errors);
                return "redirect:http://auth.gulimall.com/reg.html";
            }
        }
    }

修改验证码的bug,存进redis的是带uuid的但是传递进service服务的参数不该带uuid

 @ResponseBody
    @GetMapping("/sms/sendcode")
    public R sendCode(@RequestParam("phone") String phone){
        //TODO 接口防刷
        String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CAHE_PREFIX + phone);
        if(!StringUtils.isEmpty(redisCode)){
            long l=Long.parseLong(redisCode.split("_")[1]);//拿到时间
            if(System.currentTimeMillis()-l<60000){
                //60秒内不能再发
                return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(),BizCodeEnum.SMS_CODE_EXCEPTION.getMsg());
            }
        }
        String code = UUID.randomUUID().toString().substring(0, 5);
        String saveInRedis = code + "_" + System.currentTimeMillis();//加上系统时间
//验证码的再次校验,存入redis key-手机号 value-code
        redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CAHE_PREFIX+phone,saveInRedis,10, TimeUnit.MINUTES);
        try {
            thirdPartyFeignService.sendCode(phone,code);//第三方服务
        } catch (Exception e) {
            log.warn("远程调用不知名错误 [无需解决]");
        }
        return R.ok();
    }

至此所有注册功能已经完成

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用以下命令启动这些 jar 包: ``` nohup java -jar codegen-service.jar > codegen-service.log & nohup java -jar data-market-service-integration.jar > data-market-service-integration.log & nohup java -jar data-market-service-mapping.jar > data-market-service-mapping.log & nohup java -jar data-market-service.jar > data-market-service.log & nohup java -jar data-masterdata-service.jar > data-masterdata-service.log & nohup java -jar data-metadata-service-console.jar > data-metadata-service-console.log & nohup java -jar data-metadata-service.jar > data-metadata-service.log & nohup java -jar data-quality-service.jar > data-quality-service.log & nohup java -jar data-standard-service.jar > data-standard-service.log & nohup java -jar data-visual-service.jar > data-visual-service.log & nohup java -jar datax-auth.jar > datax-auth.log & nohup java -jar datax-config.jar > datax-config.log & nohup java -jar datax-eureka.jar > datax-eureka.log & nohup java -jar datax-gateway.jar > datax-gateway.log & nohup java -jar datax-tool-monitor.jar > datax-tool-monitor.log & nohup java -jar email-service.jar > email-service.log & nohup java -jar file-service.jar > file-service.log & nohup java -jar quartz-service.jar > quartz-service.log & nohup java -jar system-service.jar > system-service.log & nohup java -jar workflow-service.jar > workflow-service.log & ``` 其中,`nohup` 命令可以使程序在后台运行,并将输出重定向到日志文件中。`&` 符号表示在后台运行该命令。请确保在执行此命令之前,已经安装了 Java 运行时环境,并且当前路径下存在相应的 jar 文件。如果 jar 文件不在当前路径下,可以使用绝对路径或相对路径指定文件的位置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值