基于SpringBoot+MyBatis实现的个人博客系统(一)

这篇主要讲解一下如何基于SpringBoot和MyBatis技术实现一个简易的博客系统(前端页面主要是利用CSS,HTML进行布局书写),前端的静态页面代码可以直接复制粘贴,后端的接口以及前端发送的Ajax请求需要自己书写.

博客系统需要完成的接口:

  • 注册
  • 登录
  • 博客列表页展示
  • 博客详情页展示
  • 发布博客
  • 修改博客
  • .......

完整版代码详见Gitee:blogsystem · 徐明园/SSM配置信息 - 码云 - 开源中国 (gitee.com)

项目亮点:

  1. 密码实现加盐处理,确保安全性;
  2. Session升级,由原来的内存存储改为通过Redis存储,不会丢失,并且支持分布式部署;
  3. 功能升级,对于博客列表的展示添加了分页功能;
  4. 登录验证升级,添加了拦截器的功能对用于的登录进行校验;
  5. ......

一, 项目的搭建

1,导入依赖坐标(创建SpringBoot项目)

在书写任何一个项目的同时,需要先将项目的基础给搭建好,搭建项目需要提前考虑好项目的一些功能需要哪些依赖,从而进行添加,如何创建SpringBoot项目可以看我的另一篇博客:SpringBoot项目的创建和使用_蜡笔小心眼子!的博客-CSDN博客

博客系统需要添加的依赖如下:

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.16</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>blogsystem</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>blogsystem</name>
    <description>blogsystem</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>2.3.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2,书写配置文件

需要使用MyBatis技术的项目需要配置你的数据库相关的信息以及Mapper的xml文件存储的位置,同时也可以在配置文件中定义一下日志的打印级别,从而方便查看数据库操作的完整信息:

java 复制代码
#配置数据库连接信息
spring:
  datasource:
    url: "你自己的数据库"
    username: root
    password: "数据库对应的密码"
    driver-class-name: com.mysql.cj.jdbc.Driver

#配置Mapper的xml文件存储信息
mybatis:
#  xml的存储位置
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

#配置日志打印级别
logging:
  level:
    com:
      example:
        demo: debug

二, 将前端页面部署到项目中(resource文件下的static目录中)

对于后端程序员来说可以不用特别注重前端样式的书写,但是需要看得懂前端的代码以及和后端交互的请求即可(学有余力的情况下可以适当学习从而优化自己项目中的前端页面),这里的静态页面信息可以直接在我的码云中进行下载:SSM配置信息: 存放SSM项目中的一些配置信息 (gitee.com)博客系统(静态页面).zip · 徐明园/SSM配置信息 - 码云 - 开源中国 (gitee.com)SSM配置信息: 存放SSM项目中的一些配置信息 (gitee.com)

将下载好的前端页面全选之后直接复制到static目录下即可:

三, 初始化数据库

在配置文件中我们已经配置了数据库的连接信息,但是此时在数据库中还没有初始化一些数据,所以我们需要初始化数据,方便写项目的时候进行测试,初始化数据库的SQL代码也可以在我们的码云中进行下载:博客系统初始化数据库-ssm.sql · 徐明园/SSM配置信息 - 码云 - 开源中国 (gitee.com)

查看数据库中的表即相应表结构:

四, 对项目的整体架构进行分层

一个企业级的SM项目都需要对其进行合理的分层,每一层处理每一层的业务逻辑,我在项目中的分层如下:

  • common:一些工具类
  • config:配置信息类
  • controller:处理前端请求的类
  • entity:实体类(也可以写成model)
  • mapper:用来和Mapper.xml文件交互的接口类
  • service:处于controller和mapper之间的类

五, 书写前后端交互的功能

从这里开始就是项目的核心了,这里开始可以对前后端的接口和相应功能进行书写了!

1, 统一返回对象的封装

为了给前端返回统一的对象,后端需要定义一个类对返回的数据进行封装,该类包含code,msg和data三个属性,该类定义在common包下:

java 复制代码
package com.example.blogsystem.common;

import lombok.Data;

import java.io.Serializable;

/**
 * 统一返回对象
 * 返回成功的话 code 设置成 200
 * 反悔失败的话 code 设置成本身的 code
 */

@Data
public class AjaxResult implements Serializable {
    private int code;
    private String msg;
    private Object data;

    /**
     * 返回成功
     *
     * @param data
     * @return
     */
    public static AjaxResult success(Object data) {
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setCode(200);
        ajaxResult.setMsg("");
        ajaxResult.setData(data);
        return ajaxResult;
    }

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

    /**
     * 返回失败
     *
     * @param code
     * @param msg
     * @return
     */

    public static AjaxResult fail(Integer code, String msg) {
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setCode(code);
        ajaxResult.setMsg(msg);
        ajaxResult.setData("");
        return ajaxResult;
    }

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

2, 注册功能

注册功能就是用户给后端发送一次请求之后,后端就会在数据中新增一条用户记录!

前端

java 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>注册页面</title>
    <link rel="stylesheet" href="css/conmmon.css">
    <link rel="stylesheet" href="css/login.css">
    <!-- 引入jquery的js文件 -->
    <script src="js/jquery.min.js"></script>
</head>

<body>
<!-- 导航栏 -->
<div class="nav">
    <img src="img/logo2.jpg" alt="">
    <span class="title">我的博客系统</span>
    <!-- 用来占据中间位置 -->
    <span class="spacer"></span>
    <a href="blog_list.html">主页</a>
    <a href="login.html">登陆</a>
    <!-- <a href="#">注销</a> -->
</div>
<!-- 版心 -->
<div class="login-container">
    <!-- 中间的注册框 -->
    <div class="login-dialog">
        <h3>注册</h3>
        <div class="row">
            <span>用户名</span>
            <input type="text" id="username">
        </div>
        <div class="row">
            <span>密码</span>
            <input type="password" id="password">
        </div>
        <div class="row">
            <span>确认密码</span>
            <input type="password" id="password2">
        </div>
        <div class="row">
            <button id="submit" onclick="mysub()">提交</button>
        </div>
    </div>
</div>

<!-- 最好把js的代码写在这下面 因为js代码的代码是按照从上往下执行的 如果写在上面的话 可能js的代码获取的一些标签在下面还没有生效 会获取失败 -->
<script>
    function mysub(){
        // 1.非空判断
        // 1.1 先得到输入的组件
        var username = jQuery("#username");
        var password = jQuery("#password");
        var password2 = jQuery("#password2");
        // 1.2 判断输入组件是否为空
        if(username.val().trim()==""){
            alert("请先输入用户名!");
            username.focus(); // 聚焦光标
            return false;
        }
        if(password.val().trim()==""){
            alert("请先输入密码!");
            password.focus();
            return false;
        }
        if(password2.val().trim()==""){
            alert("请先输入确认密码!");
            password2.focus();
            return false;
        }
        if(password.val()!=password2.val()){
            alert("两次密码输入不一致性,请先检查!");
            password.focus();
            return false;
        }
        // 2.先把提交按钮设置成不可用(禁用)
        jQuery("#submit").attr("disabled","disabled");
        // 3.将当前页面的数据提交给后端
        jQuery.ajax({
            url:"/user/reg",
            type:"POST",
            data:{
                "username":username.val().trim(),
                "password":password.val().trim()
            },
            success:function(res){
                // 4.根据后端返回的结果(成功or失败)再处理后续流程
                if(res.code==200 && res.data==1){
                    alert("注册成功!");
                    location.href = "login.html"; // 调整到登录页面
                }else{
                    alert("抱歉:操作失败!"+res.msg);
                    // 取消禁用
                    jQuery("#submit").removeAttr("disabled");
                }
            }
        });
    }
</script>
</body>

</html>

前端给后端发送请求都是通过Ajax来实现的,所以对于任何需要发送Ajax请求的页面都需要导入js的依赖(后面就不再赘述)

后端(这里的后端只展示controller包中的代码)

java 复制代码
@RequestMapping("/reg")
    public AjaxResult reg(UserInfo userInfo) {
        //1.对前端传递来的参数进行校验
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) || !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(-1, "参数有误!");
        }
        //2.与数据库进行交互实现注册的功能
        //将密码进行加盐加密
        userInfo.setPassword(PasswordTools.encrypt(userInfo.getPassword()));
        int result = userService.reg(userInfo);
        //3.对于查询结果给前端进行反馈
        return AjaxResult.success(result);
    }

3, 登录功能

登录就是前端给后端传递用户名和密码,后端从数据库中查询是否存在这样的用户!

前端

java 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登陆页面</title>
    <link rel="stylesheet" href="css/conmmon.css">
    <link rel="stylesheet" href="css/login.css">
    <script src="js/jquery.min.js"></script>
</head>

<body>
<!-- 导航栏 -->
<div class="nav">
    <img src="img/logo2.jpg" alt="">
    <span class="title">我的博客系统</span>
    <!-- 用来占据中间位置 -->
    <span class="spacer"></span>
    <a href="blog_list.html">主页</a>
    <a href="reg.html">注册</a>
</div>
<!-- 版心 -->
<div class="login-container">
    <!-- 中间的登陆框 -->
    <div class="login-dialog">
        <h3>登陆</h3>
        <div class="row">
            <span>用户名</span>
            <input type="text" id="username">
        </div>
        <div class="row">
            <span>密码</span>
            <input type="password" id="password">
        </div>
        <div class="row">
            <button id="submit" onclick="mysub()">提交</button>
        </div>
    </div>
</div>

<script>
    function mysub() {
        //1.对提交的数据进行判空操作
        var username = jQuery("#username");
        var password = jQuery("#password");
        if(username.val().trim() == "") {
            alert("请输入用户名!");
            username.focus();
            return false;
        }
        if(password.val().trim() == "") {
            alert("请输入密码!");
            password.focus();
            return false;
        }

        //2.发送数据给服务器
        jQuery.ajax({
            url:"user/login",
            type:"post",
            data:{
                "username":username.val().trim(),
                "password":password.val().trim()
            },
            success:function(res) {
                //这里针对服务器的响应数据 规定返回1是成功 返回0是失败
                if(res.code == 200 && res.data == 1) {
                    alert("恭喜:登录成功!");
                    location.href = "myblog_list.html";
                } else {
                    alert("抱歉:登录失败!" + res.msg);
                    return false;
                }
            }
        });
    }
</script>
</body>

</html>

登录功能的前端代码和注册功能前端代码几乎一模一样

后端

java 复制代码
@RequestMapping("/login")
    public AjaxResult login(String username, String password, HttpServletRequest request) {
        //1.对前端传递来的参数进行校验
        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
            return AjaxResult.fail(-1, "参数有误!");
        }

        //2.根据用户名去数据库中进行查询
        UserInfo userInfo = userService.login(username);
        if (userInfo == null || userInfo.getId() <= 0) {
            return AjaxResult.fail(-2, "用户名或者密码错误!");
        }

        //对数据库中查找的密码进行解密
//        if (!PasswordTools.check(password,userInfo.getPassword())) {
//            return AjaxResult.fail(-2, "用户名或者密码错误!");
//        }
        
        if (!userInfo.getPassword().equals(password)) {
            return AjaxResult.fail(-2, "用户名或者密码错误!");
        }

        //当前表示登陆成功 需要存储session
        HttpSession session = request.getSession();
        session.setAttribute(ApplicationVariable.USERINFO_SESSION_KEY, userInfo);
        return AjaxResult.success(1);
    }

登录的时候需要存储用户的session(会话)信息,因为session的Key需要在多个地方使用,我们将该属性抽象出来放在了common这个公共包下了:

java 复制代码
package com.example.blogsystem.common;

public class ApplicationVariable {

    public static final String USERINFO_SESSION_KEY = "USERINFO_SESSION_KEY";
}

对于取出session()会话)中的的用户信息也可以将其封装公共的类,放在common包下:

java 复制代码
package com.example.blogsystem.common;

import com.example.blogsystem.entity.UserInfo;

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

public class UserSessionTools {
    public static UserInfo getLoginUser(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute(ApplicationVariable.USERINFO_SESSION_KEY) != null) {
            return (UserInfo) session.getAttribute(ApplicationVariable.USERINFO_SESSION_KEY);
        }

        return null;
    }
}

4, 实现拦截器

对于一些博客信息的操作需要用户进行登录,所以可以通过拦截器判断用户具有相应的权限,只有通过拦截器的用户才可以操作,可以将拦截的配置信息放在config包下.

实现HandlerInterceptor接口:

java 复制代码
package com.example.blogsystem.config;

import com.example.blogsystem.common.ApplicationVariable;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;

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


@Configuration
public class LoginInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute(ApplicationVariable.USERINFO_SESSION_KEY) != null) {
            return true;
        }

        response.sendRedirect("/login.html");//没有通过拦截器的请求需要跳转到登录页面先登录
        return false;
    }
}

实现WebMvcConfigurer进行配置接口:

java 复制代码
package com.example.blogsystem.config;

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

import javax.annotation.Resource;

@Configuration
public class MyConfig implements WebMvcConfigurer {

    @Resource
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**") //拦截所有的url
                .excludePathPatterns("/login.html")
                .excludePathPatterns("/reg.html")
                .excludePathPatterns("/blog_list.html")
                .excludePathPatterns("/blog_content.html")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/editor.md/**")
                .excludePathPatterns("/img/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/user/reg")
                .excludePathPatterns("/user/login");
    }
}

这里需要先放开所有的静态页面,图片以及登录和注册接口.

5, 博客添加功能

博客添加功能就是前端向后端提交博客的一些信息,后端在文章表中插入一条文章记录即可!

前端

java 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文章添加</title>

    <!-- 引入自己写的样式 -->
    <link rel="stylesheet" href="css/conmmon.css">
    <link rel="stylesheet" href="css/blog_edit.css">

    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="js/jquery.min.js"></script>
    <script src="editor.md/editormd.js"></script>
</head>

<body>
    <!-- 导航栏 -->
    <div class="nav">
        <img src="img/logo2.jpg" alt="">
        <span class="title">我的博客系统</span>
        <!-- 用来占据中间位置 -->
        <span class="spacer"></span>
        <a href="blog_list.html">主页</a>
        <a href="#">注销</a>
    </div>
    <!-- 编辑框容器 -->
    <div class="blog-edit-container">
        <!-- 标题编辑区 -->
        <div class="title">
            <input id="title" type="text" placeholder="在这里写下文章标题">
            <button onclick="mysub()">发布文章</button>
        </div>
        <!-- 创建编辑器标签 -->
        <div id="editorDiv">
            <textarea id="editor-markdown" style="display:none;"></textarea>
        </div>
    </div>

    <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(){
            //1.对文章标题和内容进行判空操作
            var title = jQuery("#title");
            var content = editor.getValue();
            if(title.val().trim() == "") {
                alert("请输入文章标题!");
                title.focus();
                return false;
            }
            if(content == "") {
                alert("请输入正文!")
                return false;
            }
            //2.提交数据给后端
            jQuery.ajax({
                url:"/art/add",
                type:"post",
                data:{
                    "title":title.val(),
                    "content":content
                },
                success:function (res) {
                    //假设文章添加成功后端给前端返回的data中的数据是1
                    if(res.code == 200 && res.data == 1) {
                        alert("恭喜:文章添加成功!");
                        if (confirm("是否继续添加文章?")) {
                            //如果继续继续添加文章的话 需要刷新此页面
                            location.href = location.href;
                        } else {
                            //不继续添加文章需要跳转到文章列表页
                            location.href = "myblog_list.html";
                        }
                    } else{
                        alert("抱歉:文章添加失败!" + res.msg);
                    }
                }
            })
            // alert(editor.getValue()); // 获取值
            // editor.setValue("#123") // 设置值
        }
    </script>
</body>

</html>

这里的前端页面引入了MarkDown编辑器,所以添加博客的时候相较于其他官方博客系统更加真实,而且该编辑器提供了一些Api让我们进行格式转换的时候更加方便.

java 复制代码
//相关API
alert(editor.getValue()); // 获取值
editor.setValue("#123") // 设置值

后端

java 复制代码
@RequestMapping("/add")
    public AjaxResult add(ArticleInfo articleInfo, HttpServletRequest request) {
        //1.对前端传递来的参数进行判空操作
        if (articleInfo == null || !StringUtils.hasLength(articleInfo.getTitle()) || !StringUtils.hasLength(articleInfo.getContent())) {
            return AjaxResult.fail(-1, "参数错误!");
        }
        //2.获取当前的uid进行校验
        UserInfo userInfo = UserSessionTools.getLoginUser(request);
        if (userInfo == null || userInfo.getId() <= 0) {
            return AjaxResult.fail(-1, "参数错误!");
        }
        //3.封装uid进行持久化
        articleInfo.setUid(userInfo.getId());
        int result = articleService.add(articleInfo);
        //4.给前端进行数据反馈
        return AjaxResult.success(result);
    }

这里数据库存储的是MarkDown格式的数据,是为了方便进行修改的时候直接进行修改省去了一次从Html转换成MarkDown格式的操作.

6, 博客编辑功能

博客编辑功能需要实现两个操作:

1.先去查询当前文章的信息进行展示(页面加载的时候进行调用)

2.提交修改操作(触发提交按钮的时候进行调用)

前端

java 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文章修改</title>

    <!-- 引入自己写的样式 -->
    <link rel="stylesheet" href="css/conmmon.css">
    <link rel="stylesheet" href="css/blog_edit.css">

    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css"/>
    <script src="js/jquery.min.js"></script>
    <script src="editor.md/editormd.js"></script>
    <script src="js/urltools.js"></script>
    <script src="js/logout.js"></script>
</head>

<body>
<!-- 导航栏 -->
<div class="nav">
    <img src="img/logo2.jpg" alt="">
    <span class="title">我的博客系统</span>
    <!-- 用来占据中间位置 -->
    <span class="spacer"></span>
    <a href="blog_list.html">主页</a>
    <a href="javascript:logout()">注销</a>
</div>
<!-- 编辑框容器 -->
<div class="blog-edit-container">
    <!-- 标题编辑区 -->
    <div class="title">
        <input id="title" type="text" placeholder="在这里写下文章标题">
        <button onclick="mysub()">发布文章</button>
    </div>
    <!-- 创建编辑器标签 -->
    <div id="editorDiv">
        <textarea id="editor-markdown" style="display:none;"></textarea>
    </div>
</div>

<script>
    var isSubmit = 1;
    var id = 0;
    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("# 在这里写下一篇博客"); // 初始化编译器的值
    //接口1:先去查询当前文章的信息进行展示 页面加载的时候进行调用
    function initArt() {
        //1.通过queryString获取文章id
        id = getParamByKey("id");
        if (id == null || id <= 0) {
            isSubmit = 0;
            alert("抱歉:非法参数!");
            return false;
        }
        //2.向后端发送请求 获取对应id的文章并展现在前端页面
        jQuery.ajax({
            url: "/art/getdetailbyid",
            type: "post",
            data: {
                "id": id
            },
            success: function (res) {
                if (res.code == 200 && res.data != null && res.data.id > 0) {
                    //文章查询成功
                    jQuery("#title").val(res.data.title);
                    initEdit(res.data.content);
                } else {
                    //文章获取失败
                    isSubmit = 0;
                    alert("抱歉:非法参数!" + res.msg);
                }
            }
        });
    }

    initArt();

    //接口2:提交修改操作 触发提交按钮的时候进行调用
    // 提交
    function mysub() {
        if (isSubmit == 0) {
            alert("抱歉:非法操作,请刷新页面再试!");
            return false;
        }
        //1.非空判断
        var title = jQuery("#title");
        var content = editor.getValue();
        if (title.val().trim() == "") {
            alert("请输入文章标题!");
            title.focus();
            return false;
        }
        if (content == "") {
            alert("请输入正文!")
            return false;
        }

        //2.提交请求给后端
        jQuery.ajax({
            url: "/art/update",
            type: "post",
            data: {
                "id": id,
                "title": title.val(),
                "content": content
            },
            success: function (res) {
                //规定修改成功后端返回1
                if (res.code == 200 && res.data == 1) {
                    alert("恭喜:修改成功!");
                    location.href = "myblog_list.html";
                } else {
                    alert("抱歉:非法参数!" + res.msg);
                }
            }
        });
        // alert(editor.getValue()); // 获取值
        // editor.setValue("#123") // 设置值
    }
</script>
</body>

</html>

后端

java 复制代码
/**
     * 对文章进行修改的时候也需要对拿到文章进行权限验证 拿到的文章的uid必须和登录的用户的id一致
     * 防止登录的用户对其他人的文章进行篡改
     */

    @RequestMapping("/getdetailbyid")
    public AjaxResult getdetailbyid(Integer id, HttpServletRequest request) {
        //1.对id进行判空操作
        if (id == null || id <= 0) {
            return AjaxResult.fail(-1, "参数错误!");
        }

        //2.获取到登录用户的id
        UserInfo userInfo = UserSessionTools.getLoginUser(request);
        if (userInfo == null || userInfo.getId() <= 0) {
            return AjaxResult.fail(-1, "参数错误!");
        }

        //3.封装id和uid进行持久化操作
        return AjaxResult.success(articleService.getDetailByIdAndUid(id, userInfo.getId()));
    }

    @RequestMapping("/update")
    public AjaxResult update(ArticleInfo articleInfo, HttpServletRequest request) {
        //1.对前端传递的参数进行判空
        if (articleInfo == null || articleInfo.getId() <= 0
                || !StringUtils.hasLength(articleInfo.getTitle()) || !StringUtils.hasLength(articleInfo.getContent())) {
            return AjaxResult.fail(-1, "参数错误!");
        }
        //2.获取uid进行封装并进行持久化
        UserInfo userInfo = UserSessionTools.getLoginUser(request);
        if (userInfo == null || userInfo.getId() <= 0) {
            return AjaxResult.fail(-1, "参数错误!");
        }
        articleInfo.setUid(userInfo.getId());
        articleInfo.setUpdatetime(LocalDateTime.now());
        //3.给前端返回数据
        int result = articleService.update(articleInfo);
        return AjaxResult.success(result);
    }

根据文章id进行查找文章时,必须要进行校验,确保查询到的文章时该登录用户的文章,即文章表中的uid = 用户表中的id.

相关推荐
wn53129 分钟前
【Go - 类型断言】
服务器·开发语言·后端·golang
bjzhang7540 分钟前
SpringBoot开发——集成Tess4j实现OCR图像文字识别
spring boot·ocr·tess4j
flying jiang1 小时前
Spring Boot 入门面试五道题
spring boot
小菜yh1 小时前
关于Redis
java·数据库·spring boot·redis·spring·缓存
ggdpzhk1 小时前
Mybatis 快速入门(maven)
oracle·maven·mybatis
希冀1231 小时前
【操作系统】1.2操作系统的发展与分类
后端
GoppViper1 小时前
golang学习笔记29——golang 中如何将 GitHub 最新提交的版本设置为 v1.0.0
笔记·git·后端·学习·golang·github·源代码管理
爱上语文2 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
荆州克莱2 小时前
springcloud整合nacos、sentinal、springcloud-gateway,springboot security、oauth2总结
spring boot·spring·spring cloud·css3·技术
serve the people2 小时前
springboot 单独新建一个文件实时写数据,当文件大于100M时按照日期时间做文件名进行归档
java·spring boot·后端