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.
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
Then start the test
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.
Then copy the corresponding static files to Nginx.
Then we need to add the corresponding configuration to the host file
Modify the configuration of Nginx reverse proxy
Then restart the Nginx service
Then modify the gateway service
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
registration page
4.Registration function
4.1 Mobile phone verification code
Process the verification code page first to enable the countdown operation
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/
Enter the corresponding management console and view the corresponding information.
It can be developed through the development template of the relevant programming language provided by the SMS provider.
The supplier provides the corresponding HttpUtils tool class, which we need to download and save into our own project.
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
4.3 SMS function
Then we connect the SMS functions together
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();
}
}
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
Then declare Feign's interface
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
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.
Add configuration
Storing data
、
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
Template pages also need to be processed accordingly.
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
Verification of verification code
4.5 Complete registration function
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
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
Verification prompt