第二十五章:实现短信功能和注册功能

此博客用于个人学习,来源于网上,对知识点进行一个整理。

1. 发送短信服务:

短信微服务已经准备好,我们就可以继续编写用户中心接口了。

1.1 接口说明:

业务逻辑是这样的:

  • 我们接收页面发送来的手机号码
  • 生成一个随机验证码
  • 将验证码保存在服务端
  • 发送短信,将验证码发送到用户手机

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

1.2 Redis:

1)Spring Data Redis:是Spring Data 家族的一部分。 对 Jedis 客户端进行了封装,与 spring 进行了整合。可以非常方便的来实现 redis 的配置和操作。

2)RedisTemplate 基本操作:

Spring Data Redis 提供了一个工具类:RedisTemplate。里面封装了对于 Redis 的五种数据结构的各种操作,包括:

  • redisTemplate.opsForValue() :操作字符串
  • redisTemplate.opsForHash() :操作 hash
  • redisTemplate.opsForList():操作 list
  • redisTemplate.opsForSet():操作 set
  • redisTemplate.opsForZSet():操作 zset

其它一些通用命令,如 expire,可以通过 redisTemplate.xx() 来直接调用

5种结构:

  • String:等同于 java 中的,Map<String,String>
  • list:等同于 java 中的 Map<String,List<String>>
  • set:等同于 java 中的 Map<String,Set<String>>
  • sort_set:可排序的 set
  • hash:等同于java中的:Map<String,Map<String,String>>

3) StringRedisTemplate:
RedisTemplate 在创建时,可以指定其泛型类型:

  • K:代表 key 的数据类型
  • V: 代表 value 的数据类型

这里的类型不是 Redis 中存储的数据类型,而是 Java 中的数据类型,RedisTemplate 会自动将 Java 类型转为 Redis 支持的数据类型:字符串、字节、二进制等等。不过 RedisTemplate 默认会采用 JDK 自带的序列化(Serialize)来对对象进行转换。生成的数据十分庞大,因此一般我们都会指定 key 和 value 为 String 类型,这样就由我们自己把对象序列化为 json 字符串来存储即可。

因为大部分情况下,我们都会使用 key 和 value 都为 String 的 RedisTemplate,因此 Spring 就默认提供了这样一个实现:

在这里插入图片描述
需要在项目中引入 Redis 启动器:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

然后在配置文件中指定 Redis 地址:

spring:
  redis:
    host: 192.168.56.101

1.3 在项目中实现:

要三个步骤:

  • 生成随机验证码
  • 将验证码保存到 Redis 中,用来在注册的时候验证
  • 发送验证码到 leyou-sms-service 服务,发送短信

需要引入 Redis 和 AMQP:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

添加 RabbitMQ 和 Redis 配置:

spring:
  redis:
    host: 192.168.56.101
  rabbitmq:
    host: 192.168.56.101
    username: leyou
    password: leyou
    virtual-host: /leyou

另外还要用到工具类,生成6位随机码,这个我们封装到了 leyou-common 中,因此需要引入依赖:

<dependency>
    <groupId>com.leyou.common</groupId>
    <artifactId>leyou-common</artifactId>
    <version>${leyou.latest.version}</version>
</dependency>

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);
}

1)UserController:

在 leyou-user-service 工程中的 UserController 添加方法:

/**
 * 发送手机验证码
 * @param phone
 * @return
 */
@PostMapping("code")
public ResponseEntity<Void> sendVerifyCode(@RequestParam("phone")String phone){
    this.userService.sendVerifyCode(phone);
    return ResponseEntity.status(HttpStatus.CREATED).build();
}

2)UserService:

在 Service 中添加代码:

@Autowired
private UserMapper userMapper;

@Autowired
private AmqpTemplate amqpTemplate;

@Autowired
private StringRedisTemplate redisTemplate;

private static final String KEY_PREFIX = "user:verify:";

public void sendVerifyCode(String phone) {
    if (StringUtils.isBlank(phone)){
        return;
    }
    //生成验证码
    String code = NumberUtils.generateCode(6);

    //发送消息到rabbitMQ
    Map<String,String> msg = new HashMap<>();
    msg.put("phone",phone);
    msg.put("code",code);
    this.amqpTemplate.convertAndSend("leyou.sms.exchange","verifycode.sms");

    //把验证码保存到redis中
    this.redisTemplate.opsForValue().set(KEY_PREFIX + phone,code,5, TimeUnit.MINUTES);
}

要设置短信验证码在 Redis 的缓存时间为5分钟

2. 注册功能:

2.1 接口说明:

基本逻辑:

  • 校验短信验证码
  • 生成盐
  • 对密码加密
  • 写入数据库
  • 删除 Redis 中的验证码

2.2 UserController:

/**
 * 注册
 * @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();
}

2.3 UserService:

public void register(User user,String code) {

    //从redis中获取缓存验证码
    String redisCode = this.redisTemplate.opsForValue().get(KEY_PREFIX + user.getPhone());

    //1.校验验证码
    if (!StringUtils.equals(code,redisCode)){
        return;
    }

    //2.生成盐巴
    String salt = CodecUtils.generateSalt();
    user.setSalt(salt);

    //3.加盐加密
    user.setPassword(CodecUtils.md5Hex(user.getPassword(),salt));

    //4.新增用户
    user.setId(null);
    user.setCreated(new Date());
    this.userMapper.insertSelective(user);
}

此处用到了生成密码的工具类——CodeUtils:

public class CodecUtils {

    public static String md5Hex(String data,String salt) {
        if (StringUtils.isBlank(salt)) {
            salt = data.hashCode() + "";
        }
        return DigestUtils.md5Hex(salt + DigestUtils.md5Hex(data));
    }

    public static String shaHex(String data, String salt) {
        if (StringUtils.isBlank(salt)) {
            salt = data.hashCode() + "";
        }
        return DigestUtils.sha512Hex(salt + DigestUtils.sha512Hex(data));
    }

    public static String generateSalt(){
        return StringUtils.replace(UUID.randomUUID().toString(), "-", "");
    }
}

该工具类需要 apache 加密工具包:

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

2.4 hibernate-validate:

刚才虽然实现了注册,但是服务端并没有进行数据校验,而前端的校验是很容易被有心人绕过的。所以我们必须在后台添加数据校验功能。我们这里会使用 Hibernate-Validator 框架完成数据校验,而 SpringBoot 的 web 启动器中已经集成了相关依赖。

1)Hibernate-Validator 概述:

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

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计算合法性

3)给 User 添加校验:

在 leyou-user-interface 中添加 Hibernate-Validator 依赖:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

在 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位之间")
    @JsonIgnore //对象序列化为json字符串时,忽略该属性
    private String password;// 密码

    @Pattern(regexp = "^1[356789]\\d{9}$",message = "手机号不合法")
    private String phone;// 电话

    private Date created;// 创建时间

    @JsonIgnore
    private 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;
    }
}

4)在 controller 上进行控制:

在 controller 中改造 register 方法,只需要给 User 添加 @Valid 注解即可。

3. 根据用户名和密码查询用户:

3.1 接口说明:

功能:查询功能,根据参数中的用户名和密码查询指定用户。

参数说明

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

返回结果

用户的json格式数据

{
    "id": 6572312,
    "username":"test",
    "phone":"13688886666",
    "created": 1342432424
}

状态码

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

3.2 controller:

@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.badRequest().build();
    }
    return ResponseEntity.ok(user);
}

3.3 service:

/**
 * 查询用户
 * @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);

    //判断user是否为空
    if (user == null){
        return null;
    }

    //获取盐,对用户输入的密码加密
    password = CodecUtils.md5Hex(password,user.getSalt());

    //和数据库中的密码进行比较
    if (StringUtils.equals(password,user.getPassword())){
        return user;
    }
    return null;
}

查询时也要对密码进行加密后判断是否一致。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值