【短链接项目笔记】Day3 用户模块剩余部分

0.前言

今天主要完成用户管理部分的剩余内容,包括数据库的分库分表,个人信息修改、登录、退出等。

1.用户分库分表

1.1 什么是分库分表

分库分表是数据库架构演进中的一个重要里程碑,主要是为了解决单机数据库在数据量并发量达到瓶颈时产生的性能问题。

分库和分表一共有两种方式:垂直和水平

分库的两种模式:

  • 垂直分库:将数据库进行拆分,例如一个电商数据库,拆分为订单数据库、购物车数据库和用户数据库;
  • 水平分库:将数据库水平拆分为多个数据库;

分表的两种模式

  • 垂直分表:将表按照业务维度进行拆分,常用的数据为主表、不常用的数据在扩展表中;
  • 水平分表:将用户表水平拆分为多个用户表。

1.2 什么场景下分库分表

  • 什么场景下分表
    数据量过大或者数据库对应的磁盘文件过大
  • 什么场景下分库
    连接不够用,假设MySQL Server 支持 4000 个数据库连接。一个服务连接池最大 10 个,假设有 40 个节点。已经占用了 400 个数据库连接。类似于这种服务,有10个,那这个MySQL Server连接就不够了。
  • 什么场景下分库分表
    高并发写入或查询场景、数据量巨大场景。

2.数据库分库分表插件ShardingSphere

2.1 引入依赖

xml 复制代码
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core</artifactId>
    <version>5.3.2</version>
</dependency>

2.2 定义分片规则

yaml 复制代码
spring:
  datasource:
  	# ShardingSphere 对 Driver 自定义,实现分库分表等隐藏逻辑
    driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
    # ShardingSphere 配置文件路径
    url: jdbc:shardingsphere:classpath:shardingsphere-config.yaml

shardingsphere-config.yaml

yaml 复制代码
# 数据源集合
dataSources:
  ds_0:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://127.0.0.1:3306/link?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: root

rules:
  - !SHARDING
    tables:
      t_user:
        # 真实数据节点,比如数据库源以及数据库在数据库中真实存在的
        actualDataNodes: ds_0.t_user_${0..15}
        # 分表策略
        tableStrategy:
          # 用于单分片键的标准分片场景
          standard:
            # 分片键
            shardingColumn: username
            # 分片算法,对应 rules[0].shardingAlgorithms
            shardingAlgorithmName: user_table_hash_mod
    # 分片算法
    shardingAlgorithms:
      # 数据表分片算法
      user_table_hash_mod:
        # 根据分片键 Hash 分片
        type: HASH_MOD
        # 分片数量
        props:
          sharding-count: 16
# 展现逻辑 SQL & 真实 SQL
props:
  sql-show: true

3.敏感数据加密存储

在实际开发中,像电话、身份证以及邮箱这些私密信息,在数据库中是需要加密存储的。

yaml 复制代码
# 配置数据源,底层被 ShardingSphere 进行了代理
dataSources:
  ds_0:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://127.0.0.1:3306/link?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: root

rules:
# 数据加密存储规则
  - !ENCRYPT
    # 需要加密的表集合
    tables:
      # 用户表
      t_user:
        # 用户表中哪些字段需要进行加密
        columns:
          # 手机号字段,逻辑字段,不一定是在数据库中真实存在
          phone:
            # 手机号字段存储的密文字段,这个是数据库中真实存在的字段
            cipherColumn: phone
            # 身份证字段加密算法
            encryptorName: common_encryptor
          mail:
            cipherColumn: mail
            encryptorName: common_encryptor
        # 是否按照密文字段查询
        queryWithCipherColumn: true
    # 加密算法
    encryptors:
      # 自定义加密算法名称
      common_encryptor:
        # 加密算法类型
        type: AES
        props:
          # AES 加密密钥
          aes-key-value: d6oadClrrb9A3GWo
props:
  sql-show: true

可以看到phone和mail都实现了加密存储

4.用户登录和退出

  • UserController.java
java 复制代码
    /**
     * 用户登录
     */
    @PostMapping("/user/login")
    Result<UserLoginRespDTO> login(@RequestBody UserRegisterReqDTO requestParam) {
        UserLoginRespDTO result =userService.login(requestParam);
        return Results.success(result);
    }

    /**
     * 检查用户是否登录
     */
    @GetMapping("/user/check-login")
    Result<Boolean> checkLogin(@RequestParam("username") String username,@RequestParam("token") String token) {
        return Results.success(userService.checkLogin(username,token));
    }

    /**
     * 用户退出
     */
    @DeleteMapping("/user/logout")
    Result<Void> logout(@RequestParam("username") String username,@RequestParam("token") String token) {
        userService.logout(username,token);
        return Results.success();
    }
  • UserServiceImple.java
java 复制代码
	/**
     * 用户登录
     * @param requestParam
     */
    @Override
    public UserLoginRespDTO login(UserRegisterReqDTO requestParam) {
        LambdaQueryWrapper<UserDO> wrapper = Wrappers.lambdaQuery(UserDO.class)
                .eq(UserDO::getUsername, requestParam.getUsername())
                .eq(UserDO::getPassword, requestParam.getPassword())
                .eq(UserDO::getDelFlag, 0);
        UserDO userDO = baseMapper.selectOne(wrapper);
        if(userDO==null){
            throw new ClientException("用户不存在");
        }
        Boolean hasLogin=stringRedisTemplate.hasKey("login:"+requestParam.getUsername());
        if(hasLogin!=null && hasLogin){
            throw new ClientException("用户已登录");
        }
        String redisKey = "login:"+requestParam.getUsername();
        String uuidToken = UUID.randomUUID().toString();


        stringRedisTemplate.opsForHash().put(redisKey,uuidToken,JSON.toJSONString(userDO));
        stringRedisTemplate.expire("login:"+requestParam.getUsername(),30L, TimeUnit.MINUTES);
        return new UserLoginRespDTO(uuidToken);
    }

    /**
     * 检查用户登录状态
     * @param token
     * @return
     */
    @Override
    public Boolean checkLogin(String username,String token) {
        return stringRedisTemplate.opsForHash().get("login:"+username,token)!=null;
    }

    /**
     * 用户登出
     * @param username
     * @param token
     */
    @Override
    public void logout(String username, String token) {
        if(checkLogin(username,token)){
            stringRedisTemplate.delete("login:"+username);
            return;
        }
        throw new ClientException("用户Token不存在或用户未登录");

    }

用户登录流程是根据前端传来的用户名和密码,去数据库中查找,如果查不到,说明用户不存在,抛异常;然后再去redis中查key,如果存在证明已登录,抛异常,如果不存在就创建一个rediskey。

相关推荐
Chengbei112 小时前
fastjson 原生反序列化配合动态代理绕过限制
java·安全·网络安全·系统安全·安全架构
lhrimperial2 小时前
MySQL底层原理
java·后端·mysql
qq_377112372 小时前
JAVA的平凡之路——此峰乃是最高峰JVM-GC垃圾回收器(1)-06
java·开发语言·jvm
直有两条腿2 小时前
【Redis】原理-数据结构
数据结构·数据库·redis
陌路202 小时前
redis缓存雪崩,击穿,穿透
redis·缓存·mybatis
学编程就要猛2 小时前
算法:2.复写零
java·数据结构·算法
熊猫吃竹子2 小时前
JVM G1GC参数调优实战
jvm·后端
我认不到你2 小时前
paxos一致性算法(大白话+图解)
分布式·后端
文心快码BaiduComate2 小时前
插件开发实录:我用Comate在VS Code里造了一场“能被代码融化”的初雪
前端·后端·前端框架