文章目录

  • 1. 搭建用户微服务
    • 1.1 用户微服务的结构
    • 1.2 创建 leyou-user
    • 1.3 创建 leyou-user-interface
    • 1.4 创建 leyou-user-service
    • 1.5 添加路由规则
  • 2. 后台功能准备
    • 2.1 接口文档
    • 2.2 用户表
    • 2.3 基本代码
      • 2.3.1 实体类
      • 2.3.2 Mapper
      • 2.3.3 Service
      • 2.3.4 Controller
  • 3. 实现数据校验功能
    • 3.1 接口说明
    • 3.2 Controller
    • 3.3 Service
    • 3.4 测试
  • 4. 实现发送短信功能
    • 4.1 开通阿里云短信服务
    • 4.2 测试发送短信
    • 4.3 搭建短信微服务
      • 4.3.1 创建 leyou-sms
      • 4.3.2 编写短信工具类
      • 4.3.3 测试
  • 5. 实现发送验证码功能
    • 5.1 接口说明
    • 5.2 添加依赖
    • 5.3 配置文件
    • 5.4 生成验证码工具类
    • 5.5 Controller
    • 5.6 Service
    • 5.7 测试
  • 6. 实现用户注册功能
    • 6.1 接口说明
    • 6.2 加密工具类
    • 6.3 Controller
    • 6.4 Service
    • 6.5 测试
    • 6.6 数据校验
      • 6.6.1 Hibernate-Validator 简介
      • 6.6.2 Bean 校验的注解
      • 6.6.3 实现数据校验
    • 6.7 再次测试
  • 7. 在注册页完成注册
  • 8. 实现查询用户功能
    • 8.1 接口说明
    • 8.2 Controller
    • 8.3 Service
    • 8.4 测试

1. 搭建用户微服务

用户搜索到自己心仪的商品,接下来就要去购买,但是购买必须先登录。所以接下来我们编写用户微服务,实现用户的注册和登录功能。

1.1 用户微服务的结构

由于其它微服务也会调用用户微服务,因此这里我们需要使用聚合工程。

我们会在 leyou-user 中创建两个子工程:

  • leyou-user-interface:主要是对外暴露的接口及相关实体类
  • leyou-user-service:所有业务逻辑及内部使用接口

1.2 创建 leyou-user

  1. 右键 leyou 项目 --> New Module --> Maven --> Next

  2. 填写项目信息 --> Next

  3. 填写保存的位置 --> Finish

  4. 删除 src 目录

1.3 创建 leyou-user-interface

  1. 右键 leyou-user 项目 --> New Module --> Maven --> Next

  2. 填写项目信息 --> Next

  3. 填写保存的位置 --> Finish

  4. 添加依赖

    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>leyou-user</artifactId><groupId>com.leyou.user</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><groupId>com.leyou.user</groupId><artifactId>leyou-user-interface</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>tk.mybatis</groupId><artifactId>mapper-spring-boot-starter</artifactId></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.10.0</version></dependency></dependencies>
    </project>
    

1.4 创建 leyou-user-service

  1. 右键 leyou-user 项目 --> New Module --> Maven --> Next

  2. 填写项目信息 --> Next

  3. 填写保存的位置 --> Finish

  4. 项目结构如下

  5. 添加依赖

    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>leyou-user</artifactId><groupId>com.leyou.user</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><groupId>com.leyou.user</groupId><artifactId>leyou-user-service</artifactId><version>1.0-SNAPSHOT</version><dependencies><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--spring-boot-starter--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!--eureka-client--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><!--jdbc--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- mysql 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- mybatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!-- 通用 Mapper --><dependency><groupId>tk.mybatis</groupId><artifactId>mapper-spring-boot-starter</artifactId></dependency><!--leyou-user-interface--><dependency><groupId>com.leyou.user</groupId><artifactId>leyou-user-interface</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>
    
  6. 编写启动类

    @SpringBootApplication
    @EnableDiscoveryClient
    @MapperScan("com.leyou.user.mapper")
    public class LeyouUserApplication {public static void main(String[] args) {SpringApplication.run(LeyouUserApplication.class,args);}
    }
    
  7. 编写配置文件

    server:port: 8085
    spring:application:name: user-servicedatasource:url: jdbc:mysql://localhost:3306/leyouusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver
    eureka:client:service-url:defaultZone: http://localhost:10086/eurekainstance:lease-renewal-interval-in-seconds: 5lease-expiration-duration-in-seconds: 15
    mybatis:type-aliases-package: com.leyou.user.pojo
    
  8. 在 leyou-user-interface 中创建 com.leyou.user.pojo 包

1.5 添加路由规则

我们修改 leyou-gateway,添加路由规则,对 leyou-user-service 进行路由。

server:port: 10010
spring:application:name: leyou-gateway
eureka:client:service-url:defaultZone: http://localhost:10086/eurekaregistry-fetch-interval-seconds: 5
zuul:prefix: /apiroutes:item-service: /item/**search-service: /search/**user-service: /user/**

2. 后台功能准备

2.1 接口文档

整个用户微服务的开发,我们将模拟公司内面向接口的开发。现在项目经理已经设计好了接口文档《用户中心接口说明》,如下:

我们将根据文档直接编写后台功能,不关心页面实现。

2.2 用户表

CREATE TABLE `tb_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`username` varchar(50) NOT NULL COMMENT '用户名',`password` varchar(32) NOT NULL COMMENT '密码,加密存储',`phone` varchar(20) DEFAULT NULL COMMENT '注册手机号',`created` datetime NOT NULL COMMENT '创建时间',`salt` varchar(32) NOT NULL COMMENT '密码加密的salt值',PRIMARY KEY (`id`),UNIQUE KEY `username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COMMENT='用户表';

注意:由于根据用户名查询的频率较高,所以我们给用户名创建了索引

2.3 基本代码

2.3.1 实体类

在 com.leyou.user.pojo 包中添加 User 类

@Table(name = "tb_user")
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;// 用户名@JsonIgnoreprivate String password;// 密码private String phone;// 电话private Date created;// 创建时间@JsonIgnoreprivate String salt;// 密码的盐值
}

注意:为了安全考虑。这里对 password 和 salt 添加了注解 @JsonIgnore,这样在 json 序列化时,就不会把 password 和 salt 返回。

2.3.2 Mapper

在 com.leyou.user.mapper 包中添加 UserMapper 接口

public interface UserMapper extends Mapper<User> {}

2.3.3 Service

在 com.leyou.user.service 包中添加 UserService 类

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;
}

2.3.4 Controller

在 com.leyou.user.controller 包中添加 UserController 类

@Controller
public class UserController {@Autowiredprivate UserService userService;}

3. 实现数据校验功能

3.1 接口说明

实现用户数据的校验,包括对手机号、用户名的唯一性校验。

接口路径

GET /check/{data}/{type}

参数说明

参数 说明 是否必须 数据类型 默认值
data 要校验的数据 String
type 要校验的数据类型:1,用户名;2,手机; Integer 1

返回结果

返回布尔类型结果:

  • true:可用
  • false:不可用

状态码:

  • 200:校验成功
  • 400:参数有误
  • 500:服务器内部异常

3.2 Controller

根据接口文档可知:

  • 请求方式:GET
  • 请求路径:/check/{param}/{type}
  • 请求参数:param、type
  • 返回结果:true 或 false

在 UserController 添加 checkUserData 方法

/*** 校验数据是否可用** @param data* @param type* @return*/
@GetMapping("check/{data}/{type}")
public ResponseEntity<Boolean> checkUserData(@PathVariable("data") String data, @PathVariable(value = "type") Integer type) {Boolean bool = this.userService.checkData(data, type);if (bool == null) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();}return ResponseEntity.ok(bool);
}

3.3 Service

在 UserService 添加 checkData 方法

/*** 校验数据是否可用** @param data* @param type* @return*/
public Boolean checkData(String data, Integer type) {User user = new User();if (type == 1) {user.setUsername(data);} else if (type == 2) {user.setPhone(data);} else {return null;}// 返回数据库是否存在要校验的数据,存在则为 true,不存在则为 falsereturn this.userMapper.selectCount(user) == 0;
}

3.4 测试

  1. 打开 user 表,有两条数据

  2. 使用 Postman 发送 Get 请求,校验用户名 “zhangsan” 是否唯一,成功返回 false

4. 实现发送短信功能

注册页面上有短信发送的按钮,当用户点击发送短信,我们需要生成验证码,发送给用户。

我们将使用阿里云提供的短信服务来实现发送短信功能。

4.1 开通阿里云短信服务

  1. 进入阿里云官网,在搜索栏搜索 “短信服务”

    https://www.aliyun.com/
    

  2. 点击立即开通

  3. 点击管理控制台

  4. 点击国内消息,添加签名。签名就是短信内容头部的标签,标注短信发送者的身份。

  5. 点击模板管理,添加模板。模板就是短信的内容,包含验证码变量。

  6. 点击右上角头像,AccessKey 管理

  7. 点击创建 AccessKey。 AccessKey ID 和 AccessKey Secret 是访问阿里云 API 的密钥,具有该账户完全的权限。

  8. 点击费用,充值。短信服务发送的每条短信收费 0.045 元,大概充值 2-3 元即可。

4.2 测试发送短信

  1. 进入管理控制台,点击快速入门,选择签名和模板等,右边就会生成短信。点击查看 API Demo。

  2. 可以看到右边已经生成 Java 发送短信的代码了,将它复制下来。

  3. 创建一个 Maven 工程,在 pom 文件中添加依赖。

    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.sms</groupId><artifactId>sms-test</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.0.3</version></dependency></dependencies>
    </project>
    
  4. 创建 SendSms 类,填写你自己的 accessKeyId,accessSecret

    import com.aliyuncs.CommonRequest;
    import com.aliyuncs.CommonResponse;
    import com.aliyuncs.DefaultAcsClient;
    import com.aliyuncs.IAcsClient;
    import com.aliyuncs.exceptions.ClientException;
    import com.aliyuncs.exceptions.ServerException;
    import com.aliyuncs.http.MethodType;
    import com.aliyuncs.profile.DefaultProfile;public class SendSms {public static void main(String[] args) {DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<accessKeyId>", "<accessSecret>");IAcsClient client = new DefaultAcsClient(profile);CommonRequest request = new CommonRequest();request.setMethod(MethodType.POST);request.setDomain("dysmsapi.aliyuncs.com");request.setVersion("2017-05-25");request.setAction("SendSms");request.putQueryParameter("RegionId", "cn-hangzhou");request.putQueryParameter("PhoneNumbers", "12345678901");request.putQueryParameter("SignName", "乐优商城");request.putQueryParameter("TemplateCode", "SMS_189236195");request.putQueryParameter("TemplateParam", "{\"code\":\"123456\"}");try {CommonResponse response = client.getCommonResponse(request);System.out.println(response.getData());} catch (ServerException e) {e.printStackTrace();} catch (ClientException e) {e.printStackTrace();}}
    }
    
  5. 启动运行,短信发送成功

4.3 搭建短信微服务

因为系统中不止注册一个地方需要短信发送,因此我们将短信发送抽取为微服务:leyou-sms,凡是需要的地方都可以使用。

另外,因为短信发送 API 调用时长的不确定性,为了提高程序的响应速度,短信发送我们都将采用异步发送方式,即:

  • 其它服务要发送短信时,发送消息给 RabbitMQ。
  • 短信服务监听 RabbitMQ 消息,收到消息后发送短信。

4.3.1 创建 leyou-sms

  1. 右键 leyou 项目 --> New Module --> Maven --> Next

  2. 填写项目信息 --> Next

  3. 填写保存的位置 --> Finish

  4. 添加依赖

    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>leyou</artifactId><groupId>com.leyou.parent</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><groupId>com.leyou.sms</groupId><artifactId>leyou-sms</artifactId><version>1.0-SNAPSHOT</version><dependencies><!--amqp--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--aliyun-java-sdk-core--><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.0.3</version></dependency><!--test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies></project>
    
  5. 编写启动类

    @SpringBootApplication
    public class LeyouSmsApplication {public static void main(String[] args) {SpringApplication.run(LeyouSmsApplication.class, args);}
    }
    
  6. 编写配置文件 application.yaml

    server:port: 8086
    spring:application:name: sms-servicerabbitmq:host: 127.0.0.1username: leyoupassword: leyouvirtual-host: /leyou
    

4.3.2 编写短信工具类

  1. 把短信服务的一些常量抽取到 application.yaml 中

    leyou:sms:accessKeyId: JWffwFJIwada # 你自己的accessKeyIdaccessKeySecret: aySRliswq8fe7rF9gQyy1Izz4MQ # 你自己的AccessKeySecretsignName: 乐优商城 # 签名名称verifyCodeTemplate: SMS_133976814 # 模板名称
    
  2. 创建属性读取类

    @ConfigurationProperties(prefix = "leyou.sms")
    public class SmsProperties {String accessKeyId;String accessKeySecret;String signName;String verifyCodeTemplate;public String getAccessKeyId() {return accessKeyId;}public void setAccessKeyId(String accessKeyId) {this.accessKeyId = accessKeyId;}public String getAccessKeySecret() {return accessKeySecret;}public void setAccessKeySecret(String accessKeySecret) {this.accessKeySecret = accessKeySecret;}public String getSignName() {return signName;}public void setSignName(String signName) {this.signName = signName;}public String getVerifyCodeTemplate() {return verifyCodeTemplate;}public void setVerifyCodeTemplate(String verifyCodeTemplate) {this.verifyCodeTemplate = verifyCodeTemplate;}
    }
    
  3. 编写发送短信工具类

    @Component
    @EnableConfigurationProperties(SmsProperties.class)
    public class SmsUtils {@Autowiredprivate SmsProperties smsProperties;public void sendSms(String phone, String code, String signName, String template) {DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret());IAcsClient client = new DefaultAcsClient(profile);CommonRequest request = new CommonRequest();request.setMethod(MethodType.POST);request.setDomain("dysmsapi.aliyuncs.com");request.setVersion("2017-05-25");request.setAction("SendSms");request.putQueryParameter("RegionId", "cn-hangzhou");request.putQueryParameter("PhoneNumbers", phone);request.putQueryParameter("SignName", signName);request.putQueryParameter("TemplateCode", template);request.putQueryParameter("TemplateParam", "{\"code\":\""+code+"\"}");try {CommonResponse response = client.getCommonResponse(request);System.out.println(response.getData());} catch (ServerException e) {e.printStackTrace();} catch (ClientException e) {e.printStackTrace();}}
    }
  4. 编写消息监听器

    消息体是一个 Map,里面有两个 key:

    • phone:电话号码
    • code:短信验证码
    @Component
    @EnableConfigurationProperties(SmsProperties.class)
    public class SmsListener {@Autowiredprivate SmsUtils smsUtils;@Autowiredprivate SmsProperties prop;/*** 处理发送短信消息** @param msg*/@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "leyou.sms.queue", durable = "true"),exchange = @Exchange(value = "leyou.sms.exchange",ignoreDeclarationExceptions = "true",type = ExchangeTypes.TOPIC),key = {"sms.verify.code"}))public void listenSms(Map<String, String> msg) {// 判断消息是否为空if (CollectionUtils.isEmpty(msg)) {return;}String phone = msg.get("phone");String code = msg.get("code");// 判断电话或验证码是否为空if (StringUtils.isBlank(phone) || StringUtils.isBlank(code)) {return;}// 发送消息smsUtils.sendSms(phone, code, prop.getSignName(), prop.getVerifyCodeTemplate());}
    }
    
  5. 最终目录结构

4.3.3 测试

  1. 启动 leyou-sms

  2. 打开 RabbitMQ 管理界面,成功生成队列和通道。


5. 实现发送验证码功能

前面我们已经实现了发送短信的功能,接下来我们在用户微服务中实现发送验证码的功能。

5.1 接口说明

发送验证码流程

  1. 我们接收页面发送来的手机号码
  2. 生成一个随机验证码
  3. 将验证码保存在服务端(用来校验用户输入的验证码)
  4. 发送消息(包括手机号、验证码)给 RabbitMQ
  5. 短信微服务监听 RabbitMQ 消息,收到消息后发送短信。

接口路径

POST /code

参数说明

参数 说明 是否必须 数据类型 默认值
phone 用户的手机号码 String

返回结果

  • 返回值:无

  • 状态码:

    • 201:请求已接收
    • 400:参数有误
    • 500:服务器内部异常

问题分析

那么问题来了:生成验证码后,验证码保存在哪里呢

验证码有一定有效期,一般是 5 分钟,我们可以利用 Redis 的过期机制来保存。

5.2 添加依赖

在 leyou-user-service 中添加依赖:

  • Spring Data Redis
  • Spring AMQP
  • leyou-common
<!--spring-data-redis-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--amqp-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--leyou-common-->
<dependency><groupId>com.leyou.common</groupId><artifactId>leyou-common</artifactId><version>1.0-SNAPSHOT</version>
</dependency>

5.3 配置文件

添加配置文件 application.yaml

spring:redis:host: 192.168.222.132rabbitmq:host: 127.0.0.1username: leyoupassword: leyouvirtual-host: /leyou

5.4 生成验证码工具类

在 leyou-common 中添加工具类 NumberUtils

public class NumberUtils {/*** 生成指定位数的随机数字* @param len* @return*/public static String generateCode(int len){len = Math.min(len, 8);int min = Double.valueOf(Math.pow(10, len - 1)).intValue();int num = new Random().nextInt(Double.valueOf(Math.pow(10, len + 1)).intValue() - 1) + min;return String.valueOf(num).substring(0,len);}
}

5.5 Controller

在 UserController 中添加 sendVerifyCode 方法

/*** 发送手机验证码* @param phone* @return*/
@PostMapping("code")
public ResponseEntity<Void> sendVerifyCode(@RequestParam("phone") String phone) {// 请求参数错误if(StringUtils.isBlank(phone)) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();}this.userService.sendVerifyCode(phone);return ResponseEntity.status(HttpStatus.CREATED).build();
}

5.6 Service

在 UserService 中添加 sendVerifyCode 方法

@Autowired
private AmqpTemplate amqpTemplate;@Autowired
private StringRedisTemplate stringRedisTemplate;private static final String KEY_PREFIX = "user:code:phone:"; // Redis 中 key 的前缀/*** 发送手机验证码** @param phone* @return*/
public void sendVerifyCode(String phone) {// 生成验证码String code = NumberUtils.generateCode(6);// 发送消息到 RabbitMQMap<String, String> msg = new HashMap<>();msg.put("phone", phone);msg.put("code", code);amqpTemplate.convertAndSend("leyou.sms.exchange", "sms.verify.code", msg);// 保存验证码到 Redis,5 分钟后过期stringRedisTemplate.opsForValue().set(KEY_PREFIX + phone, code, 5, TimeUnit.MINUTES);
}

5.7 测试

  1. 启动 leyou-gateway、leyou-register、leyou-user、leyou-sms 四个微服务

  2. 打开 Postman 发送一条 POST 请求,发送验证码,响应 201

  3. 打开 Redis,成功保存验证码

  4. 手机收到验证码

6. 实现用户注册功能

6.1 接口说明

用户注册流程

  1. 我们接收页面发送来的用户注册数据
  2. 校验短信验证码
  3. 生成盐,对密码进行加密
  4. 将用户注册数据写入 MySQL
  5. 删除 Redis 中的验证码

接口路径

POST /register

参数说明

以 form 表单格式提交:

参数 说明 是否必须 数据类型 默认值
username 用户名,格式为4~30位字母、数字、下划线 String
password 用户密码,格式为4~30位字母、数字、下划线 String
phone 手机号码 String
code 短信验证码 String

返回结果:

  • 返回值:无

  • 状态码:

    • 201:注册成功
    • 400:参数有误,注册失败
    • 500:服务器内部异常,注册失败

6.2 加密工具类

  1. 在 leyou-user-service 中添加工具类 CodecUtils

    public class CodecUtils {/*** MD5 加密** @param data* @param salt* @return*/public static String md5Hex(String data,String salt) {if (StringUtils.isBlank(salt)) {salt = data.hashCode() + "";}return DigestUtils.md5Hex(salt + DigestUtils.md5Hex(data));}/*** 生成 salt** @return*/public static String generateSalt(){return StringUtils.replace(UUID.randomUUID().toString(), "-", "");}
    }
    
  2. 该工具类需要 apache 的加密工具包

    <dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId>
    </dependency>
    

6.3 Controller

在 UserController 中添加 register 方法

/*** 用户注册** @param user* @param code* @return*/
@PostMapping("register")
public ResponseEntity<Void> register(User user, @RequestParam("code") String code) {this.userService.register(user, code);return ResponseEntity.status(HttpStatus.CREATED).build();
}

6.4 Service

在 UserService 中添加 register 方法

/*** 用户注册** @param user* @param code*/
public void register(User user, String code) {// 校验验证码String cacheCode = stringRedisTemplate.opsForValue().get(KEY_PREFIX + user.getPhone());if (!StringUtils.equals(code, cacheCode)) {return;}// 生成 saltString salt = CodecUtils.generateSalt();user.setSalt(salt);// 对密码数据进行加密user.setPassword(CodecUtils.md5Hex(user.getPassword(),salt));// 将用户注册数据写入 MySQLuser.setId(null);user.setCreated(new Date());userMapper.insertSelective(user);// 删除 Redis 中的验证码stringRedisTemplate.delete(KEY_PREFIX + user.getPhone());
}

6.5 测试

  1. 启动 leyou-gateway、leyou-register、leyou-user、leyou-sms 四个微服务

  2. 打开 Postman 发送一条 POST 请求,发送验证码,响应 201

  3. 打开 Redis,查看验证码

  4. 打开 Postman 发送一条 POST 请求,注册用户,响应 201

  5. 打开 MySQL,注册成功

  6. 打开 Redis,验证码被删除

6.6 数据校验

上面已经实现了用户注册,但服务端并没对用户的注册数据进行数据校验。如果有人绕过前端的数据校验,直接向服务端发送请求,也是可以注册成功的,这样显然是不安全的,所以我们必须在服务端也进行数据校验。

我们这里会使用 Hibernate-Validator 框架完成数据校验,Web 启动器中已经集成了相关依赖。

6.6.1 Hibernate-Validator 简介

Hibernate Validator 是 Hibernate 提供的一个开源框架,经常用来验证 bean 的字段,使用注解方式非常方便的实现服务端的数据校验

hibernate Validator 是 Bean Validation 的参考实现。Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint(约束) 的实现,除此之外还有一些附加的 constraint。

6.6.2 Bean 校验的注解

常用注解如下:

Constraint 详细信息
@Valid 被注释的元素是一个对象,需要检查此对象的所有字段值
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内
@NotBlank 被注释的字符串的必须非空
@URL(protocol=,host=, port=,regexp=, flags=) 被注释的字符串必须是一个有效的 url
@CreditCardNumber 被注释的字符串必须通过 Luhn 校验算法,银行卡,信用卡等号码一般都用 Luhn 计算合法性

6.6.3 实现数据校验

  1. 我们在 leyou-user-interface 中添加 Hibernate-Validator 依赖

    <dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId>
    </dependency>
    
  2. 在 User 对象的部分属性上添加注解

    @Table(name = "tb_user")
    public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Length(min = 4, max = 30, message = "用户名只能在 4~30 位之间")private String username;// 用户名@Length(min = 4, max = 30, message = "密码只能在 4~30 位之间")@JsonIgnoreprivate String password;// 密码@Pattern(regexp = "^1[35678]\\d{9}$", message = "手机号格式不正确")private String phone;// 电话private Date created;// 创建时间@JsonIgnoreprivate String salt;// 密码的盐值public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}public Date getCreated() {return created;}public void setCreated(Date created) {this.created = created;}public String getSalt() {return salt;}public void setSalt(String salt) {this.salt = salt;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", phone='" + phone + '\'' +", created=" + created +", salt='" + salt + '\'' +'}';}
    }
    
  3. 修改 UserController 中的 register 方法,给 User 添加 @Valid 注解

    /*** 用户注册** @param user* @param code* @return*/
    @PostMapping("register")
    public ResponseEntity<Void> register(@Valid User user, @RequestParam("code") String code) {this.userService.register(user, code);return ResponseEntity.status(HttpStatus.CREATED).build();
    }
    

6.7 再次测试

  1. 启动 leyou-gateway、leyou-register、leyou-user、leyou-sms 四个微服务

  2. 我们填写一些不合法的注册数据,发送请求,响应 400 状态码

  3. 并且返回错误信息

7. 在注册页完成注册

  1. 在注册页填写信息,完成注册

  2. 查看 MySQL,注册成功

8. 实现查询用户功能

8.1 接口说明

功能说明

根据用户名和密码查询指定用户。

接口路径

GET /query

参数说明

参数 说明 是否必须 数据类型 默认值
username 用户名,格式为4~30位字母、数字、下划线 String
password 用户密码,格式为4~30位字母、数字、下划线 String

返回结果

  • JSON 格式数据

    {"id": 6572312,"username":"test","phone":"13688886666","created": 1342432424
    }
    
  • 状态码

    • 200:查询成功
    • 400:用户名或密码错误
    • 500:服务器内部异常,查询失败

8.2 Controller

在 UserController 中添加 queryUser 方法

/*** 根据用户名和密码查询用户** @param username* @param password* @return*/
@GetMapping("query")
public ResponseEntity<User> queryUser(@RequestParam("username") String username, @RequestParam("password") String password) {User user = this.userService.queryUser(username, password);if (user == null) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();}return ResponseEntity.ok(user);
}

8.3 Service

在 UserService 中添加 queryUser 方法

/*** 根据用户名和密码查询用户** @param username* @param password* @return*/
public User queryUser(String username, String password) {// 根据用户名查询User record = new User();record.setUsername(username);User user = this.userMapper.selectOne(record);// 校验用户名if (user == null) {return null;}// 校验密码if (!user.getPassword().equals(CodecUtils.md5Hex(password, user.getSalt()))) {return null;}// 用户名密码都正确return user;
}

8.4 测试

我们填写用户名和密码,发送请求,响应 200 状态码,查询成功。

乐优商城(十)用户注册相关推荐

  1. 乐优商城源码/数据库及笔记总结

    文章目录 1 源码 2 笔记 2.1 项目概述 2.2 微服务 3 项目优化 4 项目或学习过程中涉及到的设计模式 5 安全问题 6 高内聚低耦合的体现 7 项目中待优化的地方 1 源码 Github ...

  2. 乐优商城之项目搭建(四)

    文章目录 (一)项目分类 (二)电商行业 (三)专业术语 (四)项目介绍 (五)技术选型 (六)开发环境 (七)搭建后台环境:父工程 (八)搭建后台环境:eureka (九)搭建后台环境:zuul ( ...

  3. 【javaWeb微服务架构项目——乐优商城day15】——会调用订单系统接口,实现订单结算功能,实现微信支付功能

    0.学习目标 会调用订单系统接口 实现订单结算功能 实现微信支付功能 源码笔记及资料: 链接:https://pan.baidu.com/s/1_opfL63P1pzH3rzLnbFiNw 提取码:v ...

  4. 乐优商城(四)商品规格管理

    文章目录 1. 商品规格 1.1 SPU 和 SKU 1.2 分析商品规格的关系 1.3 数据库设计 1.3.1 商品规格组表 1.3.2 商品规格参数表 2. 商品规格组 2.1 商品规格组前端 2 ...

  5. 【javaWeb微服务架构项目——乐优商城day05】——商品规格参数管理(增、删、改,查已完成),SPU和SKU数据结构,商品查询

    乐优商城day05 0.学习目标 1.商品规格数据结构 1.1.SPU和SKU 1.2.数据库设计分析 1.2.1.思考并发现问题 1.2.2.分析规格参数 1.2.3.SKU的特有属性 1.2.4. ...

  6. 乐优商城之分类查询品牌查询(八)

    文章目录 (一)编写分类查询 (二)跨域问题 (三)cors跨域原理 (四)解决跨域问题 (五)品牌查询页面分析 (六)品牌查询后台代码 (七)分页查询排序的原理 (八)axios (一)编写分类查询 ...

  7. 乐优商城(05)--商品管理

    乐优商城(05)–商品管理 一.导入图片资源 现在商品表中虽然有数据,但是所有的图片信息都是无法访问的,因此需要把图片导入到服务器中: 将images.zip文件上传至/leyou/static目录: ...

  8. 乐优商城(填坑)——后台登录

    后台管理模块增加登录验证,与门户网站一样都是采用无状态登录. 一.新增全局函数 在main.js中新增用户验证: 二.修改路由 先显示登录页面 效果: 三.解决cookie写入问题 在http.js中 ...

  9. 乐优商城(10)--数据同步

    乐优商城(10)–数据同步 一.RabbitMQ 1.1.问题分析 目前已经完成了商品详情和搜索系统的开发.思考一下,是否存在问题? 商品的原始数据保存在数据库中,增删改查都在数据库中完成. 搜索服务 ...

最新文章

  1. PC和手机怎么实现绝对居中?
  2. opencv 裁剪 java_如何在opencv java中裁剪检测到的面部图像
  3. Hadoop streaming: Exception in thread main java.io.IOException: No space left on device
  4. 华为诺亚方舟实验室主任李航:神经符号处理开启自然语言处理新篇章
  5. python判断语句_python条件判断语句if elif else使用
  6. CPU指令集是什么东西
  7. 第七篇:Spring Boot整合Thymeleaf_入门试炼03
  8. mysql修改启动command_MySQL Command Line[mysql命令行常用命令]
  9. Ubuntu安装cacti步骤
  10. 我为什么鼓励大家运营个人公众号
  11. emerald sword(打倒大魔王)
  12. 挖金矿问题java课程设计_某15万吨金矿采矿方法课程设计
  13. iOS 9 真机调试
  14. CSS如何设置div半透明效果
  15. img标签,hr标签
  16. Visual Paradigm 如何清除系统代理设置
  17. 游戏开发20课 tilemap 绘制
  18. 云上OneNET智慧大棚
  19. ThinkingInJava_吸血鬼数
  20. 什么叫组网_什么叫MESH组网

热门文章

  1. Http Chunked理解
  2. Linux命令行中使用计算器的5个命令
  3. Unix和UnixNano
  4. 运营商三要素验证原理,这篇文章就够了!
  5. RPC failed; curl 56 OpenSSL SSL_read: No error解决方法
  6. Wish | IT桔子
  7. vue2与vue3的diff算法区别
  8. 米家机器人连接不上了怎么重置_米家扫地机器人怎么重置wifi_米家扫地机器人wifi怎么重置...
  9. .NET、C#与Silverlight开发圣典——分享15位MVP的最佳实践经验
  10. BSN北京市区块链主干网正式发布