Servicio de certificación de centros comerciales.
1. Construir un entorno de servicios de certificación
En combinación con la arquitectura del centro comercial que presentamos anteriormente, necesitamos crear un servicio de autenticación independiente.
1.Crear proyecto
Primero cree un proyecto SpringBoot y luego agregue las dependencias correspondientes
<?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. Configuración del centro de registro
Necesitamos registrar el servicio de autenticación en Nacos, agregar las dependencias correspondientes y luego completar la configuración correspondiente.
# 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
Liberar el centro de registro de Nacos
Luego comienza la prueba.
3. Página de inicio de sesión y registro
Luego organizamos los recursos relacionados para el inicio de sesión y el registro. Primero, copiamos los archivos de plantilla de inicio de sesión y registro en el proyecto.
Luego copie los archivos estáticos correspondientes a Nginx.
Luego necesitamos agregar la configuración correspondiente al archivo host.
Modificar la configuración del proxy inverso de Nginx
Luego reinicie el servicio Nginx
Luego modifique el servicio de puerta de enlace.
Finalmente, ajuste las rutas a los archivos de recursos estáticos de las páginas de inicio de sesión y registro.
Registre el nombre del servicio: msb-auth, inicie el servicio correspondiente y pruebe
página de inicio de sesión
pagina de registro
4.Función de registro
4.1 Código de verificación del teléfono móvil
Procese primero la página del código de verificación para habilitar la operación de cuenta regresiva
código JS
$(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 Interfaz de verificación por SMS
Se realiza a través del servicio de SMS de Alibaba Cloud. Podemos comprar 0 yuanes 15 veces directamente. https://www.aliyun.com/
Ingrese a la consola de administración correspondiente y visualice la información correspondiente.
Se puede desarrollar a través de la plantilla de desarrollo del lenguaje de programación correspondiente proporcionada por el proveedor de SMS.
El proveedor proporciona la clase de herramienta HttpUtils correspondiente, que debemos descargar y guardar en nuestro propio proyecto.
Luego encapsule la interfaz correspondiente para enviar códigos de verificación.
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();
}
}
}
Agregar información de atributo correspondiente
4.3 Función SMS
Luego concatenamos la función SMS.
4.3.1 terceros servicios
Necesitamos proporcionar servicios de interfaz externa en servicios de terceros.
@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 Servicio de autenticación
Necesitamos llamar al servicio de SMS proporcionado por terceros mediante fingir en el servicio de autenticación y, al mismo tiempo, proporcionar una interfaz de acceso para el cliente.
<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>
Publicar anotaciones
Luego declara la interfaz de Feign
Definir el controlador correspondiente
@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 Cliente
Luego necesitamos enviar mensajes de texto a través del envío asincrónico de jQuery en la página.
4.3.4 Almacenamiento del código de verificación
Cuando enviamos el código de verificación al teléfono móvil de su cliente en forma de SMS, necesitamos almacenar el número de teléfono móvil y el código de verificación correspondiente, que puede implementarse en un grupo más adelante. En este momento, almacenamos esta información en Redis.
Agregar configuración
Almacenamiento de datos
、
Hecho
4.3.5 Intervalo de 60 segundos
El intervalo de envío de códigos de verificación debe ser superior a 60 segundos, en este momento podemos sumar el tiempo de envío al valor de los datos guardados en Redis.
Las páginas de plantilla también deben procesarse en consecuencia.
4.4 Verificación de datos de registro
Verificamos los datos de registro enviados a través del formulario a través de JSR303.
Primero defina el objeto VO
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; // 验证码
}
Luego está el código lógico del controlador.
@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";
}
Luego está el procesamiento del código de la página.
Verificación del código de verificación.
4.5 Función de registro completa
Procesamiento de servicios para miembros
controlador
/**
* 会员注册
* @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();
}
Luego el Servicio correspondiente
/**
* 完成会员的注册功能
* @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);
}
El servicio de autenticación se llama de forma remota a través de Fegin.
llamada remota
@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";
}
Completar el registro de servicio correspondiente
Mensaje de verificación