项目篇:Echo论坛系统项目

news2025/1/18 11:39:00

一、登录注册模块

1、注册功能

1.1、注册流程图

 

1.2、注册代码

/**
     * 用户注册
     * @param user
     * @return Map<String, Object> 返回错误提示消息,如果返回的 map 为空,则说明注册成功
     */
    public Map<String, Object> register(User user) {
        Map<String, Object> map = new HashMap<>();

        if (user == null) {
            throw new IllegalArgumentException("参数不能为空");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            map.put("usernameMsg", "账号不能为空");
            return map;
        }

        if (StringUtils.isBlank(user.getPassword())) {
            map.put("passwordMsg", "密码不能为空");
            return map;
        }

        if (StringUtils.isBlank(user.getEmail())) {
            map.put("emailMsg", "邮箱不能为空");
            return map;
        }

        // 验证账号是否已存在
        User u = userMapper.selectByName(user.getUsername());
        if (u != null) {
            map.put("usernameMsg", "该账号已存在");
            return map;
        }

        // 验证邮箱是否已存在
        u = userMapper.selectByEmail(user.getEmail());
        if (u != null) {
            map.put("emailMsg", "该邮箱已被注册");
            return map;
        }

        // 注册用户
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5)); // salt
        user.setPassword(CommunityUtil.md5(user.getPassword() + user.getSalt())); // 加盐加密
        user.setType(0); // 默认普通用户
        user.setStatus(0); // 默认未激活
        user.setActivationCode(CommunityUtil.generateUUID()); // 激活码
        // 随机头像(用户登录后可以自行修改)
        user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
        user.setCreateTime(new Date()); // 注册时间
        userMapper.insertUser(user);

        // 给注册用户发送激活邮件
        Context context = new Context();
        context.setVariable("email", user.getEmail());
        // http://localhost:8080/echo/activation/用户id/激活码
        String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
        context.setVariable("url", url);
        String content = templateEngine.process("/mail/activation", context);
        mailClient.sendMail(user.getEmail(),"激活 Echo 账号", content);

        return map;
    }

2、登录模块

2.1、登录页面

2.2、登录验证码问题

        首先,登录的时候会随机生成验证码,如何把这个验证码和当前用户对应起来,实现验证码的校验呢?

        显然,由于这个时候用户还没有登录,我们是没有办法通过用户的 id 来唯一的对应它的验证码的。所以这个时候我们考虑生成一个随机的 id 来暂时的代替这个用户,将其id和对应的验证码暂时存入 Redis 中(60s)。并且在 Cookie中暂时存一份为这个用户生成的随机 id(60s)。

        其中生成验证码和进行校验分别是两个URL请求地址。

        这样,当用户点击登录按钮后,就会去 Redis中获取这个随机 id和验证码,去Cookie查询对应的验证码,判断用户输入的验证码是否一致。

2.3、登录认证并持有用户状态问题

        用户输入用户名和密码并且校验完验证码之后,就登录成功了,那我们如何在一次请求中去保存这个用户的状态?如何回显用户的信息呢?

做法可以设计一个类如下图:

        解释一下,每个用户登录成功后,我们都会为其生成一个随机的唯一的登录凭证实体类对象 LoginTicket(包含用户 id、登录凭证字符串 ticket、是否有效、过期时间),我们把这个登录凭证实体类对象永久的存储在 Redis 中(key 就是登录凭证字符串 ticket)。而所谓登录凭证的无效,就是指用户登出后,这个凭证就会被设置为无效状态;凭证的默认过期时间是 30分。

        存储完 LoginTicket 后,我们就可以根据它来获取用户的状态了。我们定义了一个拦截器 LoginTicketInterceptor每次请求之前都会从 Redis获取到 ticket,然后根据 ticket 去 Redis 中查看这个用户的登录凭证 LoginTicket 是否过期和是否有效,只有登录凭证有效且没有过期才会执行请求,不然就会跳转到登录界面。

        如果该用户的登录凭证有效且没有过期,那我们就可以在本次请求中持有这个用户的信息了。如何持有呢?这里我们考虑使用 ThreadLocal 保存用户信息,ThreadLocal 在每个线程中都创建了一个用户信息副本,也就是说每个线程都可以访问自己内部的用户信息副本变量,从而实现了线程隔离,来看下 HostHolder 类:

        所以将登录成功后要保存的信息为:

        将生成的凭证保存到Redis上,并且设置过期时间,置state为1,其中key为凭证,value为LoginTicket 类。

       然后每次请求首先经过拦截器,通过Cookie获取ticket凭证。凭借ticket凭证从Redis获取LoginTicket 类的信息。如果存在就将通过LoginTicket 类的用户id查询用户信息其保存到ThreadLocal中,否则拦截。

2.4、退出功能

        从Redis删除凭证信息,执行ThreadLocal的remove()方法清空用户信息,删除Cookie保存的凭证信息。

2.5、性能优化

        因为每次请求需要在拦截器中通过通过Cookie获取凭证,然后去Redis获取LoginTicket 类。如果通过验证则会每次去数据库查询用户信息,这样导致每一次请求访问都会去数据库查询造成巨大的访问压力。

        为了避免这种情况,所以拦截器首先去Redis查询用户信息,如果有则直接保存到ThreadLocal,否则再去数据库查询用户信息,再保存到Redis中。

2.5、流程

  • 用户登录 —> 生成登录凭证作为key存入 Redis,value是凭证类信息,Cookie 中存一份 ticket凭证
  • 每次执行请求之前,拦截器都会通过 Cookie 去 Redis 中查询该用户的登陆凭证是否过期和是否有效。点击记住我可以延长登录凭证的过期时间,用户退出则其登录凭证变为无效状态
  • 根据这个登录凭证对应的用户 id,去数据库中查询这个用户信息
  • 使用 ThreadLocal 在本次请求中一直持有这个用户信息
  • 优化点:每次请求前都需要去数据库查询这个用户信息,访问频率比较高,所以我们考虑把登录成功的用户信息在 Redis 中保存一会,拦截器每次查询前先去 Redis 中查询

 流程图如下图:

二、帖子模块

三、评论模块

四、私信模块

五、点赞模块

六、系统通知模块

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/809451.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLOv8实现K折交叉验证教程:解决数据集样本稀少和类别不平衡的难题

本篇博文为大家讲解 YOLOv8 实现 k折交叉验证的流程,包含代码使用和讲解。 K折交叉验证 概念 K折交叉验证 (K-fold cross-validation) 是一种常用于评估机器学习模型性能的技术。 它可以更充分地利用有限的数据集,减少由于数据划分不当而引起的偏差,提高模型的泛化能力。…

计算机里基本硬件的组成以及硬件协同

文章目录 冯诺依曼体系输入设备输出设备存储器运算器控制器协同工作的流程 冯诺依曼体系 世界上第一台通用计算机&#xff0c;ENIAC&#xff0c;于1946年诞生于美国一所大学。 ENIAC研发的前期&#xff0c;需要工作人员根据提前设计好的指令手动接线&#xff0c;以这种方式输入…

卸载大脑,相信DFS

切莫相信动规&#xff0c;吾将为您指明前进之路 印子 比赛时&#xff0c;你是否有这样的经历&#xff1a;不敢用for暴搜&#xff0c;又不会用数学公式推理&#xff1b;焦急地在纸上打草&#xff0c;却没有优化思路&#xff1b;明明比赛前一天晚上背了那么多模板却脑子一片空白…

Modbus tcp转ETHERCAT网关modbus tcp/ip协议

捷米JM-ECT-TCP网关能够连接到Modbus tcp总线和ETHERCAT总线中&#xff0c;实现两种不同协议设备之间的通讯。这个网关能够大大提高工业生产的效率和生产效益&#xff0c;让生产变得更加智能化。捷米JM-ECT-TCP 是自主研发的一款 ETHERCAT 从站功能的通讯网关。该产品主要功能是…

《Deep Leakage from Gradients》

Deep Leakage from Gradients 摘要 现在的分布式机器学习&#xff0c;或联邦学习中最常用的一种方法就是交换梯度&#xff0c;长期以来人们都认为交换梯度的安全的&#xff0c;但是本文提出从公开共享的梯度中获得私有训练数据是可能的。文章将这种泄漏命名为Deep leak from …

PDPS教程:导出带颜色的JT格式2D布局图文件的另一种方法

目录 概述 布局图平面化与边缘化处理 导出为JT格式文件 JT格式文件查看 概述 在PDPS软件中导入机器人生产线或工作站2D布局图之前&#xff0c;要先将布局图文件转换为JT格式。 使用Crossmanager软件转换出来的2D布局图文件会将图形元素原有的颜色自动修改为黑白颜色&…

源码学习初章-基础知识储备

文章目录 学前准备源码地址引言extern "C" 宏定义平台宏跨平台宏vstdio平台禁用警告宏 连接、双层宏定义函数宏系统函数宏自定义函数宏多语句执行宏do while0 普通宏定义 C的一些必备函数知识回调函数和函数指针回调函数wireshark-4.0.7源码例子函数指针wireshark4.0…

Flutter-基础Widget

Flutter页面-基础Widget 文章目录 Flutter页面-基础WidgetWidgetStateless WidgetStateful WidgetState生命周期 基础widget文本显示TextRichTextDefaultTextStyle 图片显示FlutterLogoIconImageIamge.assetImage.fileImage.networkImage.memory CircleAvatarFadeInImage 按钮R…

视频号私信关注引流脚本实操演示教学,分享几个微信视频号引流推广的方法

大家好&#xff0c;我是小刘互联网思维。最近视频号越来越火&#xff0c;所以很多人或者小团队也都盯上了这个新兴市场。有人选择手动引流&#xff0c;就一定有人想软件引流&#xff0c;有些朋友就私信问我这个软件引流要怎么操作&#xff0c;接下来我就为大家简单讲解视频号脚…

【数据结构】【王道408】——PPT截图与思维导图

自用视频PPT截图 视频网址王道B站链接 23考研 408新增考点&#xff1a; 并查集&#xff0c;红黑树 2023年408真题数据结构篇 408考纲解读 考纲变化 目录 第一章 绪论第二章 线性表顺序表单链表双链表循环链表静态链表差别 第三章 栈 队列 数组栈队列栈的应用数组 第四章 串第五…

RustDesk 1.2 现已发布

RustDesk 1.2 现已发布&#xff0c;此版本采用 Flutter 重写桌面版本&#xff0c;支持 Wayland 被控。 一些值得关注的变化有&#xff1a; 用 Flutter 重写支持 ipv6&#xff08;Beta&#xff09;增加一次性密码QuickSupport &#xff08;Beta&#xff09;硬件编解码器 H264 /…

支持向量机(iris)

代码&#xff1a; import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn import svm import numpy as np# 定义每一列的属性 colnames [sepal-length, sepal-width, petal-length, petal-width, class] # 读取数据 iris pd.read_csv(data\\i…

MicroPython动手做(14)——掌控板之OLED屏幕

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

win10安装vs6行号插件

插件包名&#xff1a;win10 VC6LineNumberAddin 下载包&#xff1a; 链接: https://pan.baidu.com/s/13T-NAxQQDcA_K1hHJQ0vWw?pwdbe3r 提取码: be3r 修改reg为以下&#xff1a; Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\DavidHowe Software\Lie…

想要零起步做跨境电商,可以试试这7个步骤!

近几年&#xff0c;跨境电商行业在不断的高速发展。国家政策的支持&#xff0c;行业的迅猛发展&#xff0c;使得跨境电商成为我国经济发展中的一匹黑马&#xff0c;不仅业务呈爆发式增长&#xff0c;我国跨境电商也逐步实现了“买全球卖全球”的愿望。 想要零起步做跨境电商&a…

linux学习(gbd进程)[5]

gdb set var i n #可设置变量值进程 冯诺依曼结构

LabVIEW使用灰度和边缘检测进行视频滤波

LabVIEW使用灰度和边缘检测进行视频滤波 数字图像处理&#xff08;DIP&#xff09;是真实和连续世界的离散表示。除此之外&#xff0c;这种数字图像在通信、医学、遥感、地震学、工业自动化、机器人、航空航天和教育等领域变得非常重要。计算机技术越来越需要视频图像的数字图…

一文讲解开发中的命名规范

命名规范 好的代码本身就是注释, 所以我们需要统一命名风格。 ​ 在本文中&#xff0c;将从大到小&#xff0c;从外到内&#xff0c;总结Java编程中的命名规范。文中将会涉及到日常工作中常见的命名示例&#xff0c;如包命名&#xff0c;类命名&#xff0c;接口命名&#xff0c…

java+springboot+mysql智慧办公OA管理系统

项目介绍&#xff1a; 使用javaspringbootmysql开发的智慧办公OA管理系统&#xff0c;系统包含超级管理员&#xff0c;系统管理员、员工角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理&#xff1b;部门管理&#xff1b;职位管理&#xff1b;员工管理…

传输层中一些零碎且易忘的知识点

端口号&#xff1a;共两个字节 不同类型的端口号&#xff1a; 服务端端口号 熟知端口号&#xff1a;0&#xff5e;1023登记端口号&#xff1a;1024&#xff5e;49151 客户端使用端口号&#xff08;短暂/临时端口号&#xff09;&#xff1a;49152&#xff5e;65535 要记得常见应…