完整代码 gitee:优今夏/项目仓库
项目介绍
使用SSM框架实现一个简单的博客系统
共5个页面
- 用户登录
- 博客发表页
- 博客编辑页
- 博客列表页
- 博客详情页
功能描述
用户登录成功后,可以查看所有人的博客,点击<<查看全文>>可以查看该博客的正文内容,如果该博客作者为当前登录用户,可以完成博客的修改和删除操作,以及发表新博客
页面预览
用户登录

博客列表页

博客详情页

博客发表/修改页

1. 项目公共模块
项目分为控制层(Controller),服务层(Service),持久层(Mapper)各层调用关系如下

1.1 统一返回结果实体类



1.2 统一返回结果

1.3 定义项目异常

1.4 统一异常处理

数据准备


2. 业务代码
2.1 持久层


Mapper


2.2 实现博客列表
约定前后端接口
java
[请求]
/blog/getList GET
[响应]
{
"code": 200,
"errMsg": null,
"data": [{
"id": 1,
"title": "第⼀篇博客",
"content": "111我是博客正⽂我是博客正⽂我是博客正⽂",
"userId": 1,
"updateTime": "2024-08-22 11:27:03"
},
.....
]
}
实现服务器代码
1.定义接口返回实体
日期的返回格式有两种方法实现



2.实现Controller

- 实现Service

实现客户端代码

测试

2.3 实现博客详细
约定前后端接口
java
[请求]
/blog/getBlogDetail?blogId=1
[响应]
{
"code": 200,
"errMsg": null,
"data": {
"id": 1,
"title": "第⼀篇博客",
"content": "111我是博客正⽂我是博客正⽂我是博客正⽂",
"userId": 1,
"updateTime": "2024-08-22 11:27:03"
}
}
实现服务器代码
参数校验: jakarta。validation
这个接口中,blogId不能为空,可以借助校验. @NotNull , jakarta.validation 帮我们完成参数校验,免去繁琐的串 javax.validation 是JavaBeanValidationAPI的包名,这个API允许开发者通过注解 (如 @NotBlank , @Null 等)来声明对象的验证规则,然后在运行时自动验证这些对象.

java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
将 BlogInfo 转为 BlogInfoResponse 的操作封装起来



实现客户端代码

测试

2.4 实现登录
分析
传统思路:
- 登录页面把用户名和密码提交给服务器
- 服务器端验证用户密码是否正确,并返回校验结果给后端
- 如果密码正确,则在服务器端创建 Session ,通过Cookie 把sessionId 返回给浏览器
问题:
- 服务器重启,Session丢失
- Session 存储在内存中(耗费服务器资源)
- 集群环境下无法直接使用Session
原因分析:
开发项目,在企业中很少会部署在一台机器上,容易发生单点故障(单点故障:一旦这台服务器挂了,整个应用就无法访问了)所以通常情况下,一个Web应用会部署在多个服务器上,通过Nginx等进行负载均衡。此时,来自同一个用户的请求会被分发到不同的服务器上


此时需要另一种方案:令牌技术
令牌是一个用户身份的标识,本质是一个字符串

优点:
- 解决了集群环境下的认证问题
- 减轻了服务器的存储压力
JWT令牌
令牌本质就是一个字符串,他的实现方式有很多,我们采用一个JWT令牌来实现


JWT令牌生成和校验
- 引入依赖
java
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<!-- or jjwt-gson if Gson is preferred -->
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
- 使用Jar包中提供的API来完成JWT令牌的生成和校验



约定前后端接口
java
[
请求]
/user/login
[参数]
{
"userName": "test",
"password": "123456"
}
[响应]
{
"code": 200,
"errMsg": null,
"data": {
"userId": 1,
"token":
"eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiemhhbmdzYW4iLCJpZCI6MSwiaWF0IjoxNzI0OTE5NjgyL
CJleHAiOjE3MjQ5MjE0ODJ9.5hwKlAh2jPPBNn3uPja4JTGguZNB3QrpRoPqCep7qME"
}
}
//验证成功, 返回token, 验证失败返回 ""
实现服务器代码
创建JWT工具类:可以生成token和解析

创建请求和响应实体类


参数不合法会抛出异常:
可以单独获取想要的异常信息(原本的信息太长)


实现Controlled

实现Service

实现客户端代码


java
//存储数据
localStorage.setItem("user_token","value");
//读取数据
localStorage.getItem("user_token");
//删除数据
localStorage.removeItem("user_token");



2.5 实现强制要求登录
当用户访问博客列表页和博客详情页时,如果用户当前尚未登录,就自动跳转到登录页面
可以采用拦截器来完成,token通常有前端放在header中,从header中获取token,并校验token是否合法
添加拦截器
ERROR com.example.springblog.common.util.JwtUtils -- token解析失败,token:eyJhbGciOiJIUzI1NiJ9.ehyJuYW1lIjoiemhhbmdzYW4iLCJpZCI6MX0.8GHwbRNe44MuWq-UteQFqZ_-oIc_649gBovAn56XSeE null
解析失败会返回null



实现客户端代码
浏览器不会自动把 localStorage 中的 token 放到请求头里,必须由前端代码显式实现
前端请求时,header中统一添加token,后端返回异常时,跳转登录页面,代码逻辑卸载common。js中

2.6 实现显示用户信息
用户信息可以随着用户登录而发生改变
- 如果当前页面是博客列表页,则显示当前登录用户的信息
2.如果当前页面是博客详细页,则显示该博客的作者用户的信息
由于前端代码能力有限,改为在后端处理

约定前后端交互接口
在博客列表页,获取当前登录用户的用户信息
java
[请求]
/user/getUserInfo?userId=1
[响应]
{
"id": 1,
"username": "test",
"githubUrl":"bite.gitee.com"
}
在博客详情页,获取当前文章作者的用户信息
java
[请求]
/user/getAuthorInfo?blogId=1
[响应]
{
"id": 1,
"username": "test",
"githubUrl":"bite.gitee.com"
}
实现服务器代码
定义返回接口实体类



服务层可以相互调用



实现客户端代码



2.7 实现用户退出
前端直接清楚token即可
实现客户端代码

2.8 实现发布博客
约定前后端交互
java
[请求]
/blog/add
[参数]
{
"userId":1,
"title":"标题",
"content":"正⽂"
}
[响应]
{
"code": 200,
"msg": "",
"data": true
}
//true 成功
//false 失败
实现服务器代码



客户端代码
editor.md简单介绍 Editor.md - 开源在线 Markdown 编辑器
是一个开源的页面,markdown编辑组件


配置使得html生效

设置背景等细节

2.9 实现删除/编辑博客
进入用户详情页,如果当前登录用户正式文章作者,则在导航栏中显示【编辑】【删除】按钮,用户点击时则进行相应的处理
需要实现两件事:
- 判定当前博客详情页是否要显示【删除】【编辑】按钮
2.实现编辑/删除逻辑(采用逻辑删除)
约定前后端接口
- 修改博客
java
[请求]
/blog/update
[参数]
Content-Type: application/json
{
"id": "4",
"title": "测试修改⽂章",
"content": "在这⾥写下⼀篇博客"
}
[响应]
{
"code": 200,
"msg": "",
"data": true
}
- 删除博客
java
[请求]
/blog/delete?blogId=1
[响应]
{
"code": 200,
"msg": "",
"data": true
}
实现服务器代码
定义修改接口实体类

、

实现客户端代码
blog_detail.html


blog_update.html

测试
以zhangsan的身份登录


3.10 加密/加盐
加密介绍
在MySQL数据库中,需要对密码,身份证号,手机号等敏感信息进行加密,以保证数据的安全性,如果使用明文储存,当黑客入侵数据库时,就可以轻松获取到用户的相关信息,从而对用户或者企业造成信息泄露或者财产损失
目前我们用户的密码还是明文设置,为了保护用户的密码信息,我们需要对密码进行加密
密码算法分类
密码算法主要分为三类:对称密码算法,非对称密码算法,摘要算法


加密思路
博客系统中我们采用MD5算法来进行加密


测试:


实现服务器代码
由于该博客系统没有注册功能,只介绍登录场景

修改接口

修改数据库中的密码


