SpringBoot实战项目——博客笔记项目

news2024/11/22 20:15:32

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、项目介绍
  • 二、项目的整体框架
  •       2.1 数据库模块
  •       2.2 前端模块
  •       2.3 后端模块
  • 三、项目图片展示
  • 四、项目的实现
  •       4.1 准备工作
  •             4.1.1 数据库,表的创建
  •             4.1.2 配置数据库和MyBatis
  •             4.1.3 引入前端项目
  •       4.2 统一功能处理
  •             4.2.1 统一异常功能处理
  •             4.2.2 统一数据格式返回
  •             4.2.3 统一用户登录验证
  •       4.3 博客登录页的实现
  •             4.3.1 实体类的创建
  •             4.3.2 前后端交互
  •             4.3.3 后端代码实现
  •       4.4 博客列表页的实现
  •       4.5 博客修改删除功能实现
  •       4.6 博客分页功能实现
  • 总结


前言


一、项目介绍

项目名称:博客笔记项目

主要操作的对象是:文章和用户,用户可在该系统上发表自己的博客,查看自己或别人已经发表的文章


需要用到两张表:userinfo(用户表)、articleinfo(文章表)


需要实现的功能:

登录+注册

博客的分页列表功能

新增发表博客

修改、删除自己的博客


 项目亮点

  1. 手动对用户密码实现随机加盐
  2. 统一异常处理、拦截器
  3. 用户登录持久化(session内存)
  4. 分页功能

项目技术栈

SSM(SpringBoot + SpringMVC + MyBatis)

MySQL

jQuery


二、项目的整体框架

2.1 数据库模块

两张表:用户表 + 文章表


2.2 前端模块

前端设计到7个页面

  • login.html登录页
  • reg.html注册页
  • blog_list.html总的博客列表页——》用到了blog_l
  • myblog_list.html个人博客列表页
  • blog_content.html博客详情页
  • blog_edit.html博客编辑页
  • blog_update.html博客修改页


2.3 后端模块

后端模块:


  • 控制层(controller包)——》控制器
  • 服务层(service包)——》服务类
  • 持久层--数据访问层(mapper类 + mapper.xml)——》mapper
  • 实体层(model包)——》实体类
  • 工具层(config包+util包)——》统一异常处理、统一返回、随机加盐


 三、项目具体展示

四、项目的实现

4.1 准备工作

首先我们新建一个springboot项目,项目具体的创建流程我这里就不在赘述。

详细见链接:SpringBoot的创建icon-default.png?t=N7T8http://t.csdnimg.cn/XCd3R


引入需要的依赖pom.xml代码:

<?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.8</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</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.0</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>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.8.10</version>
		</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>
		<finalName>sxy_blog_system</finalName>
	</build>

</project>

4.1.1 数据库,表的创建

1.用户表的创建


2.文章表的创建


4.1.2 配置数据库和MyBatis

1.配置数据库的连接信息


注意: 这里的很多内容是固定的

# 数据库连接配置
Spring:
  datasource:
    url: jdbc:mysql://localhost:3306/你要连接的数据库名?characterEncoding=utf8&useSSL=false
    username: 用户名
    password: 自己的密码
    driver-class-name: com.mysql.cj.jdbc.Driver #只要你数据库用的是mysql这个是固定的

2.配置MyBatis XML存放规则和命名规则


此时我们已经在通过maven将MySQL Driver和MyBatis Framework这两个包导入了进来,此时启动项目,项目依然能够正常运行,说明我们数据库连接是正常的。


4.1.3 引入前端项目

下面的前端的静态资源复制到我们resource/static目录下面:


4.2 统一功能处理

上面我们说了,当程序出现了异常获取其他情况,我们不统一处理。

4.2.1 统一异常功能处理

所以我们需要单独在工具层中(我们的common包下面,建一个统一异常处理的类)


4.2.2 统一数据格式返回

一般在web项目中,我们前后端都是通过json这种数据格式来交换数据格式

——》我们后端需要给前端返回json格式的数据。

我们前后端用户交互的数据个数一般是统一的,不会出现你一个接口用一种数据格式,而那个接口又换了,这样就会有很多问题,不利于开发。


统一数据的优点:

  1. 方便前端程序员更好的接受和解析后端数据接口返回的数据。
  2. 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就可以了,因为所有接口都是这样返回的。
  3. 有利于项目统一数据的维护和修改。
  4. 有利于后端技术部门的统一规范的标准制定,不会出现稀奇古怪的返回内容。

统一数据格式的返回有两种实现方式,返回一个公共对象或者重写。

这里我们采用第二种重写(不过他的灵活性有待提升) 

具体的我们可以使用@ControllerAdvice+ResponseBodyAdvice的方式实现,

具体实现代码如下:

但是正如我们上面所说的,通过重写来进行统一数据格式的返回,他的灵活性的确有待提高。你看我们上面只是处理了成功的情况,但要是失败的情况呢?——》并且即使失败,也是分好几种情况呢!!!


我们不如再创建一个工具类,用来自定义返回hashmap数据(再通过@ResponseBody转成json格式)


那么对应的我们的统一数据格式返回类就发生了变化

package com.example.demo.common;
 
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
 
import java.util.HashMap;
 
/**
 * 统一数据格式返回(灵活性有待提高)
 *  通过统一数据格式的返回,不管我们控制层的方法返回了什么类型的数据
 *  通过重写末尾都能把他转成hashmap格式的数据,然后又通过@ResponseBody注解,将java对象转成了json对象。
 */
@ControllerAdvice
@ResponseBody
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true; // 这个值为true的时候,才会对返回的数据进行重写
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 在有了我们自定义数据返回后,我们的这个统一数据格式返回类就像是一个托地的。
        // 因为controller层可以直接调用AjaxResult,来返回hashmap(通过@ResponseBody转成json)
        // 但如果controller没有调用AjaxResult,直接返回了
        if (body instanceof HashMap) {
            return body;  // 此时已经是hashmap格式了
        }
        if (body instanceof Integer){ // 当controller层中的方法直接返回int类型时候
            int num = (int) body;
            if (num <= 0) {
                // 应对int类型错误返回(查询文章列表,新增和删除博客可能会用到)——》也可能用不到,如果新增或查询失败,我直接就在controller就返回了(通过调用AjaxResult)
                // 新增、删除或查询失败(非得在controller返回int值,再通过这里返回json对象的话,不灵活(出错信息显示的不具体
                // 所以说这里我们只是以防万一,我们还是选择再controller层直接返回json对象,这样更信息具体,更有怎针对性)
                return AjaxResult.fail("抱歉,本次操作失败,请稍后再试!"); // 这里无法区分是新增失败还是删除失败
                // 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式
            }
        }
 
        if (body == null) { // (比如查询操作,直接返回查询到的UserInfo对象,然后直接返回该对象)
            // 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式
            return AjaxResult.fail("抱歉,查询失败!"); // 这时对查询当前用户的特判
        }
        // 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式
        return AjaxResult.success("操作成功", body);
        // 前端是通过result中的status值来判断操作是否成功的,这个类用来处理操作成功的情况(为操作成功的情况兜底)
        // 但这可能存在问题,如果操作失败,并且在controller层没有调用AjaxResult中的fail方法(而是直接返回,通过这个类来返回统一的数据格式,就会出现问题——》在这个类我们都是按成功的处理的)
        // 解决方案,在该类中提前判断body(判断操作失败的情况)--->我们约定如果操作失败就返回负数(在controller层调用AjaxResult的情况)
    }
}

那么与之对应的我们在controller层的类也就发生了改变

package com.example.demo.controller;
 
import com.example.demo.common.AjaxResult;
import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
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.jws.soap.SOAPBinding;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
/**
 * 而Spring Boot框架项目接口返回 JSON格式的数据比较简单:
 * 在 Controller 类中使用@RestController注解即可返回 JSON格式的数据。
 *  @RestController是@Controller和@ResponseBody两者的结合,使用这个注解后该controller的所有方法都会返回json格式的数据,
 *  因为@ResponseBody的作用就是把返回的对象转换为json格式,并把json数据写入response的body中,前台收到response时就可以获取其body中的json数据了。
 * 如果在整个controller类上方添加@RestController,其作用就相当于把该controller下的所有方法都加上@ResponseBody,使每个方法直接返回response对象。
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    public UserService userService;
    @RequestMapping("/reg")
    public Object reg(String username, String password1, String password2) {
        HashMap<String, Object> result = new HashMap<>();
        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password1) || !StringUtils.hasLength(password2)) {
 
            return AjaxResult.fail("你输入的参数有误,请重新输入!");
        }
        else {
            if (!password1.equals(password2)) {
                return AjaxResult.fail("前后密码不一致,请重新输入!");
            }
            else {
                UserInfo userInfo = new UserInfo();
                userInfo.setUsername(username);
                userInfo.setPassword(password1);
                int ret = userService.reg(userInfo);
                if (ret != 1) {
                   return AjaxResult.fail("数据库添加用户失败,请稍后再试!");
                }
                else {
                    return AjaxResult.success("恭喜,注册成功!", ret);
                }
            }
        }
    }
    @RequestMapping("/login")
    public Object login(String username, String password) {
        HashMap<String, Object> result = new HashMap<>();
        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
            return AjaxResult.fail("你输入的参数有误,请重新输入!");
        }
        else {
            // 需要在数据库中查询当前登录的用户是否存在
            UserInfo userInfo = userService.selectByUsername(username);
            if (userInfo == null || !password.equals(userInfo.getPassword())) {
               return AjaxResult.fail("你当前的用户名或密码错误,请重新输入!");
            }
            else  {
                return AjaxResult.success("恭喜,登录成功!", "");
            }
        }
    }
}

4.2.3 统一用户登录验证

spring拦截器

对于以上问题Spring中提供了具体的实现拦截器:HandlerInterceptor,

拦截器的实现分为以下两个步骤:

1、创建自定义拦截器,实现 HandlerInterceptor 接口的perHandle(执行具体方法之前的预处理)方法。

2、将自定义拦截器加入 WebMvcConfiger的 addInterceptors方法中。


步骤1:创建用户登录拦截器


步骤2:

 将该自定义拦截器放到系统的配置文件中

(将自定义拦截器加入 WebMvcConfiger的 addInterceptors方法中)


检验:用浏览器测试一下是否真的拦截了


4.3 博客登录页的实现

4.3.1 实体类的创建

首先根据我们数据库的表,在我们的程序中创建想对应的实体类——我们的MyBatis是ORM框架,我们的程序对象与关系数据库数据之间有响应的映射关系


只有在启动类的同级目录下,springboot才会对该目录下的类进行扫描,配合类注解存到spring容器中。


4.3.2 前后端交互

1.首先用户在前端页面输入了用户信息


2.然后前端紧接着就把用户输入的信息传递给后端(提交到后端指定的接口上,比如登录提交的就是/user/reg 


3.后端接收到用户信息,存到数据库中,并返回注册的结果(成功了?还是失败了?) 


4.前端接受到后端返回的结果后做进一步的处理


4.3.3 后端代码实现

后端流程图:


下面是根据上图流程构建的目录

在controller层中调用了service服务层的reg方法
service服务层又调用了持久层中的mapper接口
mapper接口的实现:UserMapper.xml文件

注册前后端流程交互


4.4 博客列表页的实现

 controller层代码:

1.ArticleController代码实现:

package com.example.demo.controller;

import com.example.demo.config.AjaxResult;
import com.example.demo.model.ArticleInfo;
import com.example.demo.model.UserInfo;
import com.example.demo.service.ArticleService;
import com.example.demo.service.UserService;
import com.example.demo.util.ConstVariable;

import lombok.SneakyThrows;
import org.apache.ibatis.annotations.ResultMap;
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 javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * 而Spring Boot框架项目接口返回 JSON格式的数据比较简单:
 * 在 Controller 类中使用@RestController注解即可返回 JSON格式的数据。
 */
@RestController
@RequestMapping("/article")
public class ArticleController {
    @Autowired
    private ArticleService articleService;// 属性注入


    // 新增操作(发表博客)
    @RequestMapping("/add")
    public Object add(HttpServletRequest request, String title, String content) {
        // todo 非空校验
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {
            return AjaxResult.fail(-1, "当前用户未登录,博客发表失败!");
        }
        UserInfo userInfo = (UserInfo) session.getAttribute(ConstVariable.USER_SESSION_KEY);
        int uid = userInfo.getId(); // 标记当前博客是那个用户发布的
        int rcount = 1; // 表示当前博客的阅读量,初始值(新发表的博客阅读量)==1
        int state = 1; // state表示当前博客状态 1 ==>已发布, 2==》编辑中,保存草稿
        // 新增博客
        ArticleInfo articleInfo = new ArticleInfo();
        articleInfo.setTitle(title);
        articleInfo.setContent(content);
        articleInfo.setUid(uid);
        articleInfo.setRcount(rcount);
        articleInfo.setState(state);
        int result = articleService.add(articleInfo);
        if (result == 1) {
            return AjaxResult.success("博客发表成功!", 1);
        }
        else {
            return AjaxResult.fail(-1, "数据库插入失败,博客发表失败,请稍后再试!");
        }
    }
    // 查询指定用户id的所有博客
    @SneakyThrows
    @RequestMapping("/get_mylist")
    public Object getMyBlogList(Integer uid, HttpServletResponse response) {
//        if (uid == null) {
//            response.sendRedirect("/login.html"); // 当前用户未登录
//        }
//        int uid = Integer.parseInt(id);
        if (uid <= 0) {
            // response.sendRedirect("/lolgin.html"); // 当前用户未登录
            return AjaxResult.fail(401, "当前用户未登录");
        }
        return AjaxResult.success("查找成功",articleService.getMyBlogList(uid));
    }

    // 查看指定博客id的博客详情-->博客详情页,此id为blogId,形参名称要和前端传过来的参数一致
    @RequestMapping("/get_by_blog_id")
    public Object selectByBlogId(int id) {
        if (id <= 0) {
            return AjaxResult.fail(-1, "参数出错,无法找到指定博客的相关信息!");
        }
        ArticleInfo articleInfo = articleService.selectByBlogId(id);
        if (articleInfo == null) {
            return AjaxResult.fail(-1, "数据库查询出错");
        }
        return AjaxResult.success("查找博客详情成功", articleInfo);
    }

    // 获取总的博客列表_无分页功能
    @RequestMapping("/get_list")
    public List<ArticleInfo> getBlogList() {
        return articleService.getBlogList();
    }
    // 获取总的博客列表(实现分页功能)
    @RequestMapping("/get_list_by_page")
    public Object getListByPage(Integer pindex, Integer psize) {
        HashMap<String, Object> data = new HashMap<>();
        // 登录进去后,第一次初始化页面时候url = list.html,即pindex是空的,username等也是空的
        if (pindex == null || pindex < 1) {
            pindex = 1;
        }
        if (psize == null || psize <= 0) {
            psize = 2;
        }
        // 上面这些特殊情况的处理,很重要(就是应对url = list.html这种情况,如果没有特判,会出错(查询不到数据)
        // 我们前后端都对这些特殊情况做了处理——不多余(双重保障,你进入公司后前端可不是你写的,所以我们就要把我们负责的后端写的扎实(不管前端怎么传,我后端都能应对)
        int offset = (pindex - 1) * psize;
        List<ArticleInfo> list = articleService.getListByPage(psize, offset);
        // 得出用户名为username,地址为address,邮箱为email的所有用户个数
        // getPageCount函数————》得到总的博客条数
        int totalCount = articleService.getPageCount();
        data.put("list", list);
        data.put("count", totalCount);
        return AjaxResult.success("分页功能的实现", data);

    }
    // 删除指定博客id的博客
    @SneakyThrows
    @RequestMapping("/del")
    public Object del(HttpServletRequest request, int id, HttpServletResponse response) {
        ArticleInfo articleInfo = articleService.selectByBlogId(id);
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {
            // response.sendRedirect("/login.html");
            return AjaxResult.fail(401, "当前用户未登录!");
        }
        // 这篇博客的作者(uid)和当前登录的用户id相同才有权限删除
        UserInfo userInfo = (UserInfo) session.getAttribute(ConstVariable.USER_SESSION_KEY);
        if (articleInfo.getUid() != userInfo.getId()) {
//            response.sendRedirect("/blog_list.html");
            return AjaxResult.fail(-1, "你不是该篇博客的作者,你无权限进行删除操作");
        }
        if (id <= 0) {
            return AjaxResult.fail(-1, "找不到要删除的博客,删除失败");
        }
        int result = articleService.del(id);
        if (result <= 0) {
            return AjaxResult.fail(-1, "数据库删除操作失败!");
        }
        // 获取该篇博客的作者id

        Queue<Integer> queue = new LinkedList<>();
        ArrayList<Integer> list = new ArrayList<>();
        ArrayList<ArrayList<Integer>> lists = new ArrayList<>();
        // response.sendRedirect("/blog_list.html");
        return AjaxResult.success("删除博客成功!", result);
    }
    /**
     * 修改指定id的博客内容或标题
     */
    @RequestMapping("/update")
    public Object update(HttpServletRequest request, int id, String title, String content) {
        if (id <= 0) return AjaxResult.fail(-1, "你指定的博客id有误,稍后再试!");

        ArticleInfo articleInfo = articleService.selectByBlogId(id);
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {
            // response.sendRedirect("/login.html");
            return AjaxResult.fail(401, "当前用户未登录!");
        }
        // 这篇博客的作者(uid)和当前登录的用户id相同才有权限删除
        UserInfo userInfo = (UserInfo) session.getAttribute(ConstVariable.USER_SESSION_KEY);
        if (articleInfo.getUid() != userInfo.getId()) {
//            response.sendRedirect("/blog_list.html");
            return AjaxResult.fail(-1, "你不是该篇博客的作者,你无权限进行修改操作");
        }
        else {
            int ret = articleService.update(id, title, content);
            if (ret <= 0) {
                return AjaxResult.fail(-1, "数据库更新操作失败!");
            }
            else {
                return AjaxResult.success("更新博客成功", ret);
            }
        }
    }

}

2.UserController代码实现:

package com.example.demo.controller;

import com.example.demo.config.AjaxResult;
import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import com.example.demo.util.ConstVariable;
import com.example.demo.util.PasswordUtil;

import lombok.SneakyThrows;
import org.apache.ibatis.annotations.ResultMap;
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 javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    /**
     * 用户注册
     * @param username
     * @param password1
     * @param password2
     * @return
     */
    @RequestMapping("/reg")
    public Object reg(String username, String password1, String password2) {
        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password1) || !StringUtils.hasLength(password2)) {
            return AjaxResult.fail(-1, "参数输入错误");
        }
        else {
            if (!password1.equals(password2)) {
                return AjaxResult.fail(-1, "前后密码不一致");
            }
            else {
                int result = userService.reg(username, PasswordUtil.encrypt(password1));
                if (result != 1) {
                    return AjaxResult.fail(-1, "数据库添加出错");
                }
                return AjaxResult.success("注册成功", "1");
            }
        }
    }

    /**
     * 用户登录
     * @param request
     * @param username
     * @param password
     * @return
     */
    @RequestMapping("/login")
    public Object login(HttpServletRequest request, String username, String password) {

        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
            return AjaxResult.fail(-1, "参数输入错误,登录失败");
        }
        UserInfo userInfo = userService.selectByUsername(username);
        // 随机加盐算法,未加密的密码用同样的加密算法和数据库中的加密算法进行比较
        if (userInfo == null || ! PasswordUtil.decrypt(password, userInfo.getPassword()) || userInfo.getId() <= 0) {

            return AjaxResult.fail(-1, "账号或密码错误,登录失败");
        }
        // 如果程序走到这里,说明登陆成功了
        // 每次登录成功都要对数据库中的密码进行重新生成,这样才符合随机加密
        // userService.passwordRefresh()
        HttpSession session = request.getSession(); // 获取session,如果没有就创建一个

        session.setAttribute(ConstVariable.USER_SESSION_KEY, userInfo);
        return AjaxResult.success("登录成功", 1);
    }
    /**
     *   获取当前登录的用户名
     */
    @RequestMapping("/get_user")
    public Object getUser(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return AjaxResult.fail(401, "当前用户未登录");
        }
        UserInfo userInfo = (UserInfo) session.getAttribute(ConstVariable.USER_SESSION_KEY);
        if (userInfo == null) {
            return AjaxResult.fail(401, "当前用户未登录");
        }
        return userInfo; // 这里我们可以直接用AjaxResult自定义统一格式返回,也可以传统的统一格式返回
    }

    /**
     * 退出登录
     * @param request
     * @param response
     */
    @SneakyThrows
    @RequestMapping("/logout")
    public Object logout(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {
            // response.sendRedirect("/login.html"); // 当前用户未登录
            return AjaxResult.fail(401, "当前用户未登录!");
        }
        session.setAttribute(ConstVariable.USER_SESSION_KEY, null);
        //response.sendRedirect("/login.html");
        return AjaxResult.success("当前状态正常,可以正常退出!", "true");
    }

    /**
     * 通过个人id(uid)查询个人详细信息
     * @param authorId
     * @return
     */
    @RequestMapping("/get_detail_by_uid")
    public Object selectByUid(Integer authorId) {
        int uid = authorId;
        if (uid <= 0) {
            return AjaxResult.fail(-1, "uid《=0,查询出错!");
        }
        UserInfo userInfo = userService.selectByUid(uid);
        if (userInfo == null) {
            return AjaxResult.fail(-1, "在数据库中查询不到指定用户id的详细信息,请稍后再试!");
        }
        return AjaxResult.success("通过uid查询个人详细信息成功", userInfo);
    }
}

在controller层中调用了service服务层的reg方法


 service服务层又调用了持久层中的mapper接口


4.5 博客修改删除功能实现

mapper层代码:

1.Articlemapper:

package com.example.demo.mapper;

import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface ArticleMapper {
    // 发布新文章
    public int add(@Param("articleinfo")ArticleInfo articleinfo);
    // 查询指定博客id的博客详情--》用于博客详情页
    public ArticleInfo selectByBlogId(@Param("blogId") int blogId);

    // 查询个人的博客列表——》个人博客详情页
    public List<ArticleInfo> getMyBlogList(@Param("uid") int uid);
    // 查询总的博客列表(无分页功能)
    public List<ArticleInfo> getBlogList();

    // 删除指定博客id的博客
    public int del(@Param("id") int id);

    // 得到当前的博客总数目
    public int getPageCount();

    // 查询总的博客列表(分页功能的实现)_>注意limit和offset的位置(相对位置(参数的相对位置,不要弄反——》查询数据库会出错
    public List<ArticleInfo> getListByPage(@Param("limit") int psize, @Param("offset") int offset);
    // psize对应limit--》表示每页显示的记录条数。pindex对应offset——》表示当前查看的是第几页的数据

    // 修改指定博客id的博客title和content----------->注意形参的顺序
    public int update(@Param("title") String title, @Param("content") String content, @Param("id") int id);

}

2.Usermapper代码实现:

package com.example.demo.mapper;

import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface UserMapper {
    // 用户注册
    public int reg(@Param("username") String username, @Param("password") String password);


    // 通过名称查询用户-->用于登录的验证
    public UserInfo selectByUsername(@Param("username") String username);

    // 通过个人id(uid)查询个人详细信息
    public UserInfo selectByUid(@Param("uid") int uid);
}

1.AjaxResult代码:

用户登录拦截器,自定义拦截器添加到系统的配置文件中。

package com.example.demo.config;


import org.springframework.stereotype.Controller;

import java.util.HashMap;

/**
 * 自定义返回数据(我们通过AjaxResult类返回的都是hashmap格式的对象
 * 之后还有通过@ResponseBody将java对象转成json格式的数据
 */
public class AjaxResult {
    /**
     * 操作成功(无msg)
     * @param data
     * @return
     */
    public static Object  success(Object data) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("state", 200);
        result.put("msg", "");
        result.put("data", data);
        return result;
    }

    /**
     * 操作成功,有msg
     * @param msg
     * @param data
     * @return
     */
    public static Object success(Object msg, Object data) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("state", 200);
        result.put("msg", msg);
        result.put("data", data);
        return result;
    }

    /**
     * 操作失败(无data
     * @param state
     * @param msg
     * @return
     */
    public static Object fail(int state, Object msg) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("state", state);
        result.put("msg", msg);
        result.put("data", "");
        return result;
    }

    /**
     * 操作失败(有data)
     * @param state
     * @param msg
     * @param data
     * @return
     */
    public static Object fail(int state, Object msg,  Object data) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("state", state);
        result.put("msg", msg);
        result.put("data", data);
        return result;
    }
}

2.Logininterception代码:

用户登录拦截器(查看当前用户是否登录)

package com.example.demo.config;

import com.example.demo.util.ConstVariable;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashSet;

/**
 * 用户登录拦截器(查看当前用户是否登录)
 */
@Component
public class LoginInterception implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {
            // 当前用户未登录,进行拦截--->跳转到登录页面--->后端我们只用给前端返回状态和数据,我们后端不用管页面怎么,跳转工作是前端的事情
            // response.sendRedirect("/login.html");
            // 401表示未登录,没有权限,403不是因为没有登录而没有权限,而是该用户没有权限,管理员和普通用户的区别
            response.setStatus(401);
            response.sendError(401, "当前用户为登录,没有权限访问该接口");
            return false;
        }
        return true;
    }
}

3.ResponseAdvice代码:

数据格式同样返回(这个是兜底的,即使controller层返回的是int或string类型的数据,也可以转换成json格式)

注意:只能兜底int类的返回(可以应对操作成功和失败两种情况)

如果是String或其他格式的返回(可以应对操作成功的情况,但无法应对操作失败的情况,要继续对body进行特判)

package com.example.demo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.beans.PropertyEditorSupport;
import java.util.HashMap;

/**
 * 数据格式同样返回(这个是兜底的,即使controller层返回的是int或string类型的数据,也可以转换成json格式)
 * 注意:只能兜底int类的返回(可以应对操作成功和失败两种情况)
 * 如果是String或其他格式的返回(可以应对操作成功的情况,但无法应对操作失败的情况,要继续对body进行特判)
 *
 * 这里我们只是以防万一,我们尽量还是选择再controller层直接返回json对象,这样更信息具体,更有怎针对性)
 */
@ControllerAdvice // 统一功能处理需要加这个注解
@ResponseBody
// @ResponseBody的作用就是把返回的对象转换为json格式,并把json数据写入response的body中,前台收到response时就可以获取其body中的json数据了。
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true; // 返回true才会继续执行下面的代码
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof HashMap) {
            return body;  // 此时已经是hashmap格式了
        }
//        HashMap<String, Object> result = new HashMap<>();
//        result.put("state", 200);
//        result.put("msg", "");
//        result.put("data", body);
        if (body instanceof Integer){
            int num = (int) body;
            if (num <= 0) {
                // 应对int类型错误返回(查询文章列表,新增和删除博客可能会用到)——》也可能用不到,如果新增或查询失败,我直接就在controller就返回了(通过调用AjaxResult)
                // 新增、删除或查询失败(非得在controller返回int值,再通过这里返回json对象的话,不灵活(出错信息显示的不具体
                // 所以说这里我们只是以防万一,我们还是选择再controller层直接返回json对象,这样更信息具体,更有怎针对性)
                return AjaxResult.fail(-1,"抱歉,本次操作失败,请稍后再试!"); // 这里无法区分是新增失败还是删除失败
                // 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式
            }
        }

        if (body == null) { // 应对String类型错误返回
            // 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式
            return AjaxResult.fail(-1,"抱歉,查询失败!"); // 这时对查询当前用户的特判
        }
        if (body instanceof String) { // 以String类型正确返回
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.writeValueAsString(body); // 用jackson中的工具类返回(没弄明白)——》直接返回String吧(不序列化了,详见https://zhuanlan.zhihu.com/p/196372502)
        }
        // 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式
        return AjaxResult.success(body);
        // 前端是通过result中的state值来判断操作是否成功的,这个类用来处理操作成功的情况(为操作成功的情况兜底)
        // 但这可能存在问题,如果操作失败,并且在controller层没有调用AjaxResult中的fail方法(而是直接返回,通过这个类来返回统一的数据格式,就会出现问题——》在这个类我们都是按成功的处理的)
        // 解决方案,在该类中提前判断body(判断操作失败的情况)--->我们约定如果操作失败就返回负数(在controller层调用AjaxResult的情况)

    }
}

4.6 博客分页功能实现

1.ExcptionAdvice代码:

统一异常处理

@ResponseBody的作用就是把返回的对象转换为json格式,并把json数据写入response的body中,前台收到response时就可以获取其body中的json数据了

package com.example.demo.config;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;

/**
 * 统一异常处理
 */
@ControllerAdvice
@ResponseBody
// @ResponseBody的作用就是把返回的对象转换为json格式,并把json数据写入response的body中,前台收到response时就可以获取其body中的json数据了。
public class ExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public Object exceptionAdvice(Exception e) {
        // 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式
        return AjaxResult.fail(-1, e.getMessage());
    }
    // 你这里的统一异常处理,并不完整

}

2.ApplicationConfig代码:

package com.example.demo.config;

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

// 系统配置文件类
@Configuration // 该配置文件在项目启动时配置
public class ApplicationConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterception loginInterception; // 用户登录拦截器
    // 自定义拦截器添加到系统的配置文件中

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterception)
                .addPathPatterns("/**")       // 设置拦截规则
//                .excludePathPatterns("/login.html")// 下面这些指定的url,不会检查是否登录——进行拦截
//                .excludePathPatterns("/reg.html")
                .excludePathPatterns("/**/*.html")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/reg")
                // 总的博客的列表,和博客详情,我们需要开放权限,即使用户未登录也可以查看
                .excludePathPatterns("/article/get_list_by_page") // 放行总博客列表
                .excludePathPatterns("/article/get_by_blog_id") // 放行博客详情页面
                .excludePathPatterns("/user/get_detail_by_uid")
                //.excludePathPatterns("/css/**")--->和下一行的作用是一样的
                //.excludePathPatterns("/**/*.css")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/editor.md/**")
                .excludePathPatterns("/img/**"); // 直接img目录下的所有都放行

    }
}

总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1512833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PHP爬虫技术:利用simple_html_dom库分析汽车之家电动车参数

摘要/导言 本文旨在介绍如何利用PHP中的simple_html_dom库结合爬虫代理IP技术来高效采集和分析汽车之家网站的电动车参数。通过实际示例和详细说明&#xff0c;读者将了解如何实现数据分析和爬虫技术的结合应用&#xff0c;从而更好地理解和应用相关技术。 背景/引言 随着电…

git svn混用

背景 项目代码管理初始使用的svn, 由于svn代码操作&#xff0c;无法在本地暂存&#xff0c;有诸多不便&#xff0c;另外本人习惯使用git. 所以决定迁移至git管理 迁移要求&#xff1a; 保留历史提交记录 迁移流程 代码检出 git svn svn_project_url git代码提交 修改本…

突然估摸出了chrome数据的备份

首先是下载 其默认下载到c盘。 我们打开刚刚安装的chrome的位置&#xff0c; 我电脑上是 C:\Users\Lenovo\AppData\Local\Google\Chrome\Application 第一个文件名和版本号对应。 我们查看其上级目录&#xff0c;可以发现有个User Data&#xff0c;这个文件夹里面存放的就是…

发送短信验证码

​​​​​​【短信验证码-快速报备签名】三网短信接口-短信-短信验证码-短信服务-三网短信接口-短信-三网短信【最新版】_商业智能_电商_金融-云市场-阿里云阿里云云市场提供 专注企业短信服务10年运营与技术积累&#xff0c;稳定、安全、快速。服务&#xff0c;建站服务&…

雷达图相关

1.中间显示数字 title: {text: 88,x: center,y: center,textStyle: {color: #333,fontWeight: bolder,fontSize: 64,} } 2.提示信息 tooltip: {trigger: item, // 当鼠标悬浮在某个数据项上时触发}, 3.修改中间颜色 默认&#xff1a; splitArea: {areaStyle: {color: [rgba(…

初识Spring MVC

什么是Spring MVC? 官方给的解释是 Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架&#xff0c;从⼀开始就包含在 Spring 框架中。它的 正式名称“Spring Web MVC”来⾃其源模块的名称(Spring-webmvc)&#xff0c;但它通常被称为"Spring MVC" 注:Severlet是…

近700所高校,2024年预算出炉!

办学经费&#xff0c;是高校发展的核心与基石。学校人才培养、科学研究等各项事业的开展&#xff0c;都有赖于教育经费的支持。 近日&#xff0c;全国已有北京、上海、江苏、浙江等20多个省&#xff08;市、自治区&#xff09;的高校对外公布了2024年预算经费&#xff0c;小编…

阳光保险MySQL数据库平稳迁移OceanBase,稳定运营超700天

作者简介&#xff1a; 车东兴&#xff1a;于阳光保险就职&#xff0c;深耕保险行业的 IT 领域长达12 年&#xff0c;对保险领域的基础架构实践有深刻的理解与掌握。熟悉多款数据库&#xff0c;具有丰富的数据库运维经验。 王华城&#xff1a;于阳光保险就职&#xff0c;10多年一…

中科数安 | 电子文档加密软件——企业数据资料防泄密系统

整体介绍&#xff1a; 中科数安电子文档加密软件&#xff0c;作为一款专为企业级数据安全打造的核心产品&#xff0c;它将尖端加密技术与智能管理策略完美融合&#xff0c;为您企业的核心电子文档提供全方位、多层次的安全防护。我们的加密软件不仅能实现文档的透明加密&#x…

【论文阅读】IEEE Access 2019 BadNets:评估深度神经网络的后门攻击

文章目录 一.论文信息二.论文内容1.摘要2.引言3.主要图表4.结论 一.论文信息 论文题目&#xff1a; BadNets: Evaluating Backdooring Attacks on Deep Neural Networks&#xff08;BadNets:评估深度神经网络的后门攻击&#xff09; 论文来源&#xff1a; 2019-IEEE Access …

【C语言】字符串函数下

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《C语言》 &#x1f389;道阻且长&#xff0c;行则将至 前言 这篇博客是字符串函数下篇&#xff0c;主要是关于长度受限制的字符串函数&#xff08;strncpy,strncat,strncmp)的使用…

【绘图案例-通过storyboard使用Modal Objective-C语言】

一、继续之前的Modal,刚才讲了通过代码使用Modal,继续说如何通过storyboard来使用Modal, 1.先选中这一段代码, 删掉, 再来到TestViewController.m文件里边,选中这一段代码,也删掉, 这个也无所谓啊,这个类,直接删了就行, 选中这个类,Delete,选择Move to Trash, …

机器学习-04-分类算法-03KNN算法

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中分类算法&#xff0c;本篇为分类算法与knn算法部分。 本门课程的目标 完成一个特定行业的算法应用全过程&#xff1a; 懂业务会选择合适的算法数据处理算法训练算法调优算法融合 算法评估持续调优工程化…

mysql启动报错:ERROR! The server quit without updating PID file

mysql 重启或者启动时报错&#xff1a;ERROR! The server quit without updating PID file (/home/data/mysql/e787y33206.novalocal.pid).如下图: 重启报错&#xff1a; 启动也报错 这个错误是因为有mysql启动没有完全结束&#xff0c;如果是重启mysql服务&#xff0c;则在…

【PyQT/Pysider】控件背景渐变

默认渐变配色说明 background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 178, 102, 255), stop:0.55 rgba(235, 148, 61, 255), stop:0.98 rgba(0, 0, 0, 255), stop:1 rgba(0, 0, 0, 0));这段样式表使用了qlineargradient函数来创建…

当HR问你:“如果你与上级的意见不一致,你会怎么处理?”【文章底部添加进大学生就业交流群】

在面对与直属领导意见不一致的情况时&#xff0c;我会坚定地支持直属领导的决定。在过去的实习经历中&#xff0c;我也遇到过类似的情况&#xff0c;而我的处理方法是“坚定执行&#xff0c;逐渐理解”。 我个人有很多想法&#xff0c;但我也明白直属领导的视角和我的视角可能…

基于Qt 和python 的自动升级功能

需求&#xff1a; 公司内部的一个客户端工具&#xff0c;想加上一个自动升级功能。 服务端&#xff1a; 1&#xff0c;服务端使用python3.7 &#xff0c;搭配 fastapi 和uvicorn 写一个简单的服务&#xff0c;开出一个get接口&#xff0c;用于客户端读取安装包的版本&#…

如何采集电商网站数据之抖音商品搜索商品详情数据采集

电商网站的数据包含了丰富的市场和用户信息&#xff0c;对于商业决策和竞争分析至关重要。数据采集是获取这些有价值信息的关键步骤。下面将详细介绍如何采集电商网站数据&#xff0c;帮助你掌握这一重要技能。 1. 设定数据采集目标 在开始采集之前&#xff0c;明确你的数据采…

初识进程状态

&#x1f30e;进程状态【上】 文章目录&#xff1a; 进程状态 发现进程的状态 运行队列 进程排队 进程状态的表述       状态在代码中的表示       运行状态       阻塞状态       挂起状态 总结 前言&#xff1a; 为了搞明白正在运行的进程是什么意思…

优思学院|为什么企业要做质量管理体系认证?

在二战后的美国&#xff0c;公司对自己的产品质量颇为自满。市场需求旺盛&#xff0c;产品销售状况良好&#xff0c;即便产品存在质量缺陷&#xff0c;消费者似乎也能接受。这种态度导致了一种现象&#xff1a;即使在生产结束时发现了一定比例的缺陷&#xff0c;公司也能通过加…