个人博客系统项目总结

项目的简单介绍

首先,因为我个人有写博客的习惯,所以我就仿造 CSDN 简单实现了一个自己的个人博客系统。该项目由用户模块,博客模块,评论模块组成。用户模块主要负责的是用户的注册和登录,用户头像的修改等。博客模块主要是博客的发布,删除,修改,点赞,浏览等功能。评论模块包含博客的评论,回复等。

项目具有下面几个亮点:

首先是将 Session 存储到了 Redis 中,实现了 Session 的共享,使得其在分布式环境下可以使用。

然后我对密码进行了一个加盐加密操作,保证了密码的安全性。

我使用 Redis 实现了点赞功能,因为 Redis 的写入和读取速度非常快,很适合点赞这种高频率操作。

然后就是我对博客展示列表页面增加了一个分页功能。

目前就是我项目的介绍,老师您如果您有哪些想详细了解的可以指出来。


1.密码加盐加密

MD5密码是一种数字摘要算法,它可以将任意长度的字符串转换成一个128位的散列值,也就是一个16字节的数字。

为什么要使用加盐加密呢? 首先如果只是单纯使用 MD5 加密,由于 MD5 加密每次生成的值都是相同的,很容易通过暴力枚举的方式进行破解。而加盐加密通过生成随机 32 位盐值,每次生成的密码都是不同的,增加密码安全性。

加密功能
  • 先使用 UUID 生成一个随机的 32 位的盐值,并用 String 的 replace 方法替换掉其中的横杠。
  • 将盐值和输入的密码相加,并使用 DigestUtils 对其进行 MD5 加密。生成一个最终密码。
  • 最后将盐值加上最终密码返回。
解密功能
  • 首先,从数据库中获取存取的加密过的密码,获取密码的前 32 位盐值。
  • 使用盐值加上输入的密码使用 DigestUtils 对其进行 MD5 加密。
  • 判断加密后的密码是否与数据库中的密码相等。
/**
     * 对密码进行加密
     * @param password
     * @return
     */
    public static String encrypt(String password){
        // 随机生成不同的 32 位的盐值
        String salt = UUID.randomUUID().toString().replace("-","");//替换“-”为空
        // 生成最终密码 (盐值 + 密码) 进行加密
        String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
        return salt + finalPassword;
    }

    /**
     * 解密验证密码正确性
     * @param password 待验证的密码
     * @param finalPassword 数据库中饭加密了的密码
     * @return
     */
    public static boolean decrypt(String password,String finalPassword){
        // 获得前面 32 位的盐值
        String salt = finalPassword.substring(0,32);
        // 通过待验证密码 + 盐值生成最终待确认密码
        String securityPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
        return finalPassword.equals(salt + securityPassword);
    }

2.分页功能

初始化当前总页面数量函数实现。

  • 首先从前端获取到每页的大小。
  • 然后通过 MyBatisPlus 的 list() 获取到总博客数量。
  • 用博客数量 / 每页大小就得到总页数。如果结果是小数,如 3.5,则页数为 4,其它同理。
 /* 初始化总页数 */
    @Override
    public Object initTotalPage(Integer pageSize) {
        List<Blog> blogs = list();
        if(blogs != null) {
            // Math.ceil: 如果是  2.x 结果为 3
            return (int) Math.ceil(blogs.size() * 1.0 / pageSize);
        }
        return 0;
    }

初始化博客列表函数实现。

  • 首先先判断传入的 pageIndex 和 pageSize 是否为空。
  • 通过页面大小和当前页面索引计算偏移量。
  • 通过 MyBatis 进行数据库查询到该页面的博客。
 public List<Blog> initBlogs(Integer pageIndex, Integer pageSize){
        if(pageIndex != null && pageSize != null && pageIndex > 0 && pageSize > 0) {
            Integer offset = pageSize * (pageIndex - 1);
            return mapper.initBlogs(pageSize,offset);
        }
        return null;
    }
    <select id="initBlogs" resultType="com.example.demo.entity.Blog">
        select * from blog_info limit #{pageSize} offset #{offset}
    </select>

3.点赞功能

  • 首先获取到当前的用户对象。
  • 使用字符串结合被点赞的的博客 id 作为 Redis 的 key。
  • 通过当 key 和前用户 id 查询 Redis 中的数据,判断是否已经点赞过。
  • 如果没有点赞过,则将点赞记录加入到 Redis 和数据库中,数据库中的点赞数 -1。
  • 如果点赞过,则将 Redis 和数据库中的点赞记录清除,数据库中的点赞数 + 1。

Redis 中的点赞记录是存储在 Set 中的,每一个 Set 代表一个博客,存储的是给博客点赞过的用户 id。

而数据库中的点赞记录表如下图所示:使用了 blog_id 和 user_id 保证每条记录的唯一性。

    @Transactional
    public Object likeBlog(HttpServletRequest request,String likedBlogId) {
        // 得到当前用户对象
        User curUser = SessionUnit.getLoginUser(request);
        if(curUser == null) {
            return AjaxResult.fail(-1,"当前用户对象为空");
        }
        // 创建 Redis key: RedisKey + 博客ID
        String key = RedisKeyUtils.BLOG_LIKED_KEY + likedBlogId;
        // 判断当前用户是否点赞过
        /* 这里返回的是一个 Boolean, 可能为空, 这样可以防止空指针异常 */
        /**/

        boolean liked = Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(key,curUser.getId().toString()));
        // 如果该用户已经点赞过
        if(liked) {
            /* 数据库操作 */
            // 点赞数量 - 1
            boolean isSuccess = blogService.update().setSql("like_count = like_count - 1").eq("id",likedBlogId).update();
            if(!isSuccess) {
                return AjaxResult.fail(-1,"数据库更新失败");
            }
            // 移除数据库中该条记录
            QueryWrapper<BlogLike> wrapper = new QueryWrapper<>();
            wrapper.eq("blog_id",likedBlogId);
            wrapper.eq("user_id",curUser.getId());
            remove(wrapper);

            /* Redis 操作 */
            // 移除 set 中的该条记录
            stringRedisTemplate.opsForSet().remove(key,curUser.getId().toString());
        } else {
            // 用户没有点赞过
            // 点赞数量 + 1
            boolean isSuccess = blogService.update().setSql("like_count = like_count + 1").eq("id",likedBlogId).update();
            if(!isSuccess) {
                return AjaxResult.fail(-1,"数据库更新失败");
            }
            // 新增一条点赞记录
            /* 这里一直报类型错误,暂时不知道什么原因,用传统的  MyBatis 好了 */
            // BlogLike blogLike = new BlogLike(Integer.valueOf(likedBlogId),curUser.getId());
            // save(blogLike);
            mapper.saveLike(likedBlogId,curUser.getId().toString());


            /* Redis 操作 */
            // 新增 set 一条记录
            stringRedisTemplate.opsForSet().add(key,curUser.getId().toString());
        }
        return AjaxResult.success(1,"用户点赞成功");
    }
测试用例设计

功能测试:

  • 能否正常点赞和取消点赞。(这里没有实现,是商铺点评系统项目的:点赞后的用户是否按照时间顺序排序)。
  • 多个用户点赞同一篇博客点,是否记录了每个用户的点赞。

性能测试:

  • 点赞的响应是否够迅速。
  • 多用户某一时间段同时点赞同一篇博客的响应速度。
  • 在点赞数据量非常大的时候的响应速度。

界面测试:

  • 点赞的按钮是否美观。
  • 按钮的布局是否合理。

安全性测试:

  • 用户在未登录的情况下是否可以点赞。

易用性测试:

  • 点赞按钮是否容易被用户找到。
  • 点赞后成功的反馈是否直观。

兼容性测试:

  • 在不同浏览器下能否兼容。
  • 在不同的设备能否兼容。如手机,电脑,平板。

如果是按照接口测试来设计的话:

  • 正常和异常情况的测试
  • 接口方法:更换不同的方法进行测试,如 Post,Delete 等。
  • 参数:更改参数的类型,顺序等进行测试。
  • 业务逻辑:对该方法的业务逻辑进行测试,检查 Redis,MySQL 中是否有数据。

项目的部署

修改前后端内容并打包

前端:

修改 Axios 的路径 Url,路径记得加 http://,不然拦截器配置即使排除还是会拦截。

// axios.defaults.baseURL = "http://localhost:9090"
axios.defaults.baseURL = "Your own host + Port"

修改完前端配置后,将其打包生成 dist 文件夹:

npm run build

后端:

修改上传图片的文件路径:

    // 保存头像的文件路径
    // public static final String AVATAR_FILE_PATH = "D:/IDEA Project/MyProject(Github)/MyBlogSystem-III/vue/public/img/avatar/";

    // Linux 中存放图片的文件路径
    public static final String AVATAR_FILE_PATH = "/home/mycnblog-pplus/vue/dist/img/avatar/";

修改配置文件 application.yml 和 application.properties

修改 Redis 的库序号( database )

# Redis Configuration
spring.redis.host="Your own host"
spring.redis.password="Your own password"
spring.redis.port=6379
spring.redis.database=4
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0

修改 MySQL 的密码,如果没有密码则设置为空:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mycnblog_pplus?characterEncoding=utf8&useSSL=false
    username: root
    #如果要部署到云服务器这里密码要更改(没有密码则为空)
    password: "Your own password"
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 配置数据源类型
    type: com.zaxxer.hikari.HikariDataSource

修改完成后,双击 package 即可将项目打成 Jar 包

在 target 文件夹中找到已经打包好的 Jar 包

部署项目

Vue 项目部署:可以看这篇博客,讲的很详细 VUE项目部署-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值