当前位置: 首页> 财经> 产业 > 13.博客系统-spring版本

13.博客系统-spring版本

时间:2025/7/11 7:49:38来源:https://blog.csdn.net/xinhang10/article/details/140768817 浏览次数:0次

文章目录

  • 1.博客系统项目页面展示
    • 1.1博客登录页
    • 1.2博客详情页
    • 1.3博客发布页
    • 1.4博客列表页
    • 1.5博客编辑页
  • 2.博客系统功能设计
    • 2.1SQL数据库建库建表
      • 2.1.1建库
      • 2.1.2建用户表、博客表
      • 2.1.3插入数据
      • 2.1.4sql功能实现
      • 2.1.5sql单元测试
    • 2.2目录模块搭建
      • 2.2.1controller、service、mapper三者之间的关系:
      • 2.2.2model目录下的Result实体类 和 common目录下的Constants公共实体类 code errorMsg data
      • 2.2.3config(配置类)目录下
        • 2.2.3.1ResponseAdvice.java 统一返回结果 配置类
        • 2.2.3.2ErrorAdvice.java统一异常处理 配置类
      • 2.2.4utils目录下 DateUtils.java 工具类
    • 2.3实现博客列表页
      • 2.3.1前后端交互接口
      • 2.3.2controller目录下 BlogController.java 控制层
      • 2.3.3service目录下 BlogService.java 业务层
      • 2.3.4 blog_list.html 博客列表页前端代码展示
    • 2.4实现博客详情页
      • 2.4.1前后端交互接口
      • 2.4.2controller目录下 BlogController.java 控制层
      • 2.4.3service目录下 BlogService.java 业务层
      • 2.4.4 blog_detail.html 博客详情页前端代码展示
    • 2.5实现博客登录页
      • 2.5.1传统登录存在的问题 - 使用令牌技术
        • 2.5.1.1 令牌技术
        • 2.5.1.2 JWT令牌
        • 2.5.1.3为什么要使用令牌
        • 2.5.1.4使用JWT令牌
      • 2.5.2前后端交互接口
      • 2.5.3controller目录下 UserController.java 控制层
      • 2.5.4service目录下 UserService.java 业务层
      • 2.5.5 blog_login.html 博客登录页前端代码展示
      • 2.5.6 utils下的 JwtUtils 工具类(使用JWT令牌)
      • 2.5.7 config目录下 LoginInterceptor.java类 使用拦截器校验token
      • 2.5.8config目录下 WebConfig.java类 拦截器的添加
      • 2.5.9添加使用token全流程
    • 2.6实现博客发布页
      • 2.6.1前后端交互接口
      • 2.6.2controller目录下 BlogController.java 控制层
      • 2.6.3service目录下 BlogService.java 业务层
      • 2.6.4 blog_edit.html 博客登录页前端代码展示
    • 2.7实现博客编辑页
      • 2.7.1前后端交互接口
      • 2.7.2controller目录下 BlogController.java 控制层
      • 2.7.3service目录下 BlogService.java 业务层
      • 2.7.4 blog_update.html 博客登录页前端代码展示
    • 2.8实现博客删除功能
      • 2.8.1 mapper层中的实现
      • 2.8.2controller目录下 BlogController.java 控制层
      • 2.8.3service目录下 BlogService.java 业务层
      • 2.8.4 blog_detail.html 博客登录页前端代码展示
    • 2.9实现显示用户登录信息
      • 2.9.1前后端交互接口
      • 2.9.2获取登录用户实现信息
    • 2.10实现用户退出
    • 2.11加密/加盐
      • 2.11.2盐值+密文如何获取
      • 2.11.1 UserController.java
    • 2.12 多平台配置
      • 2.12.1配置目录
      • jquary:
  • 设置日志文件的文件名

大家好,我是晓星航。今天为大家带来的是 博客系统-spring版本 相关的讲解!😀

1.博客系统项目页面展示

1.1博客登录页

image-20240404192140993

1.2博客详情页

image-20240404191954213

1.3博客发布页

image-20240404192038171

1.4博客列表页

image-20240404192116226

1.5博客编辑页

image-20240404192210288

2.博客系统功能设计

2.1SQL数据库建库建表

2.1.1建库

-- 建表SQL
create database if not exists java_blog_spring charset utf8mb4;

image-20240404194011489

2.1.2建用户表、博客表

-- 用户表
DROP TABLE IF EXISTS java_blog_spring.user;
CREATE TABLE java_blog_spring.user(
id INT NOT NULL AUTO_INCREMENT,
user_name VARCHAR(128)NOT NULL,
password VARCHAR(128)NOT NULL,
github_url VARCHAR(128)NULL,
delete_flag TINYINT(4)NULL DEFAULT 0,
create_time DATETIME DEFAULT now(),
update_time DATETIME DEFAULT now(),
PRIMARY KEY(id),
UNIQUE INDEX user_name_UNIQUE (user_name ASC ))ENGINE = INNODB DEFAULT CHARACTER SET =utf8mb4 COMMENT ='用户表';

image-20240404194249250

image-20240404194304078

-- 博客表
drop table if exists java_blog_spring.blog;
CREATE TABLE java_blog_spring.blog(
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(200) NULL,
content TEXT NULL,
user_id INT(11)NULL,
delete_flag TINYINT(4)NULL DEFAULT 0,
create_time DATETIME DEFAULT now(),
update_time DATETIME DEFAULT now(),
PRIMARY KEY(id))
ENGINE =InnoDB DEFAULT CHARSET =utf8mb4 COMMENT ='博客表';

image-20240404194343869

image-20240404194355861

2.1.3插入数据

-- 新增用户信息
insert into java_blog_spring.user (user_name,
password,github_url) values ("zhangsan","3daf6c1523cf465f97afee9f06e4e014015a4e9e8519f728e20a9cd6b2bb4123","https://gitee.com/xiao-xinghang/java-warehouse");
insert into java_blog_spring.user (user_name,
password,github_url) values ("lisi","84f37ec4949b407bbde0373dd648c26c37843422e00a0c1b764762ae77aa3144","https://gitee.com/xiao-xinghang/java-warehouse");
insert into java_blog_spring.user (user_name,
password,github_url) values ("qiqi","ee655f9484e14762ae807013e283cdcbdd43ac2bf5f5788cbecfba159f82a2d7","https://gitee.com/xiao-xinghang/java-warehouse");

image-20240411190901066

image-20240411191015344

-- 新增博客信息
insert into java_blog_spring.blog (title,content,user_id) values ("第一篇博客","111我是博客正文我是博客正文我是博客正文",1);
insert into java_blog_spring.blog (title,content,user_id) values ("第二篇博客","222我是博客正文我是博客正文我是博客正文",2);

image-20240404195453296

image-20240404195509519

2.1.4sql功能实现

image-20240404204008107

根据用户名,查询用户信息:

image-20240404204117251

image-20240404204023208

根据用户ID,查询用户信息:

image-20240404204142161

获取博客列表:

image-20240404204211603

image-20240404204029591

根据博客ID,获取博客详情:

image-20240404204351061

根据博客ID,编辑博客:

image-20240404204517702

根据博客ID, 删除博客:

image-20240404204603545

image-20240404204034641

博客添加:

image-20240404204747205

2.1.5sql单元测试

UserInfoMapper.java的单元测试:

image-20240404210413064 image-20240404210352729

BlogInfoMapper.java的单元测试:

image-20240404210531188 image-20240404210700017

2.2目录模块搭建

image-20240404201918426

2.2.1controller、service、mapper三者之间的关系:

image-20240404202219072

Controller负责接收用户请求,并通过调用Server层来处理业务逻辑,最终将结果返回给用户;Server层负责处理业务逻辑,并调用Mapper来读写数据库;Mapper负责数据的读取、写入和映射等操作。这样的架构可以使代码逻辑清晰,各层之间的职责分离,提高代码的可维护性和可扩展性。

简要理解就是:

image-20240324112329505

图解:

image-20240404203430333

2.2.2model目录下的Result实体类 和 common目录下的Constants公共实体类 code errorMsg data

定义一个Result,设置一个接口返回状态code,接口返回结果data,以及错误原因errorMsg,T为泛型。

image-20240404211535167

成功方法success 一个data参数,类型为T泛型

失败方法fail 一个参数 返回result包含 code状态,errorMsg错误信息

失败方法fail 两个参数 返回result包含 code状态,errorMsg错误信息 以及 data结果

Constants.java:

image-20240404211818407

定义一个成功返回值200,和一个失败返回值-1.

2.2.3config(配置类)目录下

2.2.3.1ResponseAdvice.java 统一返回结果 配置类
image-20240404213001721

image-20240404213121329

AOP统一处理返回结果

2.2.3.2ErrorAdvice.java统一异常处理 配置类
image-20240404213303251

2.2.4utils目录下 DateUtils.java 工具类

image-20240404220924905

该目录下代码的作用为将日期格式转化为我们希望的 2024-01-17 11:29 的格式

可根据下表中对应的日期举例,编写格式转化要求代码

image-20240404221058688

再更改一下BlogInfo.java中的get方法,我们自己写一个,便能使时间变为我们需要的格式了

image-20240404221313131

2.3实现博客列表页

2.3.1前后端交互接口

image-20240404213753574

2.3.2controller目录下 BlogController.java 控制层

获取博客列表:

image-20240404214225908

2.3.3service目录下 BlogService.java 业务层

获取博客列表:

image-20240404214311497

2.3.4 blog_list.html 博客列表页前端代码展示

image-20240404215628970

image-20240404215825048

75行,这段代码是使用 jQuery 来将名为 “finalHtml” 的 HTML 内容设置到页面中所有类名为 “right” 的元素中。

image-20240404215956173

此时 $(".right").html(finalHtml); 的作用就是把类似图示内容按照finalHtml的格式拼接起来,并以字符串的格式储存。但是图示里面是写死的代码所以只会固定展示,当我们输入新的数据时,这段代码便会自动将新数据拼接起来

2.4实现博客详情页

2.4.1前后端交互接口

image-20240404221717311

2.4.2controller目录下 BlogController.java 控制层

获取博客详情:

image-20240405125923617

2.4.3service目录下 BlogService.java 业务层

获取博客详情:

image-20240405130126302

2.4.4 blog_detail.html 博客详情页前端代码展示

最开始通过right标签写死的前端代码

image-20240405130539688

修改后,通过ajax动态输入的数据

image-20240405131028780

通过 $(".operating").html(finalHtml);

image-20240405131207444

它选择了类名为 “operating” 的元素,并将名为 “finalHtml” 的 HTML 内容设置到这些元素中,它会将 “finalHtml” 的内容设置到所有类名为 “operating” 的元素中。

2.5实现博客登录页

2.5.1传统登录存在的问题 - 使用令牌技术

传统思路:

image-20240405131628862

存在问题:
session存储在服务器内存中。所以,服务器重启, session就丢失了,用户刚登录完, 就遇到了服务器重启,用户需要重新登录(体验)

image-20240405131850567

解决方案:

image-20240405131907203

2.5.1.1 令牌技术

令牌其实就是一个用户身份的标识,名称起的很高大上,其实本质就是一个字符串

比如我们出行在外,会带着自己的身份证,需要验证身份时,就掏出身份证

身份证不能伪造,可以辨别真假.

image-20240405133712360

服务器具备生成令牌和验证令牌的能力

我们使用令牌技术,继续思考上述场景:

  1. 用户登录 用户登录请求,经过负载均衡,把请求转给了第一台服务器,第一台服务器进行账号密码验证,验证成功后,生成一个令牌,并返回给客户端.
  2. 客户端收到令牌之后,把令牌存储起来,可以存储在Cookie中,也可以存储在其他的存储空间(比如localStorage)
  3. 查询操作 用户登录成功之后,携带令牌继续执行査询操作,比如查询博客列表.此时请求转发到了第二台机器,第二台机器会先进行权限验证操作,服务器验证令牌是否有效,如果有效,就说明用户已经执行了登录操作如果令牌是无效的,就说明用户之前未执行登录操作.

令牌的优缺点

优点:

  • 解决了集群环境下的认证问题

  • 减轻服务器的存储压力(无需在服务器端存储)

缺点:

需要自己实现(包括令牌的生成,令牌的传递,令牌的校验)

当前企业开发中,解决会话跟踪使用最多的方案就是令牌技术

2.5.1.2 JWT令牌

令牌本质就是一个字符串,他的实现方式有很多,我们采用一个]WT令牌来实现.

介绍

JWT全称: JSON Web Token

官网: https://jwt.io/

JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),用于客户端和服务器之间传递安全可靠的信息.其本质是一个token,是一种紧凑的URL安全方法

JWT组成

JWT由三部分组成,每部分中间使用点(.)分隔,比如:aaaaa.bbbbb.cccc

  • Header(头部) 头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA)

  • Payload(负载) 负载部分是存放有效信息的地方,里面是一些自定义内容,比如:

["userId":"123","userName":"zhangsan"},也可以存在jt提供的现场字段,比如exp(过期时间戳)等

此部分不建议存放敏感信息,因为此部分可以解码还原原始内容,

  • Signature(签名) 此部分用于防止iwt内容被篡改,确保安全性

防止被篡改,而不是防止被解析.

JWT之所以安全,就是因为最后的签名.jwt当中任何一个字符被篡改,整个令牌都会校验失败.

就好比我们的身份证,之所以能标识一个人的身份,是因为他不能被篡改,而不是因为内容加密.(任何人都可以看到身份证的信息,jwt 也是)

image-20240405133539111

2.5.1.3为什么要使用令牌

服务多机部署,session不能用了

解决方式通常有两类:

1)把session放在一个公共的地方(机器),比如redis 服务端

2)使用token(更加常见) 客户端

token - 具有特定含义,且不能伪造的字符串

2.5.1.4使用JWT令牌

引入依赖:

		<!--		jwt依赖--><!-- 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>

在test目录下创建JwtUtilTest.java使用令牌:

package com.example.blog;import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
import org.junit.jupiter.api.Test;import javax.crypto.SecretKey;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;public class JwtUtilTest {//过期时间: 30分钟private static final long expiration = 30 * 60 * 1000;private static final String secretString = "5CRMLhF7dQnOLCNjJw8dawYK2zTUxS4jDgUW2L99Tdo=";private static final Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));//1. 生成token//2. 验证token@Testpublic void genToken(){Map<String, Object> claim  = new HashMap<>();claim.put("id",5);claim.put("name","zhangsan");String token = Jwts.builder().setClaims(claim).setExpiration(new Date(System.currentTimeMillis()+expiration)).signWith(key).compact();System.out.println(token);}@Testpublic void genKey(){//随机生成一个keySecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);String key = Encoders.BASE64.encode(secretKey.getEncoded());System.out.println(key);}@Testpublic void parseToken(){String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiemhhbmdzYW4iLCJpZCI6NSwiZXhwIjoxNzA1NjM1NjU3fQ.jK1jhAsFGWRb6ZVZm6PPoVZp_bzsTT_i2Y3zUyMyvcU";JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();Claims body = build.parseClaimsJws(token).getBody();System.out.println(body);}
}
image-20240405135718479

分析代码:

1.genToken()方法解析:

image-20240405141545121

这里的三个设置方法,和下图网页中一样,先设置id,name,再设置签名,最后生成token。

image-20240405133539111

2.使用getKey()方法随机生成token

image-20240405142504430

3.使用parseToken()方法来解析token,获取token中的信息

image-20240405143049319

image-20240405143121589

2.5.2前后端交互接口

之前流程:

image-20240405143345511

token的方式:

image-20240405143354084

image-20240405145429161

2.5.3controller目录下 UserController.java 控制层

package com.example.blog.controller;import com.example.blog.model.Result;
import com.example.blog.model.UserInfo;
import com.example.blog.service.UserService;
import com.example.blog.utils.JwtUtils;
import com.example.blog.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login")public Result login(String userName, String password){/*** 1. 参数校验* 2. 密码校验* 3. 生成token, 并返回*/if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){return Result.fail("用户名或密码为空");}//获取数据库中的密码UserInfo userInfo = userService.queryByName(userName);if (userInfo==null || userInfo.getId()<0){return Result.fail("用户不存在");}
//        if (!password.equals(userInfo.getPassword())){
//            return Result.fail("密码错误!");
//        }if (!SecurityUtils.verify(password, userInfo.getPassword())){return Result.fail("密码错误!");}//生成token, 并返回Map<String, Object> claim = new HashMap<>();claim.put("id", userInfo.getId());claim.put("name", userInfo.getUserName());String token = JwtUtils.genToken(claim);return Result.success(token);}/*** 获取登录用户的信息*/@RequestMapping("/getUserInfo")public UserInfo getUserInfo(HttpServletRequest request){//1. 从token中获取用户ID//2. 根据用户ID, 获取用户信息String token = request.getHeader("user_token");Integer userId = JwtUtils.getUserIdFromToken(token);if (userId==null){return null;}return userService.queryById(userId);}/*** 获取当前作者的信息*/@RequestMapping("/getAuthorInfo")public UserInfo getAuthorInfo(Integer blogId){if (blogId==null && blogId<0){return null;}return userService.getAuthorInfo(blogId);}
}
image-20240405145941565 image-20240405150001483

2.5.4service目录下 UserService.java 业务层

image-20240405145738127

2.5.5 blog_login.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/login.css"></head><body><div class="nav"><img src="pic/logo.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() {// location.assign("blog_list.html");$.ajax({type:"post",url: "/user/login",data:{userName: $("#username").val(),password: $("#password").val()},success:function(result){if(result.code==200 && result.data!=null){//密码正确//存储tokenlocalStorage.setItem("user_token",result.data);location.href = "blog_list.html";}else{alert(result.errorMsg);}}});}</script>
</body></html>
image-20240405151529321

2.5.6 utils下的 JwtUtils 工具类(使用JWT令牌)

image-20240405150303489

package com.example.blog.utils;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;import java.security.Key;
import java.util.Date;
import java.util.Map;/*** Jwt工具类*/
@Slf4j
public class JwtUtils {//过期时间: 1小时private static final long expiration = 60 * 60 * 1000;private static final String secretString = "5CRMLhF7dQnOLCNjJw8dawYK2zTUxS4jDgUW2L99Tdo=";private static final Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));public static String genToken(Map<String, Object> claim){String token = Jwts.builder().setClaims(claim) //设置载荷信息.setExpiration(new Date(System.currentTimeMillis()+expiration)) //设置过期时间.signWith(key)  //设置签名.compact();return token;}public static Claims parseToken(String token){if (token==null){return null;}JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();Claims claims = null;try {claims = build.parseClaimsJws(token).getBody();}catch (Exception e){
//            e.printStackTrace();log.error("解析token失败, token:"+token);}return claims;}public static Integer getUserIdFromToken(String token){Claims claims = parseToken(token);if (claims==null){return null;}return (Integer) claims.get("id");}//    public static void main(String[] args) {
//        String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiemhhbmdzYW4iLCJpZCI6MSwiZXhwIjoxNzA1ODAyNzUyfQ.atKGO-q32Dy5PYfFZZgoogHwbkULivFR_7sXF1Dw-so";
//        Claims claims = parseToken(token);
//        System.out.println(claims);
//    }}

生成token:

image-20240405150709961

解析token:

image-20240405150731609

2.5.7 config目录下 LoginInterceptor.java类 使用拦截器校验token

拦截器(Interceptor)是一种特殊的组件,它可以在请求处理的过程中对请求和响应进行拦截和处理。拦截器可以在请求到达目标处理器之前、处理器处理请求之后以及视图渲染之前执行特定的操作。拦截器的主要目的是在不修改原有代码的情况下,实现对请求和响应的统一处理

image-20240405152328557

utils工具类中对parseToken解析token方法的实现:

image-20240405152918134

2.5.8config目录下 WebConfig.java类 拦截器的添加

image-20240405153635233

通过在common.js中添加ajax发送请求,达到给所有前端页面统一添加功能的目的,不需要一个一个写document功能了

image-20240405154305739

2.5.9添加使用token全流程

image-20240405155015577

后端负责生成token和验证token,前端负责存储token
eg:公安局负责发放和验证身份证,但是不负责存取身份证

2.6实现博客发布页

2.6.1前后端交互接口

image-20240406150235798

2.6.2controller目录下 BlogController.java 控制层

发布博客:

image-20240406150616827

&& 一假为假
|| 一真为真

2.6.3service目录下 BlogService.java 业务层

发布博客:

image-20240406150506632

2.6.4 blog_edit.html 博客登录页前端代码展示

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/logo.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){location.href = "blog_list.html";}else{alert(result.error);}}});}</script>
</body></html>
image-20240406150929000

如果成功的的话,且code状态为200,那么会跳转到blog_list.html页面中,失败就会返回result的错误码

2.7实现博客编辑页

2.7.1前后端交互接口

2.7.2controller目录下 BlogController.java 控制层

image-20240406155712919

2.7.3service目录下 BlogService.java 业务层

image-20240406155746421

2.7.4 blog_update.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/logo.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/Jay.jpg" alt=""><h3></h3><a href="#">GitHub 地址</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"></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) {if (result.code == 200 && result.data != null) {var blog = result.data;$(".title").text(blog.title);$(".date").text(blog.createTime);// $(".detail").text(blog.content);editormd.markdownToHTML("detail", {markdown: blog.content,});//判断是否显示编辑/删除按钮if(blog.loginUser){var finalHtml = "";finalHtml +='<button οnclick="window.location.href=\'blog_update.html?blogId='+blog.id+'\'">编辑</button>';finalHtml +='<button οnclick="deleteBlog('+blog.id+')">删除</button>';console.log(finalHtml);$(".operating").html(finalHtml);}else{}}},error: function (error) {//用户未登录if (error != null && error.status == 401) {alert("用户未登录");location.href = "blog_login.html";}}});//显示博客作者信息getUserInfo("/user/getAuthorInfo" + location.search);function deleteBlog(blogId) {$.ajax({type: "post",url: "/blog/delete"+location.search,success:function(result){if(result.code==200 && result.data==true){location.href ="blog_list.html";}}});}</script>
</body></html>

image-20240406155048828

判断是否显示编辑/删除按钮

image-20240406155432590

错误情况,跳转到博客登录页面

image-20240408095414688

点击更新按钮时更新文章,包括博客ID,文章标题,以及文章内容,更新完成后跳转到博客列表页

image-20240408100225338

2.8实现博客删除功能

2.8.1 mapper层中的实现

第一种方式为设置delete_flag为1来实现删除博客功能

image-20240409162748524

第二种方式为直接删除序号为id的博客的功能

image-20240409162800565

2.8.2controller目录下 BlogController.java 控制层

image-20240406160015139

2.8.3service目录下 BlogService.java 业务层

image-20240406155946904

2.8.4 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/logo.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/Jay.jpg" alt=""><h3></h3><a href="#">GitHub 地址</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"></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) {if (result.code == 200 && result.data != null) {var blog = result.data;$(".title").text(blog.title);$(".date").text(blog.createTime);// $(".detail").text(blog.content);editormd.markdownToHTML("detail", {markdown: blog.content,});//判断是否显示编辑/删除按钮if(blog.loginUser){var finalHtml = "";finalHtml +='<button οnclick="window.location.href=\'blog_update.html?blogId='+blog.id+'\'">编辑</button>';finalHtml +='<button οnclick="deleteBlog('+blog.id+')">删除</button>';console.log(finalHtml);$(".operating").html(finalHtml);}else{}}},error: function (error) {//用户未登录if (error != null && error.status == 401) {alert("用户未登录");location.href = "blog_login.html";}}});//显示博客作者信息getUserInfo("/user/getAuthorInfo" + location.search);function deleteBlog(blogId) {$.ajax({type: "post",url: "/blog/delete"+location.search,success:function(result){if(result.code==200 && result.data==true){location.href ="blog_list.html";}}});}</script>
</body></html>

实现删除功能,删除成功后跳转到博客列表页

image-20240408101018816

2.9实现显示用户登录信息

2.9.1前后端交互接口

image-20240406135307841

2.9.2获取登录用户实现信息

controller层的UserController.java:

image-20240406140612519

service层中的UserService.java类

image-20240406141022980

utils工具目录中的 JwtUtils.java 工具类

image-20240406140820904

2.10实现用户退出

common.js:

image-20240406145820112

在common.js中写入代码,然后再所有前端代码中引入这个js,他就会实现写一次代码可多次重复引入使用,它通过清除token来实现

2.11加密/加盐

加密算法:y = f(x)

image-20240408103121405

image-20240408103113186

image-20240408103138950

不可逆的,无法解密,通常用来做校验

检验数据是否正确:如果两个字符串加密后的密文一样,我们就认为明文是一样

  1. 对称密码算法 是指加密秘钥和解密秘钥相同的密码算法,常见的对称密码算法有:AES,DES,3DES,RC4,RC5,RC6等.

  2. 非对称密码算法 是指加密秘钥和解密秘钥不同的密码算法,该算法使用一个秘钥进行加密,用另外一个秘钥进行解密,

  • 加密秘钥可以公开,又称为 公钥
  • 解密秘钥必须保密,又称为 私钥

常见的非对称密码算法有:RSA,DSA,ECDSA,ECC 等

  1. 摘要算法 是指把任意长度的输入消息数据转化为固定长度的输出教据的一种密码算法,摘要算法是不可逆的,也就是无法解密,通常用来检验数据的完整性的重要技术,即对数据进行哈希计算然后比较摘要值,判断是否一致.常见的摘要算法有: MD5,SHA系列(SHA1,SHA2等),CRC(CRC8,CRC16,CRC32)

image-20240408104334611

使用MD5加密时,同样的字符串在每个平台加密得到的结果都是一样的

例如:

image-20240409152813838

MD5算法图解:

image-20240408104701495

image-20240409152941810

image-20240409152553669

博主项目中采取的加密算法就是MD5+密文(盐值+密文),直接引用DigestUtils包

2.11.2盐值+密文如何获取

image-20240409211044481

圈红的那部分就是 盐值+密文 加密后的密码,将他覆盖到数据库中的password位置,即可实现加密算法,我们输入密码还是输入初始值,例如上图初始密码是123456,此时我们数据库中密码虽然很复杂是 盐值+密文 加密之后的,但是我们登录时只需要输入初始密码即可

2.11.1 UserController.java

image-20240408105236170

检验密码是否正确

2.12 多平台配置

2.12.1配置目录

配置目录文件详情:

image-20240408141644802

image-20240408142251888

application.yml:

spring:profiles:active: @profile.name@

pom.xml:

image-20240408150354641

这里通过在xml文件中添加两个yml文件

image-20240408150458481

然后通过在主配置xml文件中的active中 添加 @profile.name@

即可随意切换xml文件了

image-20240408150719551

这里点dev和prod前面小方框,即可轻松实现添加或者删除xml文件。

application-dev.yml:

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=falseusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰自动转换log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句mapper-locations: classpath:mapper/**Mapper.xml
# 设置日志文件的文件名
logging:file:name: logger/spring-blog.log

application-prod.yml:

server:port: 9090tomcat:uri-encoding: UTF-8max-http-header-size: 8096
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=falseusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰自动转换mapper-locations: classpath:mapper/**Mapper.xml
# 设置日志文件的文件名
logging:file:name: /var/logger/spring-blog.log

application-test.yml:

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=falseusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰自动转换mapper-locations: classpath:mapper/**Mapper.xml
# 设置日志文件的文件名
logging:file:name: /var/logger/spring-blog.log

jquary:

image-20240412165636742

lse
username: root
password: 111111
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰自动转换
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句
mapper-locations: classpath:mapper/**Mapper.xml

设置日志文件的文件名

logging:
file:
name: logger/spring-blog.log


application-prod.yml:```yml
server:port: 9090tomcat:uri-encoding: UTF-8max-http-header-size: 8096
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=falseusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰自动转换mapper-locations: classpath:mapper/**Mapper.xml
# 设置日志文件的文件名
logging:file:name: /var/logger/spring-blog.log

application-test.yml:

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=falseusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰自动转换mapper-locations: classpath:mapper/**Mapper.xml
# 设置日志文件的文件名
logging:file:name: /var/logger/spring-blog.log

感谢各位读者的阅读,本文章有任何错误都可以在评论区发表你们的意见,我会对文章进行改正的。如果本文章对你有帮助请动一动你们敏捷的小手点一点赞,你的每一次鼓励都是作者创作的动力哦!😘

关键字:13.博客系统-spring版本

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: