相关技术
Spring + Spring Boot + Spring MVC + MyBatis
Html + Css + JS
pom 文件我就不放出来了, 之前用的 jdk8 做的, MySQL 用的 5.7, 都有点老了, 你们自己看着配版本就好
实现功能
- 用户注册 - 密码加盐加密 (md5 加密)
- 前后端用户信息存储 - 令牌技术
- 用户登录 - (使用 拦截器 做登录校验)
- 博客的增删改查
- 后端数据返回前端, 采用 SpringBoot 做统一功能处理和统一异常处理
数据库设计
- 用户表
- 博客表
前端页面
博客登录页 (blog_login.html)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <me_ta 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/common.css"> <link rel="stylesheet" href="css/login.css"> </head> <body> <div class="nav"> <img src="pic/logo2.jpg" alt=""> <span class="blog-title">我的博客系统</span> <div class="space"></div> <a class="nav-span" href="blog_list.html">主页</a> <a class="nav-span" href="blog_edit.html">写博客</a> </div> <div class="container-login"> <div class="login-dialog"> <h3>登陆</h3> <div class="row"> <span>用户名</span> <input type="text" name="username" id="username"> </div> <div class="row"> <span>密码</span> <input type="password" name="password" id="password"> </div> <div class="row"> <button id="submit" onclick="login()">提交</button> </div> </div> </div> <script src="js/jquery.min.js"></script> <script> function login() { // 发送 ajax 请求, 获取 token $.ajax({ type: "post", url: "/user/login", data: { "userName": $("#username").val(), "password": $("#password").val() }, success: function(result) { if(result.code == 200 && result.data != null) { // 存储 token 到本地 localStorage.setItem("user_token", result.data); location.href = "blog_list.html"; }else{ alert("用户名或密码错误"); } } }); } </script> </body> </html>
用户登录成功之后, 会将用户信息, 生成令牌, 存储到 request 中, 前后端都能从中获取当前登录用户的信息
博客列表页 (blog_list.html)
<!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/common.css"> <link rel="stylesheet" href="css/list.css"> </head> <body> <div class="nav"> <img src="pic/logo2.jpg" alt=""> <span class="blog-title">我的博客系统</span> <div class="space"></div> <a class="nav-span" href="blog_list.html">主页</a> <a class="nav-span" href="blog_edit.html">写博客</a> <a class="nav-span" href="#" onclick="logout()">注销</a> </div> <div class="container"> <div class="left"> <div class="card"> <img src="pic/doge.jpg" alt=""> <h3></h3> <a href="#"></a> <div class="row"> <span>文章</span> <span>分类</span> </div> <div class="row"> <span>2</span> <span>1</span> </div> </div> </div> <div class="right"> </div> </div> <script src="js/jquery.min.js"></script> <script src="js/common.js"></script> <script> //显示用户信息 var userUrl = "/user/getUserInfo"; getUserInfo(userUrl); // 获取所有的博客信息 $.ajax({ type: "get", url: "/blog/getList", success: function(result) { console.log("result:" + result); if(result.code == 200 && result.data != null) { var finalHtml = ""; for(var blog of result.data) { finalHtml += '<div class="blog">'; finalHtml += '<div class="title">'+blog.title+'</div>'; finalHtml += '<div class="date">'+blog.createTime+'</div>'; finalHtml += '<div class="desc">'+blog.content+'</div>'; finalHtml += '<a class="detail" href="blog_detail.html?blogId='+blog.id+'">查看全文>></a>'; finalHtml += '</div>'; } $(".right").html(finalHtml); } }, error: function(error) { console.log("error:" + error); location.href = "blog_login.html"; if(error != null && error.state == 401) { location.href = "blog_login.html"; } } }); </script> </body> </html>
当前页面会自动调用一个 ajax 请求, 用以获取数据库中 所有未删除博客 的信息进行展示 (博客正文会裁取前100字进行显示)
博客详情页 (blog_detail.html)
<!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/common.css"> <link rel="stylesheet" href="css/detail.css"> </head> <body> <div class="nav"> <img src="pic/logo2.jpg" alt=""> <span class="blog-title">我的博客系统</span> <div class="space"></div> <a class="nav-span" href="blog_list.html">主页</a> <a class="nav-span" href="blog_edit.html">写博客</a> <a class="nav-span" href="#" onclick="logout()">注销</a> </div> <div class="container"> <div class="left"> <div class="card"> <img src="pic/doge.jpg" alt=""> <h3></h3> <a href="#"></a> <div class="row"> <span>文章</span> <span>分类</span> </div> <div class="row"> <span>2</span> <span>1</span> </div> </div> </div> <div class="right"> <div class="content"> <div class="title"></div> <div class="date"></div> <div class="detail" id="detail" style="background-color: transparent;"> </div> <!-- <div class="operating"> <button onclick="window.location.href='blog_update.html'">编辑</button> <button onclick="deleteBlog()">删除</button> </div> --> </div> </div> </div> <!-- 引入 editor.md 的依赖 --> <link rel="stylesheet" href="blog-editormd/css/editormd.css" /> <script src="js/jquery.min.js"></script> <script src="blog-editormd/lib/marked.min.js"></script> <script src="blog-editormd/lib/prettify.min.js"></script> <script src="blog-editormd/editormd.js"></script> <script src="js/common.js"></script> <script> // 获取博客详情 $.ajax({ type: "get", url: "/blog/getBlogDetail"+location.search, success: function(result) { console.log(result); if(result.code == 200 && result.data != null) { console.log("abc" + result); var blog = result.data; $(".right .content .title").text(blog.title); $(".right .content .date").text(blog.createTime); // $(".right .content .detail").text(blog.content); editormd.markdownToHTML("detail", { markdown: blog.content, }); // 是否显示 编辑/删除 按钮 if(blog.isLoginUser == true) { var html = ""; html += '<div class="operating">'; html += '<button onclick="window.location.href=\'blog_update.html'+location.search+'\'">编辑</button>'; html += '<button onclick="deleteBlog()">删除</button>'; html += '</div>'; $(".content").append(html); } } }, error: function(error) { if(error != null && error.status == 401) { location.href = "blog_list.html"; } } }); //显示博客作者信息 var userUrl = "/user/getAuthorInfo" + location.search; getUserInfo(userUrl); function deleteBlog() { $.ajax({ type: "post", url: "/blog/delete" + location.search, success: function(result) { if(result.code == 200 && result.data != null && result.data == true) { location.href = "blog_list.html"; } } }); } </script> </body> </html>
对于每篇博客, 会显示博客信息 (标题, 最后一次的修改时间, 博客正文), 和博客作者的信息 (用户名) (TODO: 作者头像, 作者的总文章数量, 博客的分类所属)
页面会自动校验登录用户是否为当前博客的作者, 如果是, 那么可以对当前博客进行编辑和删除, 如果不是, 这两个按钮不会显示
博客编辑页 (blog_edit.html)
<!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/common.css"> <link rel="stylesheet" href="css/edit.css"> <link rel="stylesheet" href="blog-editormd/css/editormd.css" /> </head> <body> <div class="nav"> <img src="pic/logo2.jpg" alt=""> <span class="blog-title">我的博客系统</span> <div class="space"></div> <a class="nav-span" href="blog_list.html">主页</a> <a class="nav-span" href="blog_edit.html">写博客</a> <a class="nav-span" href="#" onclick="logout()">注销</a> </div> <div class="content-edit"> <div class="push"> <input type="text" name="" id="title"> <input type="button" value="发布文章" id="submit" onclick="submit()"> </div> <!-- markdown 插件 html代码 --> <div id="editor"> <textarea style="display:none;" id="content" name="content">##在这里写下一篇博客</textarea> </div> </div> <script src="js/jquery.min.js"></script> <script src="blog-editormd/editormd.min.js"></script> <script src="js/common.js"></script> <script type="text/javascript"> $(function () { var editor = editormd("editor", { width: "100%", height: "550px", path: "blog-editormd/lib/" }); }); function submit() { $.ajax({ type: "post", url: "/blog/add", data: { title: $("#title").val(), content: $("#content").val() }, success: function(result) { if(result.code == 200 && result.data != null && result.data == true) { location.href = "blog_list.html"; }else { alert("博客发布失败!"); } } }); } </script> </body> </html>
博客编辑页使用了 gittee 上的一个开源 markdown 组件
对于未有博客的 “写博客” , 调用的是 “插入操作”
对于已有博客的 “编辑博客” , 调用的是 “更新操作”
“删除博客” 操作是逻辑删除, 即修改数据库的某一字段, 所以调用的也是 “更新操作”
前端页面共同的 js (common.js)
$(document).ajaxSend(function(e, xhr, opt) { // 获取本地存储中的 token var user_token = localStorage.getItem("user_token"); // 将 token 设置到每个 ajax 请求的 header 中 xhr.setRequestHeader("user_token_header", user_token); }); // 获取用户信息 function getUserInfo(url) { $.ajax({ type: "post", url: url, success: function(result) { if(result.code == 200 && result.data != null) { $(".left .card h3").text(result.data.userName); $(".left .card a").attr("href", result.data.githubUrl); } } }); } // 用户退出 function logout() { localStorage.removeItem("user_token"); location.href = "blog_login.html"; }
主要就是获取当前登录用户的信息, 以及退出登录的逻辑
后端代码
项目的基本框架
实体类
BlogInfo
@Data public class BlogInfo { private Integer id; private String title; private String content; private Integer userId; private Integer deleteFlag; private Date createTime; private Date updateTime; private Boolean isLoginUser = false; // 返回 String 类型的数据 (BlogInfo 里面存储的是 Date 类型数据) public String getCreateTime() { return DateUtils.formateDate(createTime); } public String getUpdateTime() { return DateUtils.formateDate(updateTime); } }
对应数据的 blog 表
UserInfo
@Data public class UserInfo { private Integer id; private String userName; private String password; private String githubUrl; private Integer deleteFlag; private Date createTime; private Date updateTime; }
对应数据的 user 表
Result
@Data public class Result { private int code; //200成功 -1失败 -2未登录 private String errMsg; private Object data; public static Result success(Object data) { Result result = new Result(); result.setCode(Constant.SUCCESS_CODE); result.setErrMsg(""); result.setData(data); return result; } public static Result fail(String errMsg) { Result result = new Result(); result.setCode(Constant.FAIL_CODE); result.setErrMsg(errMsg); result.setData(null); return result; } public static Result fail(String errMsg, Object data) { Result result = new Result(); result.setCode(Constant.FAIL_CODE); result.setErrMsg(errMsg); result.setData(data); return result; } public static Result unlogin() { Result result = new Result(); result.setCode(Constant.FAIL_CODE); result.setErrMsg("用户未登录"); result.setData(null); return result; } public static Result unlogin(String errMsg) { Result result = new Result(); result.setCode(Constant.UNLOGIN_CODE); result.setErrMsg("用户未登录"); result.setData(null); return result; } }
用于统一数据格式返回 (不知道可以看一下我的另一篇博客 Spring Boot统一功能处理(拦截器, 统一数据返回格式, 统一异常处理) )
Constant 类 (常量值存储)
public class Constant { public final static Integer SUCCESS_CODE = 200; public final static Integer FAIL_CODE = -1; public final static Integer UNLOGIN_CODE = -2; public final static String USER_TOKEN_HEADER = "user_token_header"; public final static String USER_CLAIM_ID = "id"; public final static String USER_CLAIM_NAME = "name"; }
从 0 开始实现一个博客系统 (SSM 项目)(下):https://developer.aliyun.com/article/1520796