Day421&422.认证服务 -谷粒商城

Posted 阿昌喜欢吃黄桃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day421&422.认证服务 -谷粒商城相关的知识,希望对你有一定的参考价值。

认证服务

一、初始化

  • 创建认证模块

  • 统一springboot版本2.2.1.RELEASE,并引入Common服务依赖,因为不操作数据库,所以排除mybaitsplus依赖
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<dependency>
    <groupId>com.achang.achangmall</groupId>
    <artifactId>achangmall-common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <exclusions>
        <exclusion>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  • application.properties
spring.application.name=achang-auth-server
spring.cloud.nacos.server-addr=127.0.0.1:8848
server.port=20000
spring.thymeleaf.cache=false
  • com.achang.achangmall.auth.AchangAuthServerApplication
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class AchangAuthServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(AchangAuthServerApplication.class, args);
    }
}
  • 启动服务

  • 发现服务注册进 Nacos

  • 拉入登录页面,将资料高级篇登录页面和注册页面放到 templates 下,并改名为login、reg.html
  • 为了测试直接访问登录页,把login.html改名为index.html

  • C:\\Windows\\System32\\drivers\\etc\\hosts,添加本地域名映射
192.168.109.101 auth.achangmall.com

  • 静态文件可以选择 nginx 动静分离配置

  • 修改reg.html、login.html里面的路径引用

  • 添加网关转发配置,achangmall-gateway/src/main/resources/application.yml

        - id: auth_route
          uri: lb://achang-auth-server
          predicates:
            - Host=auth.achangmall.com
  • 启动网关服务AchangmallGatewayApplication +AchangAuthServerApplication 测试转发效果

访问http://auth.achangmall.com/,访问成功!!!

  • 修改product服务的注册和登录的uri

  • com.achang.achangmall.auth.controller.LoginController
@Controller
public class LoginController {
    @GetMapping("/login.html")
    public String loginPage(){
        return "login";
    }

    @GetMapping("/reg.html")
    public String register(){
        return "reg";
    }
}
  • achang-auth-server/src/main/resources/templates/login.html

  • achang-auth-server/src/main/resources/templates/reg.html


二、短信验证码

  • 前端验证码代码
<a id="sendCode">发送验证码</a>
$(function (){
    $("#sendCode").click(function (){
        if ($(this).hasClass("disabled")){
            //todo 发送手机验证码业务
        }{
            timeoutChangeSytle()
        }
    });
})
var num = 60;
function timeoutChangeSytle(){
    $("#sendCode").attr("class","disabled")
    if (num==0){
        $("#sendCode").text("发送验证码")
        num = 60;
        $("#sendCode").attr("class","")
    }else {
        var str = num+"后再次发送"
        $("#sendCode").text(str)
        setTimeout("timeoutChangeSytle()",1000)
    }
    num--;
}
  • 直接通过mvc做视图映射

com.achang.achangmall.auth.conf.AchangWebConfig

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

  • 购买短信第三方服务api
    https://market.aliyun.com/products/57126001/cmapi00040066.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IIijPFAa-1634478751848)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20211017162832535.png)]

  • 工具类httputils

https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java

  • 解耦配置文件
spring:
  application:
    name: achangmall-third-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    alicloud:
      sms:
        host: https://intlsms.market.alicloudapi.com
        path: /comms/sms/sendmsgall
        method: POST
        appcode: 你的appcode
        channel: 0
        templateID: '0000000'
  • 封装发送验证码组件
@Component
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Data
public class SmsComponent {
    private String host;
    private String path;
    private String method;
    private String appcode;
    private String channel;
    private String templateID;

    public void sendCode(String phone,String code){
        Map<String, String> headers = new HashMap<String, String>();
        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("callbackUrl", "http://test.dev.esandcloud.com");
        bodys.put("channel", channel);
        bodys.put("mobile", "+86"+phone);
        bodys.put("templateID", templateID);
        bodys.put("templateParamSet", code+", 1");
        
        try {
            HttpResponse  response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
            System.out.println(response.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 测试
@Test
public void test(){
    smsComponent.sendCode("13567790741","6379");
}
  • 发送短信接口

com.achang.achangmall.controller.SmsSendController

@RestController
@RequestMapping(value = "/sms")
public class SmsSendController {

    @Resource
    private SmsComponent smsComponent;

    /**
     * 提供给别的服务进行调用
     * @param phone
     * @param code
     * @return
     */
    @GetMapping(value = "/sendCode")
    public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code) {
        //发送验证码
        smsComponent.sendCode(phone,code);
        return R.ok();
    }

}
  • com.achang.achangmall.auth.feign.ThirdPartFeignService
@FeignClient("achangmall-third-service")
public interface ThirdPartFeignService {
    @GetMapping(value = "/sms/sendCode")
    public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code);
}
  • achang-auth-server/src/main/resources/templates/reg.html
<input class="phone" id="phoneNum" maxlength="20" type="text" placeholder="建议使用常用手机">
$(function (){
    $("#sendCode").click(function (){
        if ($(this).hasClass("disabled")){
        }{
            var phone = $("#phoneNum").val();
            $.get("/sms/sendCode?phone="+phone);
            timeoutChangeSytle()
        }
    });
})
  • 引入redis依赖

achang-auth-server/pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • redis配置
spring.redis.port=6379
spring.redis.host=192.168.109.101
  • com.achang.achangmall.auth.controller.LoginController
@Controller
public class LoginController {
    @Autowired
    private ThirdPartFeignService thirdPartFeignService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @GetMapping("/sms/sendCode")
    @ResponseBody
    public R sendCode(@RequestParam("phone")String phone){

        //防止接口幂等性操作
        String phoneRedisStr = stringRedisTemplate.opsForValue().get("sms:code:"+phone);
        if (!StringUtils.isEmpty(phoneRedisStr)){
            //活动存入redis的时间,用当前时间减去存入redis的时间,判断用户手机号是否在60s内发送验证码
            long currentTime = Long.parseLong(phoneRedisStr.split("_")[1]);
            if (System.currentTimeMillis() - currentTime < 60000) {
                //60s内不能再发
                return R.error("发送频率过多,请稍后重试");
            }
        }

        String code = UUID.randomUUID().toString().substring(0, 5);
        String redisStorage = code + "_" + System.currentTimeMillis();

        //存入redis,防止同一个手机号在60秒内再次发送验证码
        stringRedisTemplate.opsForValue().set("sms:code:"+ phone,
                redisStorage, 10, TimeUnit.MINUTES);

        thirdPartFeignService.sendCode(phone,code);
        return R.ok();
    }
}

三、登录注册功能

  • com.achang.achangmall.auth.vo.UserRegisterVo

通过注解可以给前端传递过来的值进行校验,例如:

@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;

}

但是这个注解必须配合 @Valid 使用,完成对参数的校验:

  • com.achang.achangmall.auth.controller.LoginController
/**
     *
     * TODO: 重定向携带数据:利用session原理,将数据放在session中。
     * TODO:只要跳转到下一个页面取出这个数据以后,session里面的数据就会删掉
     * TODO:分布下session问题
     * RedirectAttributes:重定向也可以保留数据,不会丢失
     * 用户注册
     * @return
     */
@PostMapping(value = "/register")
public String register(@Valid UserRegisterVo vos, BindingResult result,
                       RedirectAttributes attributes) {

    //如果有错误回到注册页面
    if (result.hasErrors()) {
        Map<String, String> errors = result.getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
        attributes.addFlashAttribute("errors",errors);

        //效验出错回到注册页面
        return "redirect:http://auth.achangmall.com/reg.html";
    }

    //1、效验验证码
    String code = vos.getCode();

    //获取存入Redis里的验证码
    String redisCode = stringRedisTemplate.opsForValue().get("sms:code:" + vos.getPhone());
    if (!StringUtils.isEmpty(redisCode)) {
        //截取字符串
        if (code.equals(redisCode.split("_")[0])) {
            //删除验证码;令牌机制
            stringRedisTemplate.delete("sms:code:"+vos.getPhone());
            //验证码通过,真正注册,调用远程服务进行注册
            R register = memberFeignService.register(vos);
            if (register.getCode() == 0) {
                //成功
                return "redirect:http://auth.achangmall.com/login.html";
            } else {
                //失败
                Map<String, String> errors = new HashMap以上是关于Day421&422.认证服务 -谷粒商城的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #421 (Div. 2)

SWA2G422&485JK2G基础篇: STM32+W5500(以太网)实现MQTT通信控制,485/422透传通信

根据列的总和返回最小值和最大值

SWA2G422&485JK2G基础篇: 硬件使用说明

Heroku docker:发布:预计成功回应,得到422

Codeforces Round #422 (Div. 2)