Redis实战案例 使用Redis+Mybatis-Plus实现前台登录注册功能

什么是Redis

Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库。

特性

Redis基于内存运行,性能高效
支持分布式理论上可以无限拓展
key与value的存储形式

Mysql与Redis的区别

Mysql

属于关系型数据库
优点:易于维护,使用方便,支持sql。
缺点:读写性能比较差,固定的表结构

Redis

属于Nosql(非关系型数据库)
优点:格式灵活,速度快,高可拓展性,成本低
缺点:不提供sql支持,学习和使用成本高,无事务支持,数据结构相对复杂

接下来我们各取所优来完成一个实战案例吧

实战案例:

我们使用Redis+Mybatis-Plus实现前台登录注册的功能

步骤如下:
  1. 将手机号和验证码保存到Redis中
  2. 注册时验证注册验证码
  3. 用户注册
  4. 用户名和密码登录
  5. 登录时将手机号+验证码保存到Redis中
  6. 手机号+验证码登录
  7. 获取登录用户名
  8. 获取登录用户信息
技术栈:
  1. zookeeper注册中心
  2. dubbo分布式调用
  3. Mybatis-Plus操作数据库
  4. Redis非关系型数据库
  5. SpringBoot
  6. JWT分布式鉴权
  7. 阿里云短信测试
案例说明:

redis可以保存时效性数据,即可以给数据设置过期时间,所以我们会将验证码保存到redis中而不是mysql中,当用户发送验证码后,在redis中查找该验证码,如果用户输入的验证码和redis中保存的验证码一致则进行下一步,输入用户名和密码注意:手机号和用户名都不能重复,客户端传来的明文密码我们通过MD5的方式对它进行加密,注册成功后将用户信息保存到数据库中。

代码参考:

在Linux中安装zookeeper、dubbo、redis服务

生成验证码工具类:


import java.util.Random;

public class RandomUtil {

    public static String buildCheckCode(int digit){
        String str = "0123456789";
        Random random = new Random();
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0;i < digit;i++){
            char ch = str.charAt(random.nextInt(str.length())); // 返回一个[0,str.length)之间的值包括0但不包括str.length
            stringBuilder.append(ch);
        }
        return stringBuilder.toString();

    }
}


接口:

// 前台用户服务
public interface ShoppingUserService {

    // 用户注册时,将手机号和验证码保存到redis中
    void saveRegisterCheckCode(String phone,String checkCode);
    // 注册时验证注册验证码
    void registerCheckCode(String phone,String checkCode);
    // 注册
    void register(ShoppingUser shoppingUser);
    // 用户名密码登录
    String loginPassword(String username,String password);

    // 登录时将手机号和验证码保存到redis中
    void saveLoginCheckCode(String phone,String checkCode);
    // 手机号验证码登录
    String loginCheckCode(String phone,String checkCode);

    // 获取登录用户名
    String getName(String token);
    // 获取登录用户
    ShoppingUser getLoginUser(String token);
}

接口实现类:

获取登录用户信息这里使用的时JWT来完成的,通常在用户登录成功后网站上方会显示欢迎回来:XXX
这时候大家可能会有些疑问,为什么不用session来保存登录用户信息呢?

注意:
session保存在服务器中,我们这个项目采用的时dubbo+zookeeper这样的一个分布式调用,该项目具有很多子系统,session在多台服务器间是无法访问的。
JWT这个技术,是一种令牌生成算法,我们将生成的令牌返回给前端,前端配置在访问其他模块时请求头带上令牌,我们可以解析令牌拿到用户名以及用户信息,JWT中的token还具备时效性,可以设置保存的时间


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itbaizhan.shopping_common.exception.BusException;
import com.itbaizhan.shopping_common.pojo.ShoppingUser;
import com.itbaizhan.shopping_common.result.CodeEnum;
import com.itbaizhan.shopping_common.service.ShoppingUserService;
import com.itbaizhan.shopping_common.util.JWTUtil;
import com.itbaizhan.shopping_common.util.Md5Util;
import com.itbaizhan.shopping_user_service.mapper.ShoppingUserMapper;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import java.util.List;
import java.util.concurrent.TimeUnit;

@DubboService
public class ShoppingUserServiceImpl implements ShoppingUserService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ShoppingUserMapper shoppingUserMapper;

    /**
     * 注册时将手机号和验证码保存到redis中
     * @param phone 手机号
     * @param checkCode 验证码
     */
    @Override
    public void saveRegisterCheckCode(String phone, String checkCode) {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("registerCode:"+phone,checkCode,300, TimeUnit.SECONDS);
    }

    @Override
    public void registerCheckCode(String phone, String checkCode) {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        String code = (String) valueOperations.get("registerCode:"+phone);
        if(!checkCode.equals(code)){
            throw new BusException(CodeEnum.REGISTER_CODE_ERROR);
        }
    }

    @Override
    public void register(ShoppingUser shoppingUser) {
        // 验证手机号是否重复
        String phone = shoppingUser.getPhone();
        QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();
        queryWrapper.eq("phone",phone);
        List<ShoppingUser> shoppingUsers = shoppingUserMapper.selectList(queryWrapper);
        if(shoppingUsers!=null && shoppingUsers.size() > 0){
            throw new BusException(CodeEnum.REGISTER_REPEAT_PHONE_ERORR);
        }
        // 验证用户名是否重复
        String name = shoppingUser.getName();
        QueryWrapper<ShoppingUser> queryWrapper1 = new QueryWrapper();
        queryWrapper1.eq("username",name);
        List<ShoppingUser> users = shoppingUserMapper.selectList(queryWrapper1);
        if(users != null && users.size() > 0){
            throw new BusException(CodeEnum.REGISTER_REPEAT_USERNAME_ERORR);
        }
        // 设置用户状态为Y
        shoppingUser.setStatus("Y");
        // 对密码加密
        shoppingUser.setPassword(Md5Util.encode(shoppingUser.getPassword()));
        // 新增用户
        shoppingUserMapper.insert(shoppingUser);
    }

    @Override
    public String loginPassword(String username, String password) {
        QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();
        queryWrapper.eq("username",username);
        ShoppingUser shoppingUser = shoppingUserMapper.selectOne(queryWrapper);
        if(shoppingUser == null){
            throw new BusException(CodeEnum.LOGIN_NAME_PASSWORD_ERROR);
        }
        boolean verify = Md5Util.verify(password, shoppingUser.getPassword());
        if(!verify){
            throw new BusException(CodeEnum.LOGIN_NAME_PASSWORD_ERROR);
        }
        return JWTUtil.sign(shoppingUser);
    }

    // 登录时将手机号和验证码保存到redis中
    @Override
    public void saveLoginCheckCode(String phone, String checkCode) {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("loginCode:"+phone,checkCode,300, TimeUnit.SECONDS);
    }
    // 验证手机号和验证码
    @Override
    public String loginCheckCode(String phone, String checkCode) {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        String code = (String) valueOperations.get("loginCode:" + phone);
        if(!checkCode.equals(code)){
            throw new BusException(CodeEnum.LOGIN_CHECK_CODE_ERROR);
        }
        QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();
        queryWrapper.eq("phone",phone);
        ShoppingUser shoppingUser = shoppingUserMapper.selectOne(queryWrapper);
        return JWTUtil.sign(shoppingUser);
    }

    @Override
    public String getName(String token) {
        // 解析令牌获取用户名
        String username = JWTUtil.verify(token);
        return username;
    }

    @Override
    public ShoppingUser getLoginUser(String token) {
        String username = JWTUtil.verify(token);
        QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();
        queryWrapper.eq("username",username);
        ShoppingUser shoppingUser = shoppingUserMapper.selectOne(queryWrapper);
        return shoppingUser;
    }
}

发送短信服务:

发送短信服务需要自己去申请了,获取AccessKeyIDAccessKeySecret即可。详细请参照:阿里云官网

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿晓晓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值