[Business Function Chapter 94] Microservice-springcloud-springboot-authentication service-registration function-third-party SMS verification API

Mall certification service

1. Build a certification service environment

  Combined with the mall architecture we introduced earlier, we need to build an authentication service separately.

image.png

1.Create project

  First create a SpringBoot project, then add the corresponding dependencies

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.msb.mall</groupId>
    <artifactId>mall-auth-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mall-auth_server</name>
    <description>认证服务</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2020.0.1</spring-cloud.version>
    </properties>
    <dependencies>
        <!-- 公共依赖 -->
        <dependency>
            <groupId>com.msb.mall</groupId>
            <artifactId>mall-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2. Registration center configuration

  We need to register the authentication service in Nacos, add the corresponding dependencies, and then complete the corresponding configuration

# Nacos服务注册
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.56.100:8848
  application:
    name: mall-auth_server
  # 统一的全局的--设置服务器响应给客户端的日期时间格式
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
  thymeleaf:
    cache: false # 关闭Thymeleaf的缓存
server:
  port: 30000

Release the Nacos registration center

image.png

Then start the test

image.png

3. Login and registration page

  Then we organize the related resources for login and registration. First, copy the login and registration template files into the project.

image.png

Then copy the corresponding static files to Nginx.

image.png

Then we need to add the corresponding configuration to the host file

image.png

Modify the configuration of Nginx reverse proxy

image.png

Then restart the Nginx serviceimage.png

Then modify the gateway service

image.png

Finally, adjust the paths to the static resource files of the login and registration pages.

Register the name of the service: msb-auth, start the corresponding service, and test

log in page

image.png

registration page

image.png

4.Registration function

4.1 Mobile phone verification code

  Process the verification code page first to enable the countdown operation

image.png

image.png

JS code

$(function(){
    
    
				$("#sendCode").click(function(){
    
    
					if($(this).hasClass("d1")){
    
    
						// 说明正在倒计时
					}else{
    
    
						// 给指定的手机号发送验证码
						timeoutChangeStyle()
					}

				});
			})
			var num = 10
			function timeoutChangeStyle(){
    
    
				$("#sendCode").attr("class","d1")
				if(num == 0){
    
    
					// 说明1分钟到了,可以再次发送验证码了
					$("#sendCode").text("发送验证码")
					num= 10;
					$("#sendCode").attr("class","")
				}else{
    
    
					setTimeout('timeoutChangeStyle()',1000)
					$("#sendCode").text(num+"s后再次发送")
				}

				num --;
			}

4.2 SMS verification interface

  This is achieved through Alibaba Cloud’s SMS service. We just need to purchase it directly for 0 yuan 15 times. https://www.aliyun.com/

image.png

image.png

Enter the corresponding management console and view the corresponding information.

image.png

It can be developed through the development template of the relevant programming language provided by the SMS provider.

image.png

The supplier provides the corresponding HttpUtils tool class, which we need to download and save into our own project.

image.png

Then encapsulate the corresponding interface for sending verification codes

package com.msb.mall.third.utils;

import lombok.Data;
import org.apache.http.HttpResponse;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * 短信组件
 */
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Data
@Component
public class SmsComponent {
    
    

    private String host;
    private String path;
    private String method = "POST";
    private String appCode;

    /**
     * 发送短信验证码
     * @param phone 发送的手机号
     * @param code 发送的短信验证码
     */
    public void sendSmsCode(String phone,String code){
    
    
        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();
        }
    }
}

Add corresponding attribute information

image.png

image.png

4.3 SMS function

  Then we connect the SMS functions together

image.png

4.3.1 third services

  We need to provide external interface services in third-party services

@RestController
public class SMSController {
    
    

    @Autowired
    private SmsComponent smsComponent;

    /**
     * 调用短信服务商提供的短信API发送短信
     * @param phone
     * @param code
     * @return
     */
    @GetMapping("/sms/sendcode")
    public R sendSmsCode(@RequestParam("phone") String phone,@RequestParam("code") String code){
    
    
        smsComponent.sendSmsCode(phone,code);
        return R.ok();
    }
}

image.png

4.3.2 Authentication services

  We need to call the SMS service provided in third through feign in the authentication service, and at the same time provide an access interface for the client.

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

Release annotations

image.png

Then declare Feign's interface

image.png

Define the corresponding Controller

@Controller
public class LoginController {
    
    


    @Autowired
    private ThirdPartFeginService thirdPartFeginService;

    @ResponseBody
    @GetMapping("/sms/sendCode")
    public R sendSmsCode(@RequestParam("phone") String phone){
    
    
        // 生成随机的验证码
        String code = UUID.randomUUID().toString().substring(0, 5);
        thirdPartFeginService.sendSmsCode(phone,code);
        return R.ok();
    }
}

4.3.3 Client

  Then we need to send text messages through jQuery's asynchronous submission in the page

image.png

4.3.4 Verification code storage

  When we send the verification code to your customer's mobile phone via text message, we then need to store the mobile phone number and the corresponding verification code, which may be deployed in a cluster later. At this time, we store this information in Redis.

image.png

Add configuration

image.png

Storing data

image.png

image.png

Done

4.3.5 60 second interval

  The interval for sending the verification code must be more than 60 seconds. At this time, we can add the sending time to the value of the data saved in Redis to process

image.png

Template pages also need to be processed accordingly.

image.png

4.4 Registration data verification

  The registration data submitted by the form is verified by JSR303.

First define the VO object

package com.msb.mall.vo;

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;

/**
 * 注册用户的VO对象
 */
@Data
public class UserRegisterVo {
    
    

    @NotEmpty(message = "账号不能为空")
    @Length(min = 3,max = 15,message = "账号必须是3~15位")
    private String userName; // 账号

    @NotEmpty(message = "密码不能为空")
    @Length(min = 3,max = 15,message = "密码必须是3~15位")
    private String password; // 密码

    @NotEmpty(message = "手机号不能为空")
    @Pattern(regexp = "^[1][3-9][0-9]{9}$",message = "手机号不合法")
    private String phone;  // 手机号

    @NotEmpty(message = "验证码不能为空")
    private String code;  // 验证码
}

Then there is the logic code of the controller

    @PostMapping("/sms/register")
    public String register(@Valid UserRegisterVo vo, BindingResult result, Model model){
    
    
        if(result.hasErrors()){
    
    
            // 表示提交的数据不合法
            List<FieldError> fieldErrors = result.getFieldErrors();
            Map<String,String> map = new HashMap<>();
            for (FieldError fieldError : fieldErrors) {
    
    
                String field = fieldError.getField();
                String defaultMessage = fieldError.getDefaultMessage();
                map.put(field,defaultMessage);
            }
            model.addAttribute("error",map);
            return "/reg";
        }
        // 表单提交的注册的数据是合法的

        return "redirect:/login.html";
    }

Then there is the page code processing

image.png

image.png

Verification of verification code

image.png

4.5 Complete registration function

image.png

Member service processing

controller

   /**
     * 会员注册
     * @return
     */
    @PostMapping("/register")
    public R register(@RequestBody MemberReigerVO vo){
    
    
        try {
    
    
            memberService.register(vo);
        }catch (UsernameExsitException exception){
    
    
            return R.error(BizCodeEnume.USERNAME_EXSIT_EXCEPTION.getCode(),
                    BizCodeEnume.USERNAME_EXSIT_EXCEPTION.getMsg());
        }catch (PhoneExsitExecption exsitExecption) {
    
    
            return R.error(BizCodeEnume.PHONE_EXSIT_EXCEPTION.getCode(),
                    BizCodeEnume.PHONE_EXSIT_EXCEPTION.getMsg());
        }catch (Exception e){
    
    
            return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),
                    BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
        }

        return R.ok();
    }

Then the corresponding Service

   /**
     * 完成会员的注册功能
     * @param vo
     */
    @Override
    public void register(MemberReigerVO vo) throws PhoneExsitExecption,UsernameExsitException{
    
    
        MemberEntity entity = new MemberEntity();
        // 设置会员等级 默认值
        MemberLevelEntity memberLevelEntity = memberLevelService.queryMemberLevelDefault();
        entity.setLevelId(memberLevelEntity.getId()); // 设置默认的会员等级

        // 添加对应的账号和手机号是不能重复的
        checkUsernameUnique(vo.getUserName());
        checkPhoneUnique(vo.getPhone());

        entity.setUsername(vo.getUserName());
        entity.setMobile(vo.getPhone());

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String encode = encoder.encode(vo.getPassword());
        // 需要对密码做加密处理
        entity.setPassword(encode);
         // 设置其他的默认值
        this.save(entity);
    }

The auth service is called remotely through Fegin

image.png

remote call

    @PostMapping("/sms/register")
    public String register(@Valid UserRegisterVo vo, BindingResult result, Model model){
    
    
        Map<String,String> map = new HashMap<>();
        if(result.hasErrors()){
    
    
            // 表示提交的数据不合法
            List<FieldError> fieldErrors = result.getFieldErrors();

            for (FieldError fieldError : fieldErrors) {
    
    
                String field = fieldError.getField();
                String defaultMessage = fieldError.getDefaultMessage();
                map.put(field,defaultMessage);
            }
            model.addAttribute("error",map);
            return "/reg";
        }else{
    
    
            // 验证码是否正确
             String code = (String)redisTemplate.opsForValue().get(SMSConstant.SMS_CODE_PERFIX + vo.getPhone());
            code = code.split("_")[0];
            if(!code.equals(vo.getCode())){
    
    
                // 说明验证码不正确
                map.put("code","验证码错误");
                model.addAttribute("error",map);
                return "/reg";
            }else{
    
    
                // 验证码正确  删除验证码
                redisTemplate.delete(SMSConstant.SMS_CODE_PERFIX + vo.getPhone());
                // 远程调用对应的服务 完成注册功能
                R r = memberFeginService.register(vo);
                if(r.getCode() == 0){
    
    
                    // 注册成功
                    return "redirect:http://msb.auth.com/login.html";
                }else{
    
    
                    // 注册失败
                    map.put("msg",r.getCode()+":"+r.get("msg"));
                    model.addAttribute("error",map);
                    return "/reg";
                }
            }
        }



        //return "redirect:/login.html";
    }

Complete the corresponding service registration

image.png

Verification prompt

image.png

Guess you like

Origin blog.csdn.net/studyday1/article/details/132637223