Simple personal blog based on ssm

Table of contents

1. Overview

2. Implementation steps

2.1 Create an SSM project

2.2 Prepare the project

2.2.1 Delete useless files

2.2.2 Introducing front-end pages

2.2.3 Add configuration file

2.3 Initialize the database

2.4 Carry out project stratification

2.5 Add unified return type

2.6 Realize the user registration function

2.7 Realize the user login function

2.8 Realize the user details page

2.9 Realization of logout function

2.10 Delete article

2.11 View Article Details Page

2.11 Display the detailed information of the author of the article

2.12 Increase the reading volume when requesting the details page

2.13 Article addition and modification functions

2.14 Password salting

2.15 Realization of everyone's blog page and paging function


1. Overview

A blog (Blog) is an online diary based on network technology. It is a website composed of a group of articles. It allows individuals or groups to publish articles, opinions, pictures and videos on the Internet. Content for comment and interactive exchange.

Blogs have a wide range of functions. It can be used for personal recording of life, sharing experience and insights; it can also be used for business promotion, brand promotion and marketing promotion; it can also be used for news reports, current affairs comments and social observations. Blog is not only a medium, but also a social tool, which can help people establish social networks and interpersonal relationships, and increase the exchange and sharing of knowledge and experience.

Blogs are characterized by real-time updates, interactive communication, diverse content, and strong freedom. It provides an open platform for individuals and groups to use the power of the Internet to transmit their voices and information, and influence and change society.

Including the CSDN we are currently using is also a blog platform, where we can exchange technologies, share insights, etc., so have you ever thought about implementing a personal blog project by yourself? Next, I will use the ssm framework to realize the personal blog project. (Take the professional version of IDEA as an example)

2. Implementation steps

2.1 Create an SSM project

First, we need to create an SSM project in IDEA, the steps are as follows:

First, create a new project and select Spring Initializr. At the same time, remember to select JDK8 for the Java version below. The rest of the specific configuration is as follows:

 Then click next, pay attention to select the 2.XX version of the Spring Boot version, I chose 2.7.10 here, note that the parentheses generally refer to the beta version, which may be unstable, try not to choose the parentheses

Then we have to check the five major dependencies of Spring Boot DevTools, Lombok, Spring Web, MySQL Driver and MyBatis Framework , and then click finish, as shown below:


2.2 Prepare the project

Before proceeding with formal project implementation, we need to initialize the project first.

2.2.1 Delete useless files

 See the picture above, where .mvn, HELP.md, mvnw, and mvnw.cmd are all useless files and can be deleted. Of course, there is nothing wrong with not deleting them.


2.2.2 Introducing front-end pages

As a project implemented by the ssm framework, we need the front-end page and back-end code to realize it, but as a back-end programmer, we naturally don’t need to write the front-end page ourselves. The code about the front-end page can be imported into my gitee , here It is placed at the end of the article. When we get the front-end code, we need to place it in the static directory under the resources file. After adding it, it is as shown below:


2.2.3 Add configuration file

The configuration file is divided into properties and yml. Since the properties are relatively old, we usually use the yml configuration file, so we need to choose to create a new application.yml file under resources and delete the application.properties file at the same time.

 Then we need to add the corresponding configuration information in the configuration file, namely application.yml, as follows

# 配置数据库的连接字符串
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8
    username: root
    password: "021226"
    driver-class-name: com.mysql.cj.jdbc.Driver
# 设置 Mybatis 的 xml 保存路径
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置打印 MyBatis 执行的 SQL
logging:
  level:
    com:
      example:
        demo: debug

There are two points to note above. In this configuration file, the password should be changed to your own database password ; and in the url,

url: jdbc:mysql://127.0.0.1:3306/Connected database name?characterEncoding=utf8, that is, the question mark should be changed to which database you want to connect to, and the directory for configuring and printing the SQL executed by MyBatis should be changed to the corresponding one The directory , here is com.example.demo, so it is written like this, and there is no need to make changes in other places


2.3 Initialize the database

We need to create a corresponding database, that is, a database, and then add two tables to the database, one is a user table, and its fields are id, username, password, photo, createtime, updatetime, state;

The other is the article table, the fields are id, title, content, createtime, updatetime, uid, rcount, state Here you can directly copy the code here to the database

-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;

-- 使用数据数据
use mycnblog;

-- 创建表[用户表]
drop table if exists  userinfo;
create table userinfo(
    id int primary key auto_increment,
    username varchar(100) not null,
    password varchar(65) not null,
    photo varchar(500) default '',
    createtime timestamp default current_timestamp,
    updatetime timestamp default current_timestamp,
    `state` int default 1
) default charset 'utf8mb4';

-- 创建文章表
drop table if exists  articleinfo;
create table articleinfo(
    id int primary key auto_increment,
    title varchar(100) not null,
    content text not null,
    createtime timestamp default current_timestamp,
    updatetime timestamp default current_timestamp,
    uid int not null,
    rcount int not null default 1,
    `state` int default 1
)default charset 'utf8mb4';

-- 创建视频表
drop table if exists videoinfo;
create table videoinfo(
  	vid int primary key,
  	`title` varchar(250),
  	`url` varchar(1000),
		createtime timestamp default current_timestamp,
		updatetime timestamp default current_timestamp,
  	uid int
)default charset 'utf8mb4';

-- 添加一个用户信息
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES 
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);

-- 文章添加测试数据
insert into articleinfo(title,content,uid)
    values('Java','Java正文',1);
    
-- 添加视频
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);

2.4 Carry out project stratification

An SSM project should consist of a controller layer, a service layer, a mapper layer, a config layer, and entity classes. We need to create these software packages under demo, and create a file directory called mapper for The following MyBatis operations


2.5 Add unified return type

We create a new common package under the demo, and create a class named AjaxResult to specify the unified return type

 When the return type is stipulated, there are status codes, specific information of the status codes, and returned data . We need to consider the failure and success situations, create different methods, and overload the methods to make it more flexible when passing values.

package com.example.demo.common;

import lombok.Data;

import java.io.Serializable;

/*
统一数据格式返回
 */
@Data
public class AjaxResult implements Serializable {
    //状态码
    private Integer code;
    //状态码描述信息
    private String msg;
    //返回的数据
    private Object data;

    /*
    操作成功返回的结果
     */
    public static AjaxResult success(Object data){
        AjaxResult result=new AjaxResult();
        result.setCode(200);
        result.setMsg("");
        result.setData(data);
        return result;
    }
    public static AjaxResult success(Object data,int code){
        AjaxResult result=new AjaxResult();
        result.setCode(code);
        result.setMsg("");
        result.setData(data);
        return result;
    }
    public static AjaxResult success(Object data,int code, String msg){
        AjaxResult result=new AjaxResult();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }

    /*
    * 返回失败结果
    * */
    public static AjaxResult fail(int code,String msg){
        AjaxResult result=new AjaxResult();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(null);
        return result;
    }
    public static AjaxResult fail(int code,String msg,Object data){
        AjaxResult result=new AjaxResult();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }
}

But not everyone will use this class to return a unified data format, so we have to make a precautionary measure, so that if the returned data type is detected as not a unified object, we will encapsulate it as Unified object , we create a ResponseAdvice class under the common package to achieve this function

To achieve this function, you need to add the ControllerAdvice annotation, and at the same time implement the ResponseBodyAdvice interface, rewrite the supports method and the beforeBodyWrite method

When processing, if it is of AjaxResult type, it can be returned directly; if it is of String type, special processing is required, and jackson needs to be used for type conversion

package com.example.demo.config;

import com.example.demo.common.AjaxResult;
import com.fasterxml.jackson.core.JsonProcessingException;
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.servlet.mvc.method.annotation.ResponseBodyAdvice;

/*
实现 统一数据返回的保底类
即在返回数据之前,检测数据的类型是否是统一对象,如果不是,则封装成统一的对象
 */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;
    // 开关,唯有返回值为true时才会进行beforeBodyWrite的调用
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
    /*
    对数据格式进行校验和封装
     */
    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //是统一的数据格式
        if(body instanceof AjaxResult){
            return body;
        }
        //如果是字符串类型,我们需要引入ObjectMapper进行数据的处理,将其处理为json格式
        if(body instanceof String){
                return objectMapper.writeValueAsString(AjaxResult.success(body));
        }
        return AjaxResult.success(body);
    }
}

2.6 Realize the user registration function

To realize the user registration function, we can start from the front end to the back end; we need to write reg.html first, which is used to send ajax requests, and we will not write the front end here. You can directly go to my source code to copy and paste. Here is the next step to demonstrate the writing of the backend

When performing back-end operations, we must first create a UserController under the controller. The controller is a security system to verify the reliability of the data sent by the front-end, then call UserService to arrange and schedule services, and finally UserService calls UserMapper to perform user the addition of

When adding users, we need to create the user class first, that is, create a Userinfo class under the entity package

package com.example.demo.entity;

import lombok.Data;

import java.time.LocalDateTime;

@Data
public class Userinfo {
    private Integer id;
    private String username;
    private String password;
    private String photo;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private Integer state;
}

The @Data annotation is an annotation provided by Lombok, which allows us to use the corresponding method without writing get, set methods, etc.

After creating the user class, we need to create a UserMapper interface under the mapper to describe the operation

At the same time , create a new file named UserMapper.xml in the mapper folder just created under resources . At this time, the directory structure is as follows:

 The configuration is to be introduced in UserMapper.xml, and its code is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    
</mapper>

Among them, pay attention to modifying the directory structure and class name in the namespace, and then you can describe the addition operation

Adding is an insert operation, the id is reg, and its sql is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <insert id="reg">
        insert into userinfo(username,password) values(#{username},#{password})
    </insert>
</mapper>

Then we create a UserService class under the service package, which injects userMapper and calls the reg method of userMapper

package com.example.demo.service;

import com.example.demo.entity.Userinfo;
import com.example.demo.mapper.UserMapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;
    public int reg(Userinfo userinfo){
        return userMapper.reg(userinfo);
    }
}

Then we also improve the registration code in the UserMapper class, as follows:

package com.example.demo.controller;

import com.example.demo.common.AjaxResult;
import com.example.demo.entity.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;

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/reg")
    public AjaxResult reg(Userinfo userinfo){
        //非空校验和参数有效性校验
        if(userinfo==null || !StringUtils.hasLength(userinfo.getUsername()) ||
                !StringUtils.hasLength(userinfo.getPassword())){
            return AjaxResult.fail(-1,"非法参数");
        }
        return AjaxResult.success(userService.reg(userinfo));
    }
}

At this point, the registration function has been realized


2.7 Realize the user login function

Similar to the registration function, we also need to write the front-end and back-end. Similarly, the front-end part can be copied and pasted from my source code

For the backend part, we also start writing from the mapper part

At the same time, it is also necessary to add a unique constraint to the user name in sql, so that no error will be reported when querying the user object based on the user name later

alter table userinfo add unique(username);

Then add a query operation in userMapper, that is, get the user object according to the user name

package com.example.demo.mapper;

import com.example.demo.entity.Userinfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface UserMapper {
    //注册
    int reg(Userinfo userinfo);

    //根据用户查询userinfo对象
    Userinfo getUserByName(@Param("username") String username);
}

At the same time, add the corresponding sql statement in UserMapper.xml

    <select id="getUserByName" resultType="com.example.demo.entity.Userinfo">
        select * from userinfo where username=#{username}
    </select>

Then add the corresponding method in UserService

    public Userinfo getUserByName(String username){
        return userMapper.getUserByName(username);
    }

Then create a corresponding method in the controller, the process is as follows

    @RequestMapping("/login")
    public AjaxResult login(String username,String password){
        //1.非空校验
        if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)){
            return AjaxResult.fail(-1,"非法请求");
        }
        //2.查询数据库
        Userinfo userinfo=userService.getUserByName(username);
        if(userinfo!=null && userinfo.getId()>0){
            //有效的用户名
            //密码是否正确
            if(password.equals(userinfo.getPassword())){
                //登录成功
                userinfo.setPassword(""); //返回前端之前,隐藏敏感信息
                return AjaxResult.success(userinfo);
            }
        }
        return AjaxResult.success(null,0);
    }

At this point, the code for the login function has been basically implemented, but there are still problems with the code of the above controller. After the user successfully logs in, we should store the user's information in the session . We need to get the HttpServletRequest object first, and then get the session

The modified code is as follows:

   @RequestMapping("/login")
    public AjaxResult login(HttpServletRequest request,String username, String password){
        //1.非空校验
        if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)){
            return AjaxResult.fail(-1,"非法请求");
        }
        //2.查询数据库
        Userinfo userinfo=userService.getUserByName(username);
        if(userinfo!=null && userinfo.getId()>0){
            //有效的用户名
            //密码是否正确
            if(password.equals(userinfo.getPassword())){
                //登录成功
                //todo:将用户存储到session中
                HttpSession session=request.getSession();
                session.setAttribute(AppVariable.USER_SESSION_KEY,userinfo);
                userinfo.setPassword(""); //返回前端之前,隐藏敏感信息
                return AjaxResult.success(userinfo);
            }
        }
        return AjaxResult.success(null,0);
    }

At the same time, we can create a class under common, which is used to store global variables, such as session key values, etc.

package com.example.demo.common;
/*
全局变量
 */
public class AppVariable {
    //用户 session key
    public static final String USER_SESSION_KEY="USER_SESSION_KEY";
}

After completing the login operation, since the user may click on the function in the unlogged state, we need to intercept the user and let him log in, so we need to write a login interceptor . We create a new LoginIntercept class under config, so that It implements the HandlerInterceptor interface and overrides the preHandle method

package com.example.demo.config;

import com.example.demo.common.AppVariable;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/*
登陆拦截器
 */
public class LoginIntercept implements HandlerInterceptor {
    /*
    返回true即用户已登陆,false即用户未登陆
    * */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session=request.getSession(false);
        if(session!=null && session.getAttribute(AppVariable.USER_SESSION_KEY)!=null){
            //用户已登陆
            return true;
        }
        //跳转到登陆页面
        response.sendRedirect("/login.html");
        return false;
    }
}

After the interceptor is written, we need to configure it into the system file . We create a new AppConfig class under config, let it implement the WebMvcConfigurer interface, and add the @Configuration annotation. We select some paths and choose not to intercept

package com.example.demo.config;

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 AppConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginIntercept())
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/editor.md/**")
                .excludePathPatterns("/img/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/login.html")
                .excludePathPatterns("/reg.html")
                .excludePathPatterns("/blog_list.html")
                .excludePathPatterns("/blog_content.html")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/reg");
    }
}

After this is over, the login function has been completed.


2.8 Realize the user details page

The user details page also needs to be implemented on the front and back ends, and the front-end code can still copy my source code

In the backend part, we also start to implement from mapper. At the same time, if we want to get the content of the article, we should check the article table, so we need to create an ArticleMapper interface in the mapper directory first.

package com.example.demo.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface ArticleMapper {
    int getArtCountByUid(@Param("uid") Integer uid);
}

At the same time, we need to create a corresponding xml file in the mapper under resources

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ArticleMapper">
    <select id="getArtCountByUid" resultType="Integer">
        select count(*) from articleinfo where uid=#{uid};
    </select>
</mapper>

Next, similar to the above, we need to create an ArticleService class under service

package com.example.demo.service;

import com.example.demo.mapper.ArticleMapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class ArticleService {
    @Resource
    private ArticleMapper articleMapper;
    public int getArtCountByUid(Integer uid){
        return articleMapper.getArtCountByUid(uid);
    }
}

Next, we need to implement the Controller. We inject the ArticleService class into the UserController and write out the corresponding methods.

But there is a new attribute here, the total number of articles, so we can create a new class, let this class inherit Userinfo, and add a new attribute artCount to this class, we can create a new package under the entity, and then create a new class. inherit

package com.example.demo.entity.vo;

import com.example.demo.entity.Userinfo;
import lombok.Data;

@Data
public class UserinfoVO extends Userinfo {
    private Integer artCount;//发表的文章总数
}

Since the user's login information needs to be used multiple times, we can create a new public class under common to provide operations related to the currently logged-in user

package com.example.demo.common;

import com.example.demo.entity.Userinfo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/*
当前登陆用户相关操作
 */
public class UserSessionUtils {
    //得到当前登陆用户
    public static Userinfo getSessionUser(HttpServletRequest request){
        HttpSession session=request.getSession(false);
        if(session!=null && session.getAttribute(AppVariable.USER_SESSION_KEY)!=null){
            //用户已登陆
            return (Userinfo)session.getAttribute(AppVariable.USER_SESSION_KEY);
        }
        return null;
    }
}

At this point, we can supplement the information related to the logged-in user, that is, fill in the number of user articles in the UserController class

    @RequestMapping("/showinfo")
    public AjaxResult showInfo(HttpServletRequest request){
        UserinfoVO userinfoVO=new UserinfoVO();
        //1.得到当前登陆用户(从session中获取)
        Userinfo userinfo= UserSessionUtils.getSessionUser(request);
        if(userinfo==null){
            return AjaxResult.fail(-1,"非法请求");
        }
        //Spring提供的深克隆方法
        BeanUtils.copyProperties(userinfo,userinfoVO);
        //2.得到用户发表文章的总数
        userinfoVO.setArtCount(articleService.getArtCountByUid(userinfo.getId()));

        return AjaxResult.success(userinfoVO);
    }

Next, we need to obtain the information on the right, that is, the information of the article list page. To obtain the information of the article list, we need the entity class of the article, so we should create a new entity class of the article under entity

package com.example.demo.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.time.LocalDateTime;
import java.util.Date;

@Data
public class Articleinfo {
    private Integer id;
    private String title;
    private String content;
//JsonFormat注解用于规范输出
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private LocalDateTime createtime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private LocalDateTime updatetime;
    private Integer uid;
    private Integer rcount;
    private Integer state;
}

Then add the corresponding method to get the article list according to uid in ArticleMapper

 List<Articleinfo> getMyList(@Param("uid") Integer uid);

Then add the corresponding sql statement to ArticleMapper.xml

  <select id="getMyList" resultType="com.example.demo.entity.Articleinfo">
        select * from articleinfo where uid=#{uid}
    </select>

Then proceed to the corresponding method of Service

   public List<Articleinfo> getMyList(Integer uid){
        return articleMapper.getMyList(uid); 
    }

Then finally, we need to complete the Controller, but because we need to perform related operations on the article table, we need to create a new ArticleController class

package com.example.demo.controller;

import com.example.demo.common.AjaxResult;
import com.example.demo.common.UserSessionUtils;
import com.example.demo.entity.Articleinfo;
import com.example.demo.entity.Userinfo;
import com.example.demo.service.ArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@RestController
@RequestMapping("/art")
public class ArticleController {
    @Autowired
    private ArticleService articleService;

    @RequestMapping("/mylist")
    public AjaxResult getMyList(HttpServletRequest request){
        Userinfo userinfo= UserSessionUtils.getSessionUser(request);
        if(userinfo==null){
            return AjaxResult.fail(-1,"非法请求");
        }
        List<Articleinfo> list=articleService.getMyList(userinfo.getId());
        return  AjaxResult.success(list);
    }
}

2.9 Realization of logout function

The logout function does not need to operate the database, we can directly operate in the UserController, we directly call the removeAttribute method of the session

    /*
    * 注销功能的实现
    * */
    @RequestMapping("/logout")
    public AjaxResult logout(HttpSession session){
        session.removeAttribute(AppVariable.USER_SESSION_KEY);
        return AjaxResult.success("1");
    }

2.10 Delete article

Deleting an article also requires interaction between the front end and the front end. The front end can still view my source code. Part of the process at the back end is as follows:

We first need to define a delete method in ArticleMapper. To delete an article, we need the id of the article and the id of the user. We need to compare whether the uid to which the article belongs is the same as the current user id. If they are different, they cannot be deleted. operate

  int del(@Param("id") Integer id,@Param("uid") Integer uid);

Then go to ArticleMapper.xml to implement the corresponding delete sql

 <delete id="del">
        delete from articleinfo where id=#{id} and uid=#{uid}
    </delete>

Same as above, the next step is to implement it in ArticleService

   public int del(Integer id,Integer uid){
        return articleMapper.del(id,uid);
    }

Finally, go to ArticleController to implement the corresponding method

    @RequestMapping("/del")
    public AjaxResult del(Integer id,HttpServletRequest request){
        if(id==null && id<=0){
             //参数有误
            return AjaxResult.fail(-1,"参数异常");
        }
        Userinfo user=UserSessionUtils.getSessionUser(request);
        if(user==null){
            return AjaxResult.fail(-2,"用户未登陆");
        }
         return AjaxResult.success(articleService.del(id,user.getId()));
    }

2.11 View Article Details Page

The process of viewing the article details page should be as follows: first obtain the article id from the url, query the detailed information of the current article from the backend, then query the user's information according to the uid queried in the previous step, and finally realize the self-increment of the reading volume

The back-end code should also be written from ArticleMapper, we create a method to obtain article information

    Articleinfo getDetail(@Param("id") Integer id);

Then go to ArticleMapper.xml to supplement the corresponding sql statement

  <select id="getDetail" resultType="com.example.demo.entity.Articleinfo">
        select * from articleinfo where id=#{id}
    </select>

Similarly, we will go to ArticleService to implement the corresponding method

  public Articleinfo getDetail(Integer id){
        return articleMapper.getDetail(id);
    }

Then we go to ArticleController to implement the corresponding method

   @RequestMapping("/detail")
    public AjaxResult getDetail(Integer id){
        //非空校验
        if(id==null || id<0){
            return AjaxResult.fail(-1,"非法参数");
        }
        return AjaxResult.success(articleService.getDetail(id));
    }

2.11 Display the detailed information of the author of the article

We need to obtain the user object according to the user's id. Similarly, we first implement the corresponding method in UserMapper

    //根据用户IdD查询userinfo对象
    Userinfo getUserById(@Param(("id")) Integer id);

Then go to UserMapper.xml to implement the corresponding sql statement

 <select id="getUserById" resultType="com.example.demo.entity.Userinfo">
        select * from userinfo where id=#{id}
    </select>

In the same way as above, we go to UserService to implement the corresponding method

  public Userinfo getUserById(Integer id){
        return userMapper.getUserById(id); 
    }

After the UserService method is written, we can implement the method in UserController

    @RequestMapping("/getuserbyid")
    public AjaxResult getUserById(Integer id){
        if(id==null || id<=0){
            //无效参数
            return AjaxResult.fail(-1,"非法参数");
        }
        Userinfo userinfo=userService.getUserById(id);
        if(userinfo==null || userinfo.getId()<=0){
            return AjaxResult.fail(-1,"无效参数");
        }
        //去除敏感数据
        userinfo.setPassword("");
        //查询当前用户发表的文章数
        UserinfoVO userinfoVO=new UserinfoVO();
        BeanUtils.copyProperties(userinfo,userinfoVO);
        userinfoVO.setArtCount(articleService.getArtCountByUid(id));
        return AjaxResult.success(userinfoVO);
    }

2.12 Increase the reading volume when requesting the details page

We still start writing from the ArticleMapper in the backend part, we define an incrRCount() method

    int incrRCount(@Param("id") Integer id);

Then we implement the sql statement in the mapper part

  <update id="incrRCount">
        update articleinfo set rcount=rcount+1 where id=#{id}
    </update>

Then proceed to the code implementation of the ArticleService part

  public int incrRCount(Integer id){
        return articleMapper.incrRCount(id);
    }

Finally, go to ArticleController for the final code implementation

    @RequestMapping("/incr-rcount")
    public AjaxResult incrRCount(Integer id){
        if(id!=null && id>0){
            return  AjaxResult.success(articleService.incrRCount(id));
        }
        return AjaxResult.fail(-1,"未知错误");
    }

2.13 Article addition and modification functions

Let's start with the article adding function. In terms of the back-end code of the article adding function, we also start with ArticleMapper.

    int add(Articleinfo articleinfo);

Let's go to ArticleMapper.xml to implement the corresponding sql

<insert id="add">
        insert into articleinfo(title,content,uid) values(#{title},#{content},#{uid})
    </insert>

Let's go to ArticleService to implement the corresponding implementation

    public int add(Articleinfo articleinfo){
        return articleMapper.add(articleinfo);
    }

Finally, go to ArticleController to implement the corresponding code

    @RequestMapping("/add")
    public AjaxResult add(Articleinfo articleinfo,HttpServletRequest request){
        //1.非空校验
        if(articleinfo==null || !StringUtils.hasLength(articleinfo.getTitle()) ||
         !StringUtils.hasLength(articleinfo.getContent())){
            //非法参数
            return AjaxResult.fail(-1,"非法参数");
        }
        //2.数据库添加操作
        //a.得到当前用户的uid
        Userinfo userinfo=UserSessionUtils.getSessionUser(request);
        if(userinfo==null || userinfo.getId()<=0){
            //无效的用户
            return AjaxResult.fail(-2,"无效的登陆用户");
        }
        articleinfo.setUid(userinfo.getId());
        return AjaxResult.success(articleService.add(articleinfo));
    }

The implementation of the blog modification function is relatively complicated. We need to obtain the id of the article first, then go to the backend to get the detailed information of the article and set it on the page, and then perform the article modification operation. We have implemented the first two operations before. Yes, our focus is to realize the third function, that is, to modify the article, and we also start from the mapper part

    int update(Articleinfo articleinfo);

Then we implement part of the code in ArticleMapper.xml

   <update id="update">
        update articleinfo set title=#{title},content=#{content},updatetime=#{updatetime} where id=#{id} and uid=#{uid}
    </update>

Then we will carry out the code implementation of the ArticleService part

    public int update(Articleinfo articleinfo){
        return articleMapper.update(articleinfo);
    }

Finally, implement the code implementation of the ArticleController part

    @RequestMapping("/update")
    public AjaxResult update(Articleinfo articleinfo,HttpServletRequest request){
        if(articleinfo==null || !StringUtils.hasLength(articleinfo.getTitle())
                || !StringUtils.hasLength(articleinfo.getContent()) ||
                articleinfo.getId()==null){
            //非法参数
            return AjaxResult.fail(-1,"非法参数");
        }
        //得到当前登陆用户的ID
        Userinfo userinfo=UserSessionUtils.getSessionUser(request);
        if(userinfo==null && userinfo.getId()==null){
            //无效登陆用户
            return AjaxResult.fail(-2,"无效用户");
        }
        //核心代码(解决了修改文章归属人)
        articleinfo.setUid(userinfo.getId());
        articleinfo.setUpdatetime(LocalDateTime.now());
        return AjaxResult.success(articleService.update(articleinfo));
    }

2.14 Password salting

The key point of password salt processing is that a salt value is generated every time a method is called, and this salt value is random and unique. We add this random salt value to our own password to get the final password. Encryption is actually It is relatively simple, but the difficulty lies in the decryption operation. The salt value here can be selected as uuid, because uuid is random

We need to get the salt value for the decryption operation, that is, we set a rule according to our own, and encrypt the password entered by the user accordingly. We can set the final password format as: salt value $ final password, and then compare it with the password in the database Corresponding comparison to determine whether it is the correct password

That is , the correct way is to first obtain the salt value from the final password, then combine the salt value with the password entered by the user for md5 encryption, and finally combine the salt value with the password stored in the database for comparison

We can create a new tool class about passwords under common, and write two main methods, one for adding salt and one for verifying passwords

package com.example.demo.common;

import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.util.UUID;

public class PasswordUtils {
    //1. 加盐并生成密码
    public static String encrypt(String password){
        //盐值
        String salt= UUID.randomUUID().toString().replace("-",""); //32位盐值
        //生成加盐后密码
        String saltPassword= DigestUtils.md5DigestAsHex((salt+password).getBytes());
        //生成最终密码(保存到数据库中的密码,32位盐值+$+32位加盐之后的密码)
        String finalPassword=salt+"$"+saltPassword;
        return finalPassword;

    }
    //2.生成加盐的密码(重载
    public static String encrypt(String password,String salt){
        //生成加盐密码
        String saltPassword= DigestUtils.md5DigestAsHex((salt+password).getBytes());
        //生成最终密码
        String finalPassword=salt+"$"+saltPassword;
        return finalPassword;
    }
    //3.验证密码
    //inputPassword为用户输入明文密码
    //finalPassword为数据库保存的密码
    public static boolean check(String inputPassword,String finalPassword){
        if(StringUtils.hasLength(inputPassword) && StringUtils.hasLength(finalPassword) && finalPassword.length()==65){
            //得到盐值
            String salt=finalPassword.split("\\$")[0];
            // 进行加密得到相应的密码
            String confirmPassword=encrypt(inputPassword,salt);
            //对比两个密码是否相同
            return confirmPassword.equals(finalPassword);
        }
        return false;
    }
}

At this point we need to modify two places, one is the password verification when logging in in userController, and the password storage when registering

    @RequestMapping("/login")
    public AjaxResult login(HttpServletRequest request,String username, String password){
        //1.非空校验
        if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)){
            return AjaxResult.fail(-1,"非法请求");
        }
        //2.查询数据库
        Userinfo userinfo=userService.getUserByName(username);
        if(userinfo!=null && userinfo.getId()>0){
            //有效的用户名
            //密码是否正确
            if(PasswordUtils.check(password,userinfo.getPassword())){
                //登录成功
                //todo:将用户存储到session中
                HttpSession session=request.getSession();
                session.setAttribute(AppVariable.USER_SESSION_KEY,userinfo);
                userinfo.setPassword(""); //返回前端之前,隐藏敏感信息
                return AjaxResult.success(userinfo);
            }
        }
        return AjaxResult.success(null,0);
    }
  @RequestMapping("/reg")
    public AjaxResult reg(Userinfo userinfo){
        //非空校验和参数有效性校验
        if(userinfo==null || !StringUtils.hasLength(userinfo.getUsername()) ||
                !StringUtils.hasLength(userinfo.getPassword())){
            return AjaxResult.fail(-1,"非法参数");
        }
        //密码加盐
        userinfo.setPassword(PasswordUtils.encrypt(userinfo.getPassword()));
        return AjaxResult.success(userService.reg(userinfo));
    }

2.15 Realization of everyone's blog page and paging function

The key to realizing the paging function is that the front end needs to know the number of fixed blogs displayed on each page and the current page number

The backend also needs to know these two

At the same time, the realization of the paging function also needs the limit offset keyword in the database to realize

In the same way, our code in the back-end part also starts from the ArticleMapper part

List<Articleinfo> getListByPage(@Param("psize")Integer psize,@Param("offsize") Integer offsize);

Next, we will implement the sql statement in the ArticleMapper.xml part

 <select id="getListByPage" resultType="com.example.demo.entity.Articleinfo">
        select * from articleinfo limit #{psize} offset #{offsize}
    </select>

After completing the design of the Mapper part, we will continue to complete the code implementation of ArticleService

   public List<Articleinfo> getListByPage(Integer psize,Integer offsize){
        return articleMapper.getListByPage(psize,offsize);
    }

The last is to complete the code implementation of the ArticleController part

   @RequestMapping("/listbypage")
    //pindex为当前页面,且至少为1
    //psize为每页的显示条数
    public AjaxResult getListByPage(Integer pindex,Integer psize){
        //1.参数矫正
        if(pindex==null || pindex<=1){
            pindex=1;
        }
        if(psize==null || psize<=1){
            psize=2;
        }
        //分页公式的值(当前页码-1)*每页显示数
        int offset=(pindex-1)*psize;
        List<Articleinfo> list=articleService.getListByPage(psize,offset);
        return AjaxResult.success(list); 
    }

Due to the cumbersome and complicated project, it may be difficult to explain in many places, because a project not only requires the backend, but also requires the implementation of the front-end code to a certain extent, and it is inconvenient to explain the corresponding front-end code here, hope to understand

The source code address is here, JavaSenior/mycnblog-ssm · Xixixixixili/java - Code Cloud - Open Source China (gitee.com)

Guess you like

Origin blog.csdn.net/yss233333/article/details/129915590