个人博客系统(SSM版 前端+后端)

这篇具有很好参考价值的文章主要介绍了个人博客系统(SSM版 前端+后端)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

        在学习Servlet的时候,也写了一个博客系统,主要的就是使用servelet加Tomcat进行实现的,而这个项目 仅仅适合去学习Web项目开发的思想,并不满足当下企业使用框架的思想,进行学习过Spring,Spring Boot,Spring MVC以及MyBatis之后,我们就可以对之前的项目使用SSM框架的形式进行升级,然后对相应的功能进行升级,帮助我们对企业的开发流程进行了解,文章最后会有本项目的Gitee地址,欢迎访问!!💕💕💕


下图对此项目对照之前的版本进行了项目升级的总结.

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

 那话不多说啦,开始我们的新项目.

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring


目录

前言

​编辑

1. 前期项目准备

1.1 创建项目

1.2 准备项目

a. 删除项目中无用的文件和目录

b. 引入前端页面     

c. 初始化数据库

d. 项目分层

2. 添加统一的返回格式

3. 配置登录拦截器

4. 实现用户的注册功能

4.1 编写前端Ajax请求代码 

4.2 编写后端代码

a. 用户信息类UserInfo.class

b. Mapper层(UserMapper)

c. MyBatis的xml文件的构造

d. 服务层Service

e. Controller层

4.3 测试功能

5. 实现用户的登录功能

5.1 编写前端Ajax请求代码 

5.2 编写后端代码

a. Mapper 层(UserMapper)

b. MyBatis的xml文件的构造

c. service层

d. Controller层

5.3 测试功能 

6. 个人主页展示功能

6.1 左侧个人信息

6.1.1 编写前端Ajax请求代码 

6.1.2 编写后端代码

a. Mapper 层(UserMapper)

b. MyBatis的xml文件的构造

c. service层

d. Controller层

6.1.3 测试功能 

6.2 右侧个人博客列表信息 

6.2.1 编写前端Ajax请求代码 

6.2.2 编写后端代码

a. Mapper 层(ArticleMapper)

b. MyBatis的xml文件的构造

c. service层

d. Controller层

6.3 删除博客功能 

6.3.1 编写前端Ajax请求代码 

6.3.2 编写后端代码

a. Mapper 层(ArticleMapper)

b. MyBatis的xml文件的构造

c. service层

d. Controller层

6.3.3 测试功能

 6.4 查看全文功能 

6.4.1 编写前端Ajax请求代码 

6.4.2 编写后端代码

a. Mapper 层(ArticleMapper)

b. MyBatis的xml文件的构造

c. service层

d. Controller层

6.4.3 测试功能

6.5 修改博客功能 

6.5.1 编写前端Ajax请求代码 

6.5.2 编写后端代码

a. Mapper 层(ArticleMapper)

b. MyBatis的xml文件的构造

c. service层

d. Controller层

6.5.3 测试功能

6.6 发布博客功能

6.6.1 编写前端Ajax请求代码 

6.6.2 编写后端代码

a. Mapper 层(ArticleMapper)

b. MyBatis的xml文件的构造

c. service层

d. Controller层

6.6.3 功能测试

6.7 注销用户功能

6.7.1 编写前端Ajax请求代码 

6.7.2 编写后端代码

a. Mapper 层(ArticleMapper)

b. MyBatis的xml文件的构造

c. service层

d. Controller层

6.7.3 功能测试

7. 博客主页功能实现

7.1 主页的导航栏

7.1.1 编写前端Ajax请求代码 

7.1.2 编写后端代码

7.2 分页功能  

8. 密码使用加盐的算法进行加密

9. Session持久化到Redis

功能优化

1. 登录页面添加图片验证码验证 

2. 删除列表页特殊的markdown字符

结语



1. 前期项目准备

1.1 创建项目

第一步 : 创建SSM项目 

 个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

 第二步: 添加项目依赖

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

 个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

1.2 准备项目

a. 删除项目中无用的文件和目录

b. 引入前端页面     

(这里就不打算总结前端页面设计的具体代码了,我们把重点放在后端是实现以及前端与后端交互.)(具体的前端代码,我会将此代码放在项目路径下面,然后大家根据下面的步骤进行下载就可以了).

前端代码在项目中路径,大家将有关于前端的代码放在resources/static下面就可以了

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

c. 初始化数据库

我使用的MySQL版本是5.7版本,下面是建库建表的sql

-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;

-- 使⽤数据数据
use mycnblog;

-- 创建表[⽤户表]
drop table if exists userinfo;
create table userinfo(
    id int primary key auto_increment,
    username varchar(100) not null,
    password varchar(32) not null,
    photo varchar(500) default '',
    createtime timestamp default current_timestamp,
    updatetime timestamp default current_timestamp,
    `state` int default 1
) default charset 'utf8mb4';

alter table userinfo modify password varchar(65);

-- 创建⽂章表
drop table if exists articleinfo;
create table articleinfo(
    id int primary key auto_increment,
    title varchar(100) not null,
    content text not null,
    createtime datetime default CURRENT_TIMESTAMP,
    updatetime datetime default CURRENT_TIMESTAMP,
    uid int not null,
    rcount int not null default 1,
    `state` int default 1
)default charset 'utf8mb4';

-- 创建视频表
drop table if exists videoinfo;
create table videoinfo(
    vid int primary key,
    `title` varchar(250),
    `url` varchar(1000),
    createtime timestamp default current_timestamp,
    updatetime timestamp default current_timestamp,
    uid int
)default charset 'utf8mb4';
alter table userinfo add unique(username);
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`,`createtime`, `updatetime`, `state`) 
VALUES(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);

-- ⽂章添加测试数据
insert into articleinfo(title,content,createtime,updatetime,uid) values('Java','Java正文','2021-12-06 17:10:48', '2021-12-06 17:10:48',1);
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);
insert into articleinfo(title,content,uid) values('Java','Java正文',1);

d. 项目分层

我们按照统一的格式,将项目进行分层操作

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

e. 项目的配置文件application.yml

# 配置数据库的连接字符串
spring:
#  jackson:
#    date-format: yyyy-MM-dd HH:mm:ss      # 设置时间的格式
#    time-zone: GMT+8               # 设置地域
  datasource:
    url: jdbc:mysql://127.0.0.1/mycnblog?characterEncoding=utf8
    username: root
    password: 111111
    driver-class-name: com.mysql.cj.jdbc.Driver
server:
  port: 8800
# 设置 Mybatis 的 xml 保存路径
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置打印 MyBatis 执行的 SQL
logging:
  level:
    com:
      example:
        demo: debug

至此,我们项目的前期准备就完成了,接下来我们将一一进行实现功能.

2. 添加统一的返回格式

        为了与前端更方便更有效率的进行交互,我们可以规定好统一的返回格式给前端,使用了SpringAOP的思想,在项目的common文件夹中创建一个类为AjaxResult.class.我们返回给前端的数据格式为Json格式的数据,主要包括了状态码,状态码描述信息,以及返回的数据.具体代码如下.

具体分为两个方法,一个是访问成功,一个是失败.然后对这两个方法进行重载.

package com.example.demo.common;

import lombok.Data;
import org.springframework.stereotype.Component;

import java.io.Serializable;

/**
 * Created with IntelliJ IDEA.
 * Description:普通的对象,统一的返回格式
 * User: YAO
 * Date: 2023-07-17
 * Time: 19:17
 */
@Data
public class AjaxResult implements Serializable {
    // 1.状态码
    private Integer code;

    // 2.状态码描述信息
    private String msg;

    // 3.返回的数据
    private Object data;

    /**
     * 1. 操作成功返回的结果
     */
    public static AjaxResult success(Object data){
        AjaxResult result = new AjaxResult();
        result.setCode(200);
        result.setMsg("");
        result.setData(data);
        return result;
    }
    public static AjaxResult success(int code,Object data){
        AjaxResult result = new AjaxResult();
        result.setCode(code);
        result.setMsg("");
        result.setData(data);
        return result;
    }

    public static AjaxResult success(String msg,int code,Object data){
        AjaxResult result = new AjaxResult();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }

    /**
     * 2. 返回失败结果
     */
    public static AjaxResult fail(int code,String msg){
        AjaxResult result = new AjaxResult();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(null);
        return result;
    }

    public static AjaxResult fail(String msg,int code,Object data){
        AjaxResult result = new AjaxResult();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }
}

为了保证最后的返回格式的正确性,我们最后对数据的返回的格式进行验证.在config文件目录下创建返回格式的保底类:ResponseAdvice,对特殊的String类型进行处理. 

package com.example.demo.config;

import com.example.demo.common.AjaxResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * Created with IntelliJ IDEA.
 * Description:统一返回格式,最保底的返回数据的格式
 *    说明:在返回数据之前,检测数据的类型是否为统一的对象,如果不是,封装成统一的数据格式
 * User: YAO
 * Date: 2023-07-17
 * Time: 19:28
 */
@ControllerAdvice // 统一返回格式
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 只有返回为true的时候,才会调用supports方法
     * @param returnType
     * @param converterType
     * @return
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    /**
     * 对数据格式进行校验和封装
     * @param body
     * @param returnType
     * @param selectedContentType
     * @param selectedConverterType
     * @param request
     * @param response
     * @return
     */
    @SneakyThrows   // 抛出异常
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof AjaxResult) {
            return body;
        }
        if (body instanceof String) {
            // String 比较特殊需要单独处理
            // 使用Jackson封装成成功返回的对象的格式
            return objectMapper.writeValueAsString(AjaxResult.success(body));
        }
        return AjaxResult.success(body);
    }
}

3. 配置登录拦截器

我们对有些系统功能进行捕获用户的登录状态,实现必须登录之后才能进行使用功能.在config的文件目录下创建LoginInterceptor.class.实现HandlerInterceptor接口,重写preHandle方法.获取当前登录的用户的SESSION,判断是否为空,为空就重定向到登录页面,然后将自定义好的登录拦截器进行配置到项目中(添加注解@Configuration,实现接口WebMvcConfigurer,重写addInterceptors方法)具体代码如下:

1.创建全局变量的 USER_SESSION_KEY(SESSION的key值)

package com.example.demo.common;

/**
 * Created with IntelliJ IDEA.
 * Description:全局变量
 * User: YAO
 * Date: 2023-07-18
 * Time: 10:58
 */
public class AppVariable {
    // 全局变量,session的key值
    public static final String USER_SESSION_KEY = "USER_SESSION_KEY";
    // 所有用户的key值是一样的但是内容是不一样的.就相当于大家都用的是钥匙但是钥匙的类型是不一样的.
    // 这里只是一个统一的格式,或者是同一种规格
}

2. 自定义拦截器 

package com.example.demo.config;

import com.example.demo.common.AppVariable;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Created with IntelliJ IDEA.
 * Description:自定义登录功能呢的拦截器
 * User: YAO
 * Date: 2023-07-18
 * Time: 11:02
 */
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * true --> 用户已经登录
     * false --> 用户未登录,跳转到登录页
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute(AppVariable.USER_SESSION_KEY) != null){
            // 用户已经登录
            return true;
        }
        // 用户未登录,重定向到登录界面
        response.sendRedirect("/login.html");
        return false;
    }
}

3. 将自定义拦截器加入到项目的配置中.(具体的拦截界面,大家先不要全部添加,可以随着项目的功能的实现,对指定的页面和路由进行拦截.)

package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Created with IntelliJ IDEA.
 * Description:项目的配置文件
 * User: YAO
 * Date: 2023-07-18
 * Time: 11:07
 */
@Configuration
public class AppConfig implements WebMvcConfigurer {

    /**
     * 1. 配置自定义的拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/img/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/editor.md/**")

                .excludePathPatterns("/login.html")
                .excludePathPatterns("/blog_content.html")
                .excludePathPatterns("/blog_list.html")
                .excludePathPatterns("/reg.html")

                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/reg")
                .excludePathPatterns("/art/detail")
                .excludePathPatterns("/user/getuserbyid")
                .excludePathPatterns("/art/incr-rcount")
                .excludePathPatterns("/art/add")
                .excludePathPatterns("/art/listbypage")
                .excludePathPatterns("/user/loginstate");
    }
}

4. 实现用户的注册功能

此处我们就用到了Servlet的和前端交互的思想了.以下所有的功能实现就是下面这两步:

  • 前端向用户发送Ajax请求
  • 后端接收请求,进行响应 

4.1 编写前端Ajax请求代码 

reg.html文件中进行补充代码

我们对这个提交的按钮进行构造请求 

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

<script>
        // 1.提交注册时间(jQuery进行操作)
        function mysub(){
            // 1.1 非空校验
            var username = jQuery("#username");
            var password = jQuery("#password");
            var password2 = jQuery("#password2");
            if(username.val() == ""){
                alert("请输入用户名");
                username.focus(); // 将鼠标光标设置到指定位置
                return;
            }
            if(password.val() == ""){
                alert("请输入密码");
                password.focus();
                return;
            }
            if(password2.val() == ""){
                alert("请输入确认密码");
                password2.focus();
                return;
            }
            // 1.2 判断两次密码是否一致
            if(password.val() != password2.val()){
                alert("两次输入的密码不一致请检查");
                password.focus();
                return;
            }
            // 1.3 Ajax提交请求
            jQuery.ajax({
                url:"/user/reg",
                type:"post",
                data:{"username":username.val(),"password":password.val()},
                // 回调函数,请求成功得到响应.
                success:function(result){
                    // 判断响应的结果,状态码200 data:修改返回值1
                    if(result != null && result.code == 200 && result.data == 1){
                        // 执行成功 
                        if(confirm("注册成功!是否跳转登录页面?")){
                            location.href="/login.html";
                        }
                    }else{
                        alert("执行失败,请稍后再试");
                    }
                }
            })
        }
    </script>

 具体的注册页面个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

4.2 编写后端代码

因为要对数据库中的用户表进行操作,所以后段要对用户表进行创建对象.

a. 用户信息类UserInfo.class

package com.example.demo.entity;

import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * Created with IntelliJ IDEA.
 * Description:用户表对象
 * User: YAO
 * Date: 2023-07-18
 * Time: 9:24
 */
@Data
public class UserInfo implements Serializable {
    private Integer id;
    private String username;
    private String password;
    private String photo;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private Integer state;
}

b. Mapper层(UserMapper)

@Mapper
public interface UserMapper {
    /**
     * 1.注册功能
     */
    int reg(UserInfo userInfo);
}

c. MyBatis的xml文件的构造

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ArticleMapper">

    <select id="getArtCountByUid" resultType="java.lang.Integer">
        select count(*) from articleinfo where uid=#{uid}
    </select>

</mapper>

d. 服务层Service

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    /**
     * 1. 注册功能
     */
    public int reg(UserInfo userInfo){
        return userMapper.reg(userInfo);
    }
}

e. Controller层

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @Autowired
    private ArticleService articleService;

    @RequestMapping("/reg")
    public AjaxResult reg(UserInfo userInfo){
        // 1. 进行非空校验和参数的有效性
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())||
                !StringUtils.hasLength(userInfo.getPassword())){
            return AjaxResult.fail(-1,"非法参数");
        }
        // 2.密码加密
        String olderPassword = userInfo.getPassword();
        // 加密
        userInfo.setPassword(PasswordUtils.encrypt(olderPassword));
        // 2.返回成功的修改值
        return AjaxResult.success(userService.reg(userInfo));
    }
}

后端代码完成

4.3 测试功能

注册小白用户 

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

数据库用户展示(密码是经过加盐加密的,后面会讲到这一点)

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

我们的注册功能就结束了.

5. 实现用户的登录功能

         用户登录功能与注册功能逻辑差不多,都是先进行前端 提交ajax数据请求给后端,在进行效验,其中后端进行数据库查询判断用户名或者密码是否存在且正确。

5.1 编写前端Ajax请求代码 

 对提交按钮进行方法的实现个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

<script>
        function mysub(){
            // 1. 非空校验
            var username = jQuery("#username");
            var password = jQuery("#password");
            if(username.val() == ""){
                alert("请输入用户名!");
                username.focus();
                return;
            }
            if(password.val() == ""){
                alert("请输入密码!");
                password.focus();
                return;
            }
            // 2. 构造ajax请求
            jQuery.ajax({
                url:"/user/login",
                type:"post",
                data:{"username":username.val(),"password":password.val()},
                success: function(result){
                    if(result != null && result.code == 200 && result.data != null){
                        // 执行成功 
                        location.href="/myblog_list.html";
                    }else{
                        alert("用户名或密码错误,请重新登录!");
                    }
                }
            })
        }
    </script>

5.2 编写后端代码

a. Mapper 层(UserMapper)

    /**
     * 2.登录功能
     */
    UserInfo getUserByName(@Param("username") String username);

b. MyBatis的xml文件的构造

<!--    2. 登录功能-->
    <select id="getUserByName" resultType="com.example.demo.entity.UserInfo">
        select * from userinfo where username=#{username}
    </select>

c. service层

    /**
     * 2. 根据名字查询用户信息
     */
    public UserInfo getUserByName(String username){
        return userMapper.getUserByName(username);
    }

d. Controller层

(其中代码加入了密码加盐加密的实现,这个具体后面再将)

@RequestMapping("/login")
    public AjaxResult login(HttpServletRequest request,String username, String password){
        System.out.println(username + " " + password);
        // 1. 进行非空校验和参数的有效性
        if (!StringUtils.hasLength(username)|| !StringUtils.hasLength(password)){
            return AjaxResult.fail(-1,"非法参数");
        }
        // 2.查询数据库
        UserInfo userInfo = userService.getUserByName(username);
        if (userInfo != null && userInfo.getId() > 0){
            // 使用加盐的验证
            if (PasswordUtils.check(password,userInfo.getPassword())){
                // 登陆成功
                // 将用户存储带到Session中
                HttpSession session = request.getSession();
                session.setAttribute(AppVariable.USER_SESSION_KEY,userInfo);
                // 返回之前将密码等信息进行隐藏
                userInfo.setPassword("");
                return AjaxResult.success(userInfo);
            }
        }
        return AjaxResult.success(0,null);
    }

5.3 测试功能 

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

 跳转到登录页个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6. 个人主页展示功能

6.1 左侧个人信息

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.1.1 编写前端Ajax请求代码 

myblog_list.html文件进行补充

这里仅仅对名字和文章数量进行动态展示,其他的标签就没实现,.但是实现的原理都是一样的,后续会做扩展.

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

<script>
        function showInfo(){
            jQuery.ajax({
                url:"/user/showinfo",
                type:"get",
                data:{},
                success: function(result){
                    if(result != null && result.code==200){
                        jQuery("#username").text(result.data.username);
                        jQuery("#artCount").text(result.data.artCount);
                    }else{{
                        alert("当前用户信息加载失败")
                    }}
                } 
            })
        }
        showInfo();
</script>

6.1.2 编写后端代码

因为要进行获取当前登录用户的信息,所以我们得实现一个得到当前用户的方法

我们在common文件目录下创建一个类UserSessionUtils实现用户登录的相关操作

实现方法getSessionUser()

package com.example.demo.common;

import com.example.demo.entity.UserInfo;
import com.example.demo.entity.vo.UserInfoVo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * Created with IntelliJ IDEA.
 * Description:当前登录用户相关的操作
 * User: YAO
 * Date: 2023-07-18
 * Time: 14:28
 */
public class UserSessionUtils {
    /**
     * 1. 得到当前的登录用户
     * @param request
     * @return
     */
    public static UserInfo getSessionUser(HttpServletRequest request){
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute(AppVariable.USER_SESSION_KEY) != null){
            // 当前用户已经正常登录
            return (UserInfo) session.getAttribute(AppVariable.USER_SESSION_KEY);
        }
        return null;
    }
}
a. Mapper 层(UserMapper)
/**
     * 3.根据id返回用户信息
     */
    UserInfo getUserById(@Param("id") Integer id);
b. MyBatis的xml文件的构造
<select id="getUserById" resultType="com.example.demo.entity.UserInfo">
        select * from userinfo where id=#{id}
    </select>
c. service层
/**
     * 3.根据id返回用户信息
     */
    public UserInfo getUserById(Integer id){
        return userMapper.getUserById(id);
    }
d. Controller层
@RequestMapping("/showinfo")
    public AjaxResult showinfo(HttpServletRequest request){
        UserInfoVo userInfoVo = new UserInfoVo();
        // 1. 得到当前登录用户
        UserInfo userInfo = UserSessionUtils.getSessionUser(request);
        if (userInfo == null){
            return AjaxResult.fail(-1,"非法请求");
        }
        // 将获得对象赋值给UserinfoVo
        BeanUtils.copyProperties(userInfo,userInfoVo);// Spring 提供的深拷贝的方法
        // 2. 得到用户发表文章的总数
        userInfoVo.setArtCount(articleService.getArtCountByUid(userInfo.getId()));
        return AjaxResult.success(userInfoVo);
    }

6.1.3 测试功能 

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.2 右侧个人博客列表信息 

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.2.1 编写前端Ajax请求代码 

myblog_list.html文件进行补充

 <script>
        function showInfo(){
            jQuery.ajax({
                url:"/user/showinfo",
                type:"get",
                data:{},
                success: function(result){
                    if(result != null && result.code==200){
                        jQuery("#username").text(result.data.username);
                        jQuery("#artCount").text(result.data.artCount);
                    }else{{
                        alert("当前用户信息加载失败")
                    }}
                } 
            })
        }
        showInfo();

        function getMyArtList(){
                jQuery.ajax({
                    url:"/art/mylist",
                    type:"POST",
                    data:{},
                    success:function(result){
                        if(result!=null && result.code==200){
                           // 有两种情况,一种是发表了文章,一种是没有发表任何文章 
                           if(result.data!=null && result.data.length>0){
                            // 此用户发表文章了
                            var artListDiv ="";
                            for(var i=0;i<result.data.length;i++){
                                var artItem = result.data[i];
                                artListDiv += '<div class="blog">';
                                artListDiv += '<div class="title">'+artItem.title+'</div>';
                                artListDiv += '<div class="date">'+artItem.updatetime+'</div>';
                                artListDiv += '<div class="desc">';
                                artListDiv += artItem.content;
                                artListDiv += '</div>';
                                artListDiv += '<a href="blog_content.html?id='+
                                    artItem.id + '" class="detail">查看全文 &gt;&gt;</a>&nbsp;&nbsp;';
                                artListDiv += '<a href="blog_edit.html?id='+
                                    artItem.id + '" class="detail">修改 &gt;&gt;</a>&nbsp;&nbsp;';  
                                artListDiv += '<a href="javascript:myDel('+
                                    artItem.id+');" class="detail">删除 &gt;&gt;</a>'; 
                                artListDiv += '</div>';    
                            }
                            jQuery("#artDiv").html(artListDiv);
                           }
                        }else{
                            alert("查询文章列表出错,请重试!");
                        }
                    }
                });
        }
        getMyArtList();
</script>

6.2.2 编写后端代码

文章信息表实体对象

import java.time.LocalDateTime;
import java.util.Date;

/**
 * Created with IntelliJ IDEA.
 * Description:文章表
 * User: YAO
 * Date: 2023-07-18
 * Time: 13:52
 */
@Data
public class ArticleInfo {
    private Integer id;
    private String title;
    private String content;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private LocalDateTime createtime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private LocalDateTime updatetime;
    private Integer uid;
    private Integer rcount;
    private Integer state;
}
a. Mapper 层(ArticleMapper)
/**
     * 2.根据uid查询对应用户的文章列表
     */
    List<ArticleInfo> getMyList(@Param("uid")Integer uid);
b. MyBatis的xml文件的构造
<select id="getMyList" resultType="com.example.demo.entity.ArticleInfo">
        select * from articleinfo where uid=#{uid}
    </select>
c. service层
    public List<ArticleInfo> getMyList(Integer uid){
        return articleMapper.getMyList(uid);
    }
d. Controller层
@RestController
@RequestMapping("/art")
public class ArticleController {
    @Autowired
    private ArticleService articleService;

    @RequestMapping("/mylist")
    public AjaxResult getMyList(HttpServletRequest request){
        UserInfo userInfo = UserSessionUtils.getSessionUser(request);
        if (userInfo == null){
            return AjaxResult.fail(-1,"非法请求");
        }
        List<ArticleInfo> list = articleService.getMyList(userInfo.getId());
        for (ArticleInfo articleInfo : list) {
            if (articleInfo.getContent().length() > 100){
                // 当文章的长度大于100字的时候,不完全进行展示
                articleInfo.setContent(articleInfo.getContent().substring(0,100) + "...");
            }
        }
        return AjaxResult.success(list);
    }
}

6.3 删除博客功能 

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.3.1 编写前端Ajax请求代码 

myblog_list.html文件进行补充

// 删除文章
        function myDel(id){
            if(confirm("确实删除?")){
                // 删除文章
                jQuery.ajax({
                    url:"art/del",
                    type:"get",
                    data:{"id":id},
                    success:function(result){
                        if(result!=null && result.code==200 && result.data==1){
                            alert("删除成功!");
                            // 刷新当前页面
                            location.href = location.href;
                        }else{
                            alert("抱歉:删除失败,请重试!");
                        }
                    }
                });
            }
        }

6.3.2 编写后端代码

a. Mapper 层(ArticleMapper)
    /**
     * 3.根据id进行删除文章
     */
    int del(@Param("id") Integer id,@Param("uid") Integer uid);
b. MyBatis的xml文件的构造
<delete id="del">
        delete from articleinfo where id=#{id} and uid=#{uid}
    </delete>
c. service层
public Integer del(Integer id,Integer uid){
        return articleMapper.del(id,uid);
    }
d. Controller层
@RequestMapping("/del")
    public AjaxResult del(HttpServletRequest request,Integer id){
        if (id == null || id <= 0){
            return AjaxResult.fail(-1,"参数异常");
        }
        UserInfo userInfo = UserSessionUtils.getSessionUser(request);
        if (userInfo == null){
            return AjaxResult.fail(-2,"用户未登录");
        }
        return AjaxResult.success(articleService.del(id,userInfo.getId()));
    }

6.3.3 测试功能

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

 6.4 查看全文功能 

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.4.1 编写前端Ajax请求代码 

blog-content.html文件进行补充

        查看全文的功能,要对文章的内容进行展示,以及文章的作者,发布信息,以及文章阅读量进行展示.需要根据url中的id进行获取指定博客的id,我们需要对id的提取进行设置方法,因为后面的修改文章也需要获取文章id,以及主页的列表也需要,所以将此方法写入到公共的js方法中,此处需要注意的操作就是,每次查看的时候就要给文章的阅读量进行加1操作.

common.js 

function getUrlValue(key){
    // 一开始获取的字符串为?id=2&v=1
    var params = location.search;
    if(params.length>1){
        //?id=2&v=1 去除这个问号
        params = location.search.substring(1);   // 从第二个字符开始取出
        var paramArr = params.split("&");   // 差分成key-value的数组
        for(var i=0;i<paramArr.length;i++){
            var kv = paramArr[i].split("=");
            if(kv[0] = key){
                return kv[1];
            }
        } 
    }
    return "";
}
<script type="text/javascript">
            var editormd;
            function initEdit(md){
                editormd = editormd.markdownToHTML("editorDiv", {
                markdown : md, // Also, you can dynamic set Markdown text
                // htmlDecode : true,  // Enable / disable HTML tag encode.
                // htmlDecode : "style,script,iframe",  // Note: If enabled, you should filter some dangerous HTML tags for website security.
                });
            }

            // 获取当前url查询字符串参数的公共方法
            
          
            function getArtDetail(id){
                if(id==""){
                    alert("非法参数!");
                    return;
                }
                jQuery.ajax({
                    url:"art/detail",
                    type:"POST",
                    data:{"id":id},
                    success:function(result){
                        if(result!=null && result.code==200){
                            jQuery("#title").html(result.data.title);
                            jQuery("#updatetime").html(result.data.updatetime);
                            jQuery("#rcount").html(result.data.rcount);
                            initEdit(result.data.content);
                            // 得到用户 id
                            showUser(result.data.uid);
                        }else{
                            alert("查询失败,请重试!");  
                        }
                    }
                });
            }
            getArtDetail(getUrlValue("id"));

            // 查询用户的详情信息
            function showUser(id){
                jQuery.ajax({
                    url:"/user/getuserbyid",
                    type:"POST",
                    data:{"id":id},
                    success:function(result){
                        if(result!=null && result.code==200 && result.data.id>0){
                            jQuery("#username").text(result.data.username);
                            jQuery("#artCount").text(result.data.artCount);
                        }else{
                            alert("抱歉:查询用户信息失败,请重试!");
                        }
                    }
                });
            }
            // 阅读量 +1
            function updataRCount(){
                // 先得到文章 id
                var id = getUrlValue("id");
                if(id!=""){
                    jQuery.ajax({
                        url:"/art/incr-rcount",
                        type:"POST",
                        data:{"id":id},
                        success:function(result){}
                    });
                }
            }
            updataRCount();

    </script> 

6.4.2 编写后端代码

a. Mapper 层(ArticleMapper)
    /**
     * 3.根据id获取文章的具体内容
     */
    ArticleInfo getDetail(@Param("id")Integer id);

/**
     * 4.文章阅读量进行加1
     */
    int incrRCount(@Param("id") Integer id);
b. MyBatis的xml文件的构造
<select id="getDetail" resultType="com.example.demo.entity.ArticleInfo">
        select * from articleinfo where id=#{id}
    </select>

<update id="incrRCount">
        update articleinfo set rcount=rcount+1 where id=#{id};
    </update>
c. service层
public int getArtCountByUid(Integer uid){
        return articleMapper.getArtCountByUid(uid);
    }

public ArticleInfo getDetail(Integer id){
        return articleMapper.getDetail(id);
    }
d. Controller层
@RequestMapping("/incr-rcount")
    public AjaxResult incrRCount(Integer id){
        if (id == null || id <= 0){
            return AjaxResult.fail(-1,"参数异常");
        }
        return AjaxResult.success(articleService.incrRCount(id));
    }

@RequestMapping("/detail")
    public AjaxResult getDetail(HttpServletRequest request,Integer id){
        if (id == null || id <= 0){
            return AjaxResult.fail(-1,"参数异常");
        }
        return AjaxResult.success(articleService.getDetail(id));
    }

6.4.3 测试功能

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.5 修改博客功能 

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.5.1 编写前端Ajax请求代码 

blog_edit.html文件进行补充

修改功能就是点击跳转到编辑页面,首先发送请求得到当前文章的内容,再进行修改提交发送Ajax请求

function mysub(){
            // 1.非空效验
            var title = jQuery("#title");
            if(title.val()==""){
                alert("请先输入标题!");
                title.focus();
                return;
            }
            if(editor.getValue()==""){
                alert("请先输入正文!");
                return;
            }
            // 2.进行修改操作
            if(confirm("确定修改文章?")){
                    jQuery.ajax({
                    url:"/art/update",
                    type:"POST",
                    data:{"id":id,"title":title.val(),"content":editor.getValue()},
                    success:function(result){
                        if(result!=null && result.code==200 && result.data==1){
                            alert("修改成功!");
                            location.href = "myblog_list.html";
                        }else{
                            alert("抱歉:操作失败,请重试!");
                        }
                    }
                });
            }  
            
        }
        // 文章初始化
        function initArt(){
            // 得到当前页面 url 中的参数 id(文章id)
            id = getUrlValue("id");
            if(id==""){
                alert("无效参数");
                location.href = "myblog_list.html";
                return;
            }
            // 请求后端,查询文章的详情信息
            jQuery.ajax({
                url:"art/detail",
                type:"POST",
                data:{"id":id},
                success:function(result){
                    if(result!=null && result.code==200){
                        jQuery("#title").val(result.data.title);
                        initEdit(result.data.content);
                    }else{
                        alert("查询失败,请重试!");  
                    }
                }
            });
        }
        initArt();

6.5.2 编写后端代码

a. Mapper 层(ArticleMapper)
/**
     * 3.根据id获取文章的具体内容
     */
    ArticleInfo getDetail(@Param("id")Integer id);

 /**
     * 6. 修改文章
     * @param articleInfo
     * @return
     */
    int update(ArticleInfo articleInfo);
b. MyBatis的xml文件的构造
<select id="getDetail" resultType="com.example.demo.entity.ArticleInfo">
        select * from articleinfo where id=#{id}
    </select>

<update id="update">
        update articleinfo set title=#{title},content=#{content},updatetime=#{updatetime}
        where id=#{id} and uid=#{uid}
    </update>
c. service层
    public ArticleInfo getDetail(Integer id){
        return articleMapper.getDetail(id);
    }

    public int update(ArticleInfo articleInfo){
        return articleMapper.update(articleInfo);
    }
d. Controller层
@RequestMapping("/detail")
    public AjaxResult getDetail(HttpServletRequest request,Integer id){
        if (id == null || id <= 0){
            return AjaxResult.fail(-1,"参数异常");
        }
        return AjaxResult.success(articleService.getDetail(id));
    }

@RequestMapping("/update")
    public AjaxResult update(HttpServletRequest request, ArticleInfo articleInfo){
        if (articleInfo == null || !StringUtils.hasLength(articleInfo.getTitle())
                || !StringUtils.hasLength(articleInfo.getContent()) || articleInfo.getId()==null){
            return AjaxResult.fail(-1,"非法参数");
        }
        UserInfo userInfo = UserSessionUtils.getSessionUser(request);
        if (userInfo == null || userInfo.getId() == null){
            return AjaxResult.fail(-2,"无效用户");
        }
        articleInfo.setUid(userInfo.getId());
        articleInfo.setUpdatetime(LocalDateTime.now());
        return AjaxResult.success(articleService.update(articleInfo));
    }

6.5.3 测试功能

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.6 发布博客功能

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.6.1 编写前端Ajax请求代码 

blog_add.html文件进行补充

<script>
        var editor;
        function initEdit(md){
            // 编辑器设置
            editor = editormd("editorDiv", {
                // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. 
                width: "100%",
                // 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度
                height: "calc(100% - 50px)",
                // 编辑器中的初始内容
                markdown: md,
                // 指定 editor.md 依赖的插件路径
                path: "editor.md/lib/",
                saveHTMLToTextarea: true // 
            });
        }
        initEdit("# 在这里写下一篇博客"); // 初始化编译器的值
        // 提交
        function mysub(){
            if(confirm("确认提交?")){
                // 1.非空效验
                var title = jQuery("#title");
                if(title.val()==""){
                    alert("请先输入标题!");
                    title.focus();
                    return;
                }
                if(editor.getValue()==""){
                    alert("请先输入文章内容!");
                    return;
                }
                // 2.请求后端进行博客添加操作
                jQuery.ajax({
                    url:"/art/add",
                    type:"POST",
                    data:{"title":title.val(),"content":editor.getValue()},
                    success:function(result){
                        if(result!=null && result.code==200 && result.data==1){
                            if(confirm("文章添加成功!是否继续添加文章?")){
                                // 刷新当前页面
                                location.href = location.href;
                            }else{
                                location.href = "/myblog_list.html";
                            }
                        }else{
                            alert("抱歉,文章添加失败,请重试!");
                        }
                    }
                });
            }
        }
        
    </script>

6.6.2 编写后端代码

a. Mapper 层(ArticleMapper)
/**
     * 5. 添加文章
     * @param articleInfo
     * @return
     */
    int add(ArticleInfo articleInfo);
b. MyBatis的xml文件的构造
<insert id="add">
        insert into articleinfo(title,content,uid) values(#{title},#{content},#{uid})
    </insert>
c. service层
public int add(ArticleInfo articleInfo){
        return articleMapper.add(articleInfo);
    }
d. Controller层
@RequestMapping("/add")
    public AjaxResult add(HttpServletRequest request, ArticleInfo articleInfo){
        // 1.进行非空校验
        if (articleInfo == null || !StringUtils.hasLength(articleInfo.getTitle())
        ||  !StringUtils.hasLength(articleInfo.getContent())){
            return AjaxResult.fail(-1,"参数异常");
        }
        // 2.获取当前登录用户的id,将当前登录用户的id进行设置到传入sql的文章信息的对象,尽心添加操作的时候就会准确的直到这是当前登录用户记性发布的
        UserInfo userInfo = UserSessionUtils.getSessionUser(request);
        if (userInfo == null || userInfo.getId() <= 0){
            return AjaxResult.fail(-2,"无效的登录用户");
        }
        articleInfo.setUid(userInfo.getId());
        int result = articleService.add(articleInfo);
        return AjaxResult.success(result);
    }

6.6.3 功能测试

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring 个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.7 注销用户功能

        我们要给检测到登录状态的页面加注销功能, 如个人主页,写博客,主页.因为是公共操作,所以直接写成一个公共的js,然后在需要的页面直接引入就可以.

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

6.7.1 编写前端Ajax请求代码 

common.js

// 公共操作 -- 注销登录
function logout(){
    if(confirm("确定注销??")){
        // 注销登录
        jQuery.ajax({
            url:"user/logout",
            type:"get",
            data:{},
            success:function(result){
                if(result!=null && result.code==200){
                    location.href = "/login.html";
                }
            }
        });
    }
}

6.7.2 编写后端代码

a. Mapper 层(ArticleMapper)
/**
     * 5. 添加文章
     * @param articleInfo
     * @return
     */
    int add(ArticleInfo articleInfo);
b. MyBatis的xml文件的构造
<insert id="add">
        insert into articleinfo(title,content,uid) values(#{title},#{content},#{uid})
    </insert>
c. service层
public int add(ArticleInfo articleInfo){
        return articleMapper.add(articleInfo);
    }
d. Controller层
@RequestMapping("/add")
    public AjaxResult add(HttpServletRequest request, ArticleInfo articleInfo){
        // 1.进行非空校验
        if (articleInfo == null || !StringUtils.hasLength(articleInfo.getTitle())
        ||  !StringUtils.hasLength(articleInfo.getContent())){
            return AjaxResult.fail(-1,"参数异常");
        }
        // 2.获取当前登录用户的id,将当前登录用户的id进行设置到传入sql的文章信息的对象,尽心添加操作的时候就会准确的直到这是当前登录用户记性发布的
        UserInfo userInfo = UserSessionUtils.getSessionUser(request);
        if (userInfo == null || userInfo.getId() <= 0){
            return AjaxResult.fail(-2,"无效的登录用户");
        }
        articleInfo.setUid(userInfo.getId());
        int result = articleService.add(articleInfo);
        return AjaxResult.success(result);
    }

6.7.3 功能测试

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring 个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

7. 博客主页功能实现

7.1 主页的导航栏

这里需要注意的是,已经登录的用户的导航栏和未登录的有细微的区别

已经登录的主页是可以进行切换到个人主页的,未登录的是没有这个选项的.

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

 在进入主页上的时候发送请求的时候进行验证登录的状态,检测为登录,就给导航栏拼接个人主页的跳转按钮就可以了

7.1.1 编写前端Ajax请求代码 

在login.html中进行编写代码

 <script>
        // 根据登录的状态进行设置列表页的导航栏信息
        function loginstate(){
            jQuery.ajax({
                url:"/user/loginstate",
                type:"POST",
                data:{},
                success:function(result){
                    var navText='<img src="img/logo2.jpg" alt=""><span class="title">我的博客系统</span><span class="spacer"></span>';
                    if(result.code==200 && result!=null){
                        navText += '<a href="myblog_list.html">个人主页</a><a href="blog_add.html">写博客</a><a href="javascript:logout()">注销</a>'
                        jQuery("#nav").html(navText)
                    }else{
                        navText += '<a href="blog_add.html">写博客</a><a href="login.html">登录</a>'
                        jQuery("#nav").html(navText)
                    }
                }
            })
        }
        loginstate();
 </script>

7.1.2 编写后端代码

因为不用操作数据库,所以只编写Controller层就可以了

@RequestMapping("loginstate")
    public AjaxResult loginstate(HttpServletRequest request){
        UserInfo userInfo = UserSessionUtils.getSessionUser(request);
        if (userInfo == null || userInfo.getId()<=0){
            return AjaxResult.fail(-1,"当前用户未登录");
        }
        return AjaxResult.success(userInfo);
    }

7.2 分页功能  

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

实现分页功能,就要先获取总的文章数量,以及定义好每页显示多少条内容.具体总结为下面.

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

8. 密码使用加盐的算法进行加密

为了更形象的展示,我使用了别的画图软件

主要总结了三种加密方式\

  • a. 使用传统的MD5进行加密
  • b. 自己实现加盐算法进行加密
  • c. 使用Spring Security进行加密(Spring官方的)

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

本项目采用自己写的加盐算法:

package com.example.demo.common;

import org.springframework.beans.BeanUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.util.UUID;

/**
 * Created with IntelliJ IDEA.
 * Description:关于密码的工具类
 * User: YAO
 * Date: 2023-07-19
 * Time: 13:49
 */
public class PasswordUtils {
    /**
     * 1.加盐并生成密码
     * @param password 明文密码
     * @return 保存到数据库的密码
     */
    public static String encrypt(String password){
        // 1. 32位盐值
        String salt = UUID.randomUUID().toString().replace("-","");
        // 2. 生成加盐之后的密码
        String saltPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
        // 3. 生成最终保存在数据库的密码   约定:32位盐值+$+32位加盐之后MD5加密之后的密码  -->65位
        String finalPassword = salt + "$" + saltPassword;
        return finalPassword;
    }

    /**
     * 2.生成用于验证的加盐密码
     * @param password  明文密码
     * @param salt  盐值
     * @return 验证的密码
     */
    public static String encrypt(String password,String salt){
        // 1. 生成一个加盐之后的密码
        String saltPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
        // 2.生成最终的密码  约定:32位盐值+$+32位加盐之后MD5加密之后的密码  -->65位
        String finalPassword = salt + "$" + saltPassword;
        return finalPassword;
    }


    /**
     * 3.验证密码
     * @param inputPassword  用户输入的明文密码
     * @param finalPassword  数据库存储的最终的密码
     * @return
     */
    public static Boolean check(String inputPassword, String finalPassword){
        if (StringUtils.hasLength(inputPassword) && StringUtils.hasLength(finalPassword)
        && finalPassword.length() == 65){
            // 1.得到盐值
            String salt = finalPassword.split("\\$")[0];
            // 2.将用户输入的铭文密码进行加盐加密
            String finalInputPassword = encrypt(inputPassword,salt);
            // 3.进行比较
            return finalInputPassword.equals(finalPassword);
        }
        return false;
    }

}

在注册的Controller以及登录的Controller可以看出相应的使用.

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

9. Session持久化到Redis

Redis的具体使用我会另外进行总结一个系列,这里直接将SSM项目Session持久化到Redis进行总结.

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

在我们的项目只需要配置好依赖文件配置文件,指定Session存储的类型为redis,指定好Session存储的路径以及仓库.

我们使用redis的可视化工具可以查看我们存储的Session.

个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring

功能优化

1. 登录页面添加图片验证码验证 

添加验证码https://blog.csdn.net/weixin_46114074/article/details/131957952

2. 删除列表页特殊的markdown字符

删除列表页markdown字符https://blog.csdn.net/weixin_46114074/article/details/131978050

结语

        写到这里,这个基础的SSM项目就算实现的差不多了,后续还可以扩展很多的功能,比如评论,草稿,登录验证码登功能,后续时间不紧的话我也会进行响应的更新.好啦,就到这里咯,谢谢!!!可以的话,给个赞,点个关注吧!!!😊😊😊

代码链接👇👇👇👇👇

SSM博客系统(前端+后端)https://gitee.com/yao-fa/advanced-java-ee/tree/master/MyCnBlog-SSM个人博客系统(SSM版 前端+后端),Java项目-博客系统,前端,spring boot,SSM,java,spring文章来源地址https://www.toymoban.com/news/detail-600098.html


到了这里,关于个人博客系统(SSM版 前端+后端)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端综合项目-个人博客网页设计

    个人博客前端部分设计 从零设计一个页面太难了,我们可以通过预预计的效果去写代码实现! 总不能没头没脑的边写边设计吧,总得有个参考 前端知识不做赘述了,大家可以去看看我之前写的前端博客 这只是我的设计,css更多定制内容,您可以灵活变通! 我的博客系统分

    2024年02月05日
    浏览(43)
  • SSM项目-博客系统

    在线体验项目:登陆页面 项目连接:huhublog_ssm: 个人博客系统 技术栈:SpringBoot、SpringMVC、Mybatis、Redis、JQuery、Ajax、Json (gitee.com) SpringBoot、SpringWeb(SpringMVC)、MyBatis、MySQL(8.x)、Redis(存储验证码及用户登录信息) 注册页面、登录页面 -- 用户表(显式字段:用户编号、登录用户名、

    2024年02月14日
    浏览(23)
  • 部署若依前端vue3后端SSM项目实战

    前端部署在nginx 后端部署在tomcat 系统 前端服务器 后端服务器 前端项目架构 后端项目架构 win10 nginx1.22.1 tomcat9 vue3 ssm linux 亲测! 环境同样适用。 前端项目修改项目下载地址 GIThub RuoYi-Vue3 vite.config.js 基本不用动, 默认打包为文件夹名 dist , 不用dist可以自定义(我用的自定义) 前

    2024年04月09日
    浏览(32)
  • 个人博客系统【项目篇】

    1.CSS 2. JS 3.HTML 4.数据库 5.pom.xml image  

    2024年02月11日
    浏览(44)
  • 博客系统后端(项目系列2)

    目录 前言 : 1.准备工作 1.1创建项目 1.2引入依赖 1.3创建必要的目录 2.数据库设计 2.1博客数据 2.2用户数据 3.封装数据库 3.1封装数据库的连接操作 3.2创建两个表对应的实体类 3.3封装一些必要的增删改查操作 4.前后端交互逻辑的实现 4.1博客列表页 4.1.1约定前后端交互接口 4.1

    2024年02月10日
    浏览(27)
  • 自动化项目实战 [个人博客系统]

    效验第一篇博客 不是 “自动化测试” 退出到登录页面,用户名密码为空

    2024年02月08日
    浏览(43)
  • Java Web课设——个人博客(双端系统)

    先看看演示视频吧 演示图         个人博客管理系统采用Springboot2.4.5框架开发,是标准的MVC模式,将这个系统划分为View层、Controller层、Service层、DAO层和持久层五层。其中,Spring MVC负责请求的转发和视图管理,Spring实现业务对象管理,MyBatis-Plus作为数据对象的持久化引擎

    2024年02月02日
    浏览(27)
  • 基于java的个人博客系统设计与实现

    基于java的个人博客系统设计与实现 研究背景: 在互联网和信息技术快速发展的时代,人们对于信息交流和知识分享的需求越来越大。个人博客作为一种自由、开放、个性化的信息发布平台,受到了广泛的关注和青睐。个人博客系统的设计和实现也成为了计算机领域中的一个

    2024年02月04日
    浏览(34)
  • (附源码)基于Spring Boot的个人博客系统的设计与实现 毕业设计271611

    目 录 摘要 1 1 绪论 1 1.1研究意义 1 1.2开发背景 1 1.3系统开发技术的特色 1 1.4论文结构与章节安排 1 2 个人博客系统系统分析 3 2.1 可行性分析 3 2.2 系统流程分析 3 2.2.1数据增加流程 3 2.3.2数据修改流程 4 2.3.3数据删除流程 4 2.3 系统功能分析 4 2.3.1 功能性分析 4 2.3.2 非功能性分析

    2024年02月06日
    浏览(34)
  • 课程设计基于Spring Boot+Semantic ui的个人博客系统的设计与实现

    项目背景: 本项目是基于互联网软件开发技术搭建的一款个人博客系统,因为自己十分喜欢创作,比如写一些开源项目,写一些博客笔记,虽然网络上有许多大型博客系统,但是一直想写一个属于自己专有的博客系统,比如可以上传一些自己的知识笔记,上传一些自己的生活

    2024年02月04日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包