登录博客:博客登陆页
我的论坛系统:学生论坛 - 用户登录 论坛功能会断丰富,并接入AI,拭目以待!
1. 运用的框架和依赖
Spring Boot、Spring MVC、Mybatis-plus、lombok、JWT
2. 运用的主要技术
编程web接口、统一异常处理、统一数据返回、拦截器、Spring IOC 和 Spring DI 、Slf4j(lombok)、JWT令牌技术、Md5加密、加盐
3.项目分层
- 主体是"三层架构",即表现层 、业务逻辑层 、数据层。
- 主体下还有,配置层 、公共层 、实体层。
4.实现的功能
前端、后端和数据库三部分联动;前端发送合法请求,通过拦截器到达接口;发送非法请求则被拦截。
- 用户登录,输入账号和密码,后端获取数据库数据进行校验,校验通过则生成Token,并返回含有Token和登录用户Id的响应。
- 强制登录,(排除拦截 登录接口 和 前端页面)拦截器判断请求中是否包含登录后设定的Token。没有Token,响应状态码就设定为401,前端收到401响应,则跳转到登录页面
- 获取博客列表,把数据库中所有的博客文章返回给前端。(别人写的博客也一并显示罗列)
- 获取博客详情,从数据库单独获取一篇博客返回给前端,如果登录用户和博客作者是同一个人则显示 "编辑" 和 "删除"。
- 添加博客,前端把作者Id、标题和博客内容数据发送给后端,后端使用SQL存入MySQL数据库。
- 修改博客,后端接收博客Id、标题和内容数据,通过SQL修改数据库数据。
- 删除博客,后端接收一个id,SQL根据id逻辑删除博客。
- 获取用户信息,博客列表页获取和显示登录用户信息,信息包含用户id、用户名和用户给GitHub地址。
- 获取作者信息,博客详情页显示博客作者信息,获取和显示的信息与"获取用户信息"接口返回信息类型的一致。
5.数据库设计
- 根据功能分析,项目主要围绕"用户 "和"博客 "两个名词,因此建立数据库blog_spring,数据库中创建用户表user_info和博客表blog_info。
- 客观分析,用户表最基本应有字段:1.id、2.用户名、3.登录密码、4.用户注销状态、5.用户信息创建时间、6.用户信息修改时间,除此之外,可适当添加字段丰富功能,我还添加了7."github地址字段"。(后续表中字段不足以支持功能实现,可以再添加)
|-----------------------|---------------------------|
| id | primary key |
| user_name(用户名) | not null |
| password(密码) | not null |
| github_url | 可为空 |
| delete_flag(用户注销状态) | default 0(0-未注销 1-注销) |
| create_time(用户信息创建时间) | CURRENT_TIMESTAMP(自动生成时间) |
| update_time(用户信息修改时间) | CURRENT_TIMESTAMP(自动生成时间) |
[用户表设计]
- 客观分析,博客表最基本应有字段:1.id、2.标题、3.正文内容、4.所属作者、5.博客删除状态、6.博客信息创建时间、7.博客信息修改时间。
|-----------------------|---------------------------|
| id | primary key |
| title(标题) | 可为空 |
| content(正文内容) | 可为空 |
| user_id(作者id) | 可为空 |
| delete_flag(博客删除状态) | default 0(0-未删除 1-删除) |
| create_time(博客信息创建时间) | CURRENT_TIMESTAMP(自动生成时间) |
| update_time(博客信息修改时间) | CURRENT_TIMESTAMP(自动生成时间) |
[博客表]
(解释,博客表的title、content和user_id明显不能为空,但我的数据库设计还是null,是因为我明确我在后端代码一定会对这三个属性进行非空校验。当然设置为 not null,也没问题)
6. 博客系统UML图展示
这个UML图是正常进行登录后,获取了Token的前提下,正常访问的流程。
没有正常登录获取不到Token,在访问后端接口时被拦截器拦截。
Flowchart Maker & Online Diagram Software
7.Java后端技术难点
7.1 加盐
计算机中的"加盐",这个"盐"指的是盐值。 盐值也就是一串随机生成的字符串。
"加盐"指的是,在用户自定义的密码基础上随机添加一串字符串,让用户的密码变得健壮一点,防止某些用户(如老人,小孩)因密码过于简单而被破解。加盐后的密码会进行加密,然后再存储到数据库中。加密再放入数据库,是为了防止被别人脱库后直接看到用户信息。
盐值的生成方式有多种,但只要保证每次生成的盐是随机的就行。
我使用Java原生的UUID生成盐值:
java
import java.util.UUID;
public static String encryption(String password){
//随机生成一个盐值
String salt = UUID.randomUUID().toString().replace("-","");
//加密--》(salt+password),下面这一条语句是加密
String sqlPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));
return salt+sqlPassword;
}
UUID生成的是带四个"-"的36位字符(如,92f43d09-eec1-4e47-94e7-41f37eff45a0),因此使用replace("-","")把-换成"",相当于剔除了某些字符变成32位十六进制的字符。
7.2 摘要算法
密码算法分为三种,1."对称密码算法",2."非对称密码算法",3."摘要算法"。
常见的摘要算法有:MD5,SHA系列(SHA1, SHA2等), CRC(CRC8, CRC16,CRC32)
摘要算法是不可逆的,也就是加密后不能解密回去(MD5最好不要用在高安全环境),而对称和非对称密码算法可加密/解密。
7.2.1 MD5算法
博客系统采用MD5加密密码(用户密码+salt(盐)),加密后的明文,变成密文。
实现MD5的API有很多,像Spring、Hutool等都封装了MD5算法。
MD5加密后输出的密文存在两种,第一种:输出16位十六进制的字符;第二种:输出32位十六进制的字符。(其中,每一种又有大小写的版本,不过大小写之分不重要)
7.3 如何校验用户输入的密码
7.3.1 密码存入数据库
此密码 ,非彼用户输入的密码(设用户密码为inputPassword),而是加密(inputPassword+salt)后得到的密文,密文再拼接salt存入数据库。
由于密文和盐值都是一堆字符,别人分不清哪堆是盐,哪堆是密码加密后的密文,所以开发人员随意把它们两任意拼接 后存入一个字段(我的拼接方式是直接:salt+密文)。加密后放入数据库,是为了防止被别人脱库后看到用户信息。
任意拼接的意思:

7.3.2 校验用户输入的密码是否正确
因为摘要算法不能解密,因此我们把输入的密码和数据库中存的密码进行比较,就能知道是否正确。当然用户输入的密码是明文,数据库存的是密文,直接校验肯定失败。开发人员肯定知道密文和salt的拼接规则。这时只要根据规则,就能从数据库中的密码解出salt。
这时加密(inputPassword+salt),得到密文,(salt+密文).equals(数据库密码)=》返回true 密码正确,返回false 密码错误。
注意:(salt+密文).equals(...)中的salt+密文的拼接方式必须和存入数据库时的拼接方式一样。
7.4 JWT令牌技术
7.4.1介绍JWT令牌
令牌本质就是字符串,实现它的方式有很多,JWT就是其中之一。
JWT全称:JSON Web Token.
JWT组成:
- Header (头部)包含令牌类型(即JWT),及使用的哈希算法(如HMAC SHA256或RSA)。
- Payload(负载)包含自定义的有效信息,比如{"userName":"123","userAge":"12"},也含有时间信息等。
- Signature(签名)此部分用于防止JWT被篡改,确保安全性。
下图左侧是JWT令牌,右侧是JWT解码后的数据信息:
右边三部分也就是"头部"、"负载"和"签名",是左边解码后得出来的。相应的,左边的JWT是右边三部分编码后等到的。JWT令牌被两个"."分隔开,每个部分颜色都不同,第一部分解码后对应header;第二部分的紫色解码对应payload;需要注意的是,第三部分是通过自己设定256位的密码,再配合上前面两部分的base64编码,经过自己选定的签名算法加密得出来的值,值再经过base64编码后得出来的。
文字看的一头雾水的可直接看下图:

7.4.2 JWT令牌的用处
我们知道Cookie中存储SeesionId是为了,保证用户的隐私(session)只被自己使用和方便访问多个页面。
JWT也类似,客户端在登录时输入用户名和密码后,服务端则会返回一个独有的JWT。之后用户每次发送的请求都会带上JWT,服务端只有校验JWT内容没被修改,且信息正确才会进行后续操作。操作图如下:

java
@Test
void genToken(){
//要输入>=256位的数据生成,自定义key,但"123456"小于256位会导致报错
// Key key = Keys.hmacShaKeyFor("123456".getBytes(StandardCharsets.UTF_8));
//用HS256签名算法,随机生成一个密钥
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
//可使用Map作为令牌载荷
Map<String,Object> map = new HashMap<>();
map.put("name","哈利波特");
map.put("age",15);
//生成令牌
String compact = Jwts.builder()
.setClaims(map)
//签名使用密钥生成,一种密钥生成一种签名字符串
.signWith(key)
.compact();
System.out.println(compact);
//传入key
JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();
/*
验证令牌:使用密钥校验签名的有效性。
JWT令牌的签名验证过程,本质上就是服务端使用同一个密钥重新生成一个签名,然后与令牌中携带的签名进行比对
如果签名不匹配(数据被篡改)或密钥错误,会抛出异常(如 SignatureException),表示令牌不可信。
*/
System.out.println("111"+build.parse(compact).getBody());
System.out.println("222"+build.parseClaimsJws(compact).getBody());
}
(总的来说,JWT的核心是防伪,这一点和"证书"类似。)
8.博客系统简述
- 没设计用户注册功能
- 登录用户只有两个,第一个 zhangsan 密码123456;第二个 lisi 密码 123456
- 主页面展示所有未删除博客;主页面显示登录用户信息
- 点入任何一篇博客,如果博客是登录用户写的,则显示"编辑"和"删除"
- 编辑博客,点击编辑,跳转到修改页面,页面填充标题和正文,编辑方式采用markdown
- 删除博客,点击即删除
- 写博客,采用markdown格式
- 注销,这里的注销是退出,并不是注销用户
- 主页,处于任何页面,点击主页按钮,立马跳转至主页
博客系统,是我第一个部署上云服务的项目,虽然项目很简陋,但学有所用的感觉很开心!