Separación de front-end del proyecto SSM + entorno operativo IDEA (incluido el código fuente de front-end) (sistema de blog personal)

Tabla de contenido

 Configuración del entorno del proyecto backend

1. Cree un proyecto SpringBoot, agregue el marco MyBatis y las dependencias del controlador MySQL de la base de datos

2. Archivo de proyecto de configuración: application.yml

3. Crea una tabla de base de datos

4. Crear un directorio de estructura jerárquica

Volver al formato de datos unificado 

Cree una clase de retorno de formato de datos unificado: AjaxResult

Cree una clase de garantía para la devolución de datos unificados: ResponseAdvice

Procesamiento unificado

interceptor de inicio de sesión

Configurar reglas de interceptación

Realizar la función de registro de usuario

Paso 1: escriba el código de front-end para enviar la solicitud ajax

Paso 2: El backend recibe los datos ajax devueltos por el frontend para su juicio

entidad de clase de entidad

Mapeador de capa de persistencia de datos

servicio de capa de servicio

Controlador de capa de control

Declaración de inserción del archivo de configuración de MyBatis

 Implementar la función de inicio de sesión de usuario

El front-end envía el código Ajax

Código de proceso de verificación de backend

Definir la clase de variable global AppVariable

Mapeador de capa de persistencia de datos

Archivo de configuración de base de datos mapper.xml

servicio de capa de servicio

controlador de capa de control

función de información personal

Obtener la función de número de artículos

Obtenga el número de artículos del código de front-end

Obtener el número de artículos de código de fondo

Paquete de clase de entidad vo

ArticleMapper de capa de persistencia de datos

Capa de servicio ArticleService

controlador de capa de control

Archivo de configuración de base de datos ArticleMapper.xml

Obtenga la función de cantidad de clasificación de la lista de artículos

Código front-end para obtener el número de categorías en la lista de artículos

Obtenga el código de back-end de la cantidad de clasificación de la lista de artículos

Crear una clase de entidad de clasificación de lista de artículos Articleinfo

Capa de control ArticleController

Eliminar función de artículo

Eliminar el código del front-end del artículo

Eliminar el código de fondo del artículo

Configuración de ArticleMapper.xml

ArticleMapper de capa de persistencia de datos

Capa de servicio ArticleService

Capa de control ArticleController

función de cierre de sesión

Implementación del código frontal de la función de cierre de sesión

Implementación del código de fondo de la función de cierre de sesión

Capa de control UserController

Ver la función de página de detalles y realizar la función de volumen de lectura

Consulta de información detallada y código de front-end de volumen de lectura

Consultar información detallada y leer el código de back-end del volumen

ArticleMapper de capa de persistencia de datos

Información de configuración de ArticleMapper.xml

Capa de servicio ArticleService

Capa de control ArticleController

Agregar función de artículo

Código JS frontal

Código de implementación de back-end

Modificar la función del artículo

código de front-end

código de fondo

Función de lista de artículos de consulta de paginación

código de fondo

código de front-end

Cifrado de sal de contraseña

1. Almacenamiento de contraseña de texto sin formato original

2. Método de cifrado tradicional: MD5

3. Algoritmo de cifrado de sal

Código implementado manualmente usando encriptación salt

Algoritmo de cifrado salt método dos: usando el marco Spring Security 


Dirección de descarga del código fuente de la página estática frontal: https://pan.baidu.com/s/1RHxWTX1xUNJdK1GNVrImsw?pwd=ofep

Código de extracción: ofep

Este artículo registra el resumen del sistema de blogs personales creado desde cero, principalmente para presentar la realización de funciones del proyecto back-end. El código front-end se ha implementado de antemano y no se resumirá en este artículo.

La visualización del efecto estático del código front-end:

Interfaz de registro 

 interfaz de inicio de sesión

Interfaz de la página de inicio del blog 

 Importe el archivo de código de front-end en el directorio estático en el directorio de recursos en el proyecto SpringBoot creado por IDEA

 Configuración del entorno del proyecto backend

1. Cree un proyecto SpringBoot, agregue el marco MyBatis y las dependencias del controlador MySQL de la base de datos

        Para obtener detalles de la operación, consulte la publicación de blog anterior: https://pan.baidu.com/s/1RHxWTX1xUNJdK1GNVrImsw?pwd=ofep

2. Archivo de proyecto de configuración: application.yml

# 配置数据库的连接字符串
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&useSSL=false
    username: "root"
    password: "xzf1314520"
    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
# 配置打印日志输出
logging:
  level:
    com:
      example:
        demo: debug

3. Crea una tabla de base de datos

-- 创建数据库
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 unique,
    password varchar(100) not null,
    photo varchar(500) default '',
    createtime datetime default now(),
    updatetime datetime default now(),
    `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 datetime default now(),
    updatetime datetime default now(),
    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 datetime default now(),
		updatetime datetime default now(),
  	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);

4. Crear un directorio de estructura jerárquica

Asignador de interfaz de capa de persistencia de datos, servicio de capa de servicio, controlador de capa de control, entidad de clase de entidad, configuración de clase de configuración de proyecto 

Volver al formato de datos unificado 

Cree una clase de retorno de formato de datos unificado: AjaxResult

1. Cree un commer de paquete público para almacenar un formato de devolución unificado en el front-end

2. Cree una clase para devolver en un formato de datos unificado bajo el paquete público commer : AjaxResult

        La clase AjaxResult contiene el código de estado, la información de descripción del código de estado y los resultados de los datos devueltos.

3. Métodos que devuelven los resultados de operaciones exitosas y fallidas

4. La clase AjaxResult implementa la interfaz de serialización SeriaLizable, que puede implementar operaciones especiales y el código no informará errores.

package com.example.demo.commer;
import lombok.Data;

import java.io.Serializable;

/*
* 返回统一数据格式
* */
@Data // 注解:添加get和set方法
public class AjaxResult implements Serializable {

    // 状态码
    private Integer code;
    // 状态码描述信息
    private String mesg;
    // 返回的数据
    private Object data;



    public static AjaxResult success(Object data){
        /*
         * 操作成功返回的结果
         * */
        AjaxResult result = new AjaxResult();
        result.setCode(200);
        result.setMesg("");
        result.setData(data);
        return result;
    }
    public static AjaxResult success(Integer code,Object data){
        /*
         * 操作成功返回的结果
         * */
        AjaxResult result = new AjaxResult();
        result.setCode(code);
        result.setMesg("");
        result.setData(data);
        return result;
    }
    public static AjaxResult success(Integer code,String mesg,Object data){
        /*
         * 操作成功返回的结果
         * */
        AjaxResult result = new AjaxResult();
        result.setCode(code);
        result.setMesg(mesg);
        result.setData(data);
        return result;
    }
    public static AjaxResult fail(Integer code,String mesg,Object data){
        /*
        * 返回失败的结果
        * */
        AjaxResult result = new AjaxResult();
        result.setCode(code);
        result.setMesg(mesg);
        result.setData(null);
        return result;
    }

}

         Cuando se crea la clase de formato de devolución de datos unificados, se debe llamar a la clase AjaxResult para devolver los datos cuando se necesita devolver el formato de datos unificado. Sin embargo, en un proyecto donde muchas personas trabajarán juntas, existirá la posibilidad de olvidarse de llamar a la clase de formato de devolución de datos unificados, por lo tanto, es necesario crear una clase de garantía que detecte si la devolución de datos unificados se realiza antes de devolver el datos.

        La clase de garantía que realiza el retorno de datos unificado puede detectar si el tipo de datos es un objeto unificado antes de devolver los datos.Si no es un formato de retorno de datos unificado, se encapsulará en un formato de datos unificado

Cree una clase de garantía para la devolución de datos unificados: ResponseAdvice

Cree una clase de garantía ResponseAdvice en la clase de configuración del sistema

package com.example.demo.config;

import com.example.demo.commer.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;
    }

   /*
   * 对数据格式进行封装和效验
   * */
    @Override
    @SneakyThrows // 抛出异常的注解
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(body instanceof AjaxResult){
            return body;
        }
        if(body instanceof String ){
                return objectMapper.writeValueAsString(AjaxResult.success(body));
        }
        return AjaxResult.success(body);
    }
}

Procesamiento unificado

La función del procesamiento unificado es realizar un manejo de excepciones unificado, un formato de devolución de datos unificado y una interceptación de verificación de inicio de sesión unificado.

interceptor de inicio de sesión

El interceptor de inicio de sesión necesita implementar la interfaz HandlerInterceptor y reescribir el método preHandle

Cree una clase de interceptación de inicio de sesión LoginInterceptor en el paquete de configuración

package com.example.demo.config;

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

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

/*
* 登录拦截器
* */
public class LoginInterceptor implements HandlerInterceptor {

    /*
    * true -> 用户已登录
    * false -> 用户未登录
    * */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        if(session != null && session.getAttribute(AppVariable.USER_SESSION_KEY) != null){
            // 用户已登录
            return true;
        }
            // 用户未登录 需要跳转到登录界面
        response.sendRedirect("/login.html");
        return false;
    }
}

Configurar reglas de interceptación

Después de escribir el interceptor de inicio de sesión, debe establecer las reglas de interceptación para explicar cuáles deben interceptarse y cuáles no.

Cree una clase de regla de interceptación de configuración AppConfig en el paquete de configuración,   implemente la interfaz WebMvcConfigurer y reescriba el método addInterceptors

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 LoginInterceptor())
               .addPathPatterns("/**") // 拦截所有接口
               .excludePathPatterns("/css/**")  // 不拦截css文件下所有目录
               .excludePathPatterns("/editor.md/**") // 不拦截editor.md文件下所有目录
               .excludePathPatterns("/img/**") // 不拦截img文件下所有目录
               .excludePathPatterns("/js/**") // 不拦截js文件下所有目录
               .excludePathPatterns("/login.html") // 不拦截 登录页面 文件下所有目录
               .excludePathPatterns("/reg.html") // 不拦截 注册页面 文件下所有目录
               //.excludePathPatterns("/blog_list.html") //不拦截 博客所有人 文件下所有目录
               .excludePathPatterns("/blog_content.html") // 不拦截 博客列表 文件下所有目录
               .excludePathPatterns("/art/detail")  // 不拦截 文章详情页面
               .excludePathPatterns("/art/listbypage") // 不拦截 文章主页分页页面
               .excludePathPatterns("/art/incr-rcount") // 不拦截阅读文章量加1
               .excludePathPatterns("/user/login") //不拦截登录接口
               .excludePathPatterns("/user/getuserbyid") // 不拦截查询文章详情页的用户id
               .excludePathPatterns("/user/reg"); // 不拦截注册接口

    }
}

Realizar la función de registro de usuario

La implementación de la función de registro de usuario consta de dos pasos:

1. El usuario front-end envía una solicitud ajax al back-end

2. El backend recibe el frontend y devuelve datos ajax para su juicio

Paso 1: escriba el código de front-end para enviar la solicitud ajax

Escriba la validación y solicite el código ajax en la página reg.html del front-end

<script>
        // 提交注册事件 使用jQuery
        function mysub(){
        //1.非空效验
            var username = jQuery("#username");
            var password = jQuery("#password");
            var password2 = jQuery("#password2");
            if(username.val() ==""){
                alert("请先输入用户名");
                return;
            }
            if(password.val() ==""){
                alert("请先输入密码");
                 return;
            }
            if(password2.val() ==""){
                 alert("请先输入确认密码");
                 return;
            }
        //2.判断两次密码是否一致
        if(password.val() !=password2.val()){
                alert("两次输入密码不一致,请检查");
                return;
        }

        //3.ajax 提交请求
        jQuery.ajax({
            url:"/user/reg",
            method:"POST",
            data:{"username":username.val(),"password":password.val()},
            success:function(result){
                //响应的结果
                if(result != null && result.code == 200 && result.data == 1){
                        //执行成功
                       if(confirm("注册成功,是否跳转到登录界面?")){
                            location.href="/login.html";
                       }
                } else{
                        //执行失败
                        alert("注册失败,请稍后再试");
                }
            }
        });
    }
        
    </script>

Paso 2: El backend recibe los datos ajax devueltos por el frontend para su juicio

entidad de clase de entidad

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;
}

Mapeador de capa de persistencia de datos

package com.example.demo.mapper;

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

@Mapper
public interface UserMapper {

    // 传递实体类对象,方便后续维护参数的修改
    // 注册
    public int reg(Userinfo userinfo);

}

servicio de capa de servicio

package com.example.demo.service;

import com.example.demo.entity.Userinfo;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public  int reg(Userinfo userinfo){
        return userMapper.reg(userinfo);
    }
}

Controlador de capa de control

package com.example.demo.controller;

import com.example.demo.commer.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){
        // 非空效验,判断前端返回参数是否非空及有效性效验

        //若前端返回Ajax参数为空则返回Ajax失败参数
        if(userinfo == null || !StringUtils.hasLength(userinfo.getUsername()) ||
                !StringUtils.hasLength(userinfo.getPassword()) ){
            return AjaxResult.fail(-1,"非法参数");
        }
        //返回ajax成功参数
        return AjaxResult.success(userService.reg(userinfo));
    }
}

Declaración de inserción del archivo de configuración de MyBatis

UserMapping.xml

<?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>

Verifique que la función de registro se pueda implementar de acuerdo con nuestros requisitos, ejecute el proyecto e ingrese http://localhost:8080/reg.html para acceder a nuestra interfaz de registro

Cuando el registro es exitoso, puede elegir si saltar a la interfaz de inicio de sesión. En este momento, consulte la base de datos para ver si ya hay datos insertados en la base de datos.

 Implementar la función de inicio de sesión de usuario

        La lógica de la función de inicio de sesión del usuario es similar a la de la función de registro. El front-end envía la solicitud de datos ajax al back-end primero y luego realiza la validación. El back-end realiza una consulta a la base de datos para determinar si el usuario nombre o contraseña existe y es correcto.

El front-end envía el código Ajax

<script>
        function mysub(){
            // 1、非空效验
            var username = jQuery("#username");
            var password = jQuery("#password");
            if(username == null){
                alert("请输入用户名");
                username.focus();
                return;
            }
            if(password == null){
                alert("请输入密码");
                username.focus();
                return;
            }
            // 2、ajax请求登录接口
            jQuery.ajax({
                url:"/user/login",
                method:"POST",
                data:{"username":username.val(),"password":password.val()},
                success:function(result){
                    if(result != null && result.code == 200 && result.data != null){
                        // 登录成功则将跳转到博客主界面
                        location.href ="myblog_list.html";
                    }else{
                        alert("登录失败!用户名或密码错误,请重新输入");
                    }
                }
            });
        }
    </script>

Código de proceso de verificación de backend

En el proceso de inicio de sesión de autenticación de backend, si el inicio de sesión es exitoso, el usuario debe almacenarse en la sesión. En este momento, primero se define una variable global para representar la sesión del usuario.

Definir la clase de variable global AppVariable

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

Mapeador de capa de persistencia de datos

Consulta el objeto de usuario de acuerdo con el nombre de usuario y devuélvelo al servicio de la capa de servicio para llamar

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

Archivo de configuración de base de datos mapper.xml

De acuerdo con el nombre de usuario ajax pasado por la interfaz, consulte si el nombre de usuario existe en la base de datos

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

servicio de capa de servicio

La capa de servicio obtiene el nombre de usuario pasado por la capa de persistencia de datos y lo devuelve al controlador de la capa de control.

 //登录方法
    public Userinfo getUsername(String username){
        return userMapper.getUsername(username);
    }

controlador de capa de control

1. La capa de control primero realiza una validación no vacía en los datos ajax del front-end,

2. Obtenga los datos del nombre de usuario pasados ​​por el front-end y luego vaya a la base de datos para verificar si el nombre de usuario y la contraseña existen.

3. Compare la contraseña consultada desde la base de datos con la contraseña devuelta por el front-end.Si son diferentes, configure los datos como nulos y devuélvalos al front-end

4. Si la comparación es la misma, almacene la información del usuario en la sesión para guardar el registro , luego configure la contraseña para que esté vacía (oculte la información confidencial para garantizar la seguridad de la información) y devuelva los datos al front-end

 // 登录效验
    @RequestMapping("/login")
    public AjaxResult login(HttpServletRequest request,String username, String password){
        // 1、非空效验
        //若前端返回Ajax参数为空则返回Ajax失败参数
        if(!StringUtils.hasLength(username)  || !StringUtils.hasLength(password)){
            return AjaxResult.fail(-1,"非法参数");
        }
        // 2、查询数据库
        Userinfo userinfo = userService.getUsername(username);
        if(userinfo != null && userinfo.getId() > 0){
            // 有效的用户
            // 判断两个密码是否相同
            if(password.equals(userinfo.getPassword())){
                //登录成功
                // 将用户存储在session中
                HttpSession session = request.getSession();
                session.setAttribute(AppVariable.USER_SESSION_KEY,userinfo);
                //返回前端之前,隐藏密码等敏感信息
                userinfo.setPassword("");
                return AjaxResult.success(userinfo);
            }
        }
        return AjaxResult.success(0,null);
    }

Ejecute el proyecto e ingrese a la interfaz de inicio de sesión para verificar el efecto. El nombre de usuario registrado anteriormente es Zhang San y la contraseña es 123. Si el inicio de sesión es exitoso, saltará a la interfaz principal de la lista de blogs.

 

función de información personal

        En la información personal, puede consultar la última cantidad de artículos y la cantidad de categorías de la lista de artículos, eliminar artículos y cerrar la sesión de los usuarios. La premisa para realizar estas funciones es que el front-end debe enviar una solicitud ajax al back-end para obtener los datos.

Obtener la función de número de artículos

Obtenga el número de artículos del código de front-end

<script>
        // 获取最新文章数量
        function showInfo(){
            jQuery.ajax({
                url:"/user/showinfo",
                method:"POST",
                data:{},
                success:function(result){
                    if(result != null && result.code == 200){
                        jQuery("#username").text(result.data.username); // 用户名id
                        jQuery("#artCount").text(result.data.artCount); // 文章数量
                    }else{
                        alert("个人信息加载失败,请重新刷新");
                    }
                }
            });
        }
        showInfo();
    </script>

Obtener el número de artículos de código de fondo

Cree una clase bajo el commer de clase pública para registrar la sesión del usuario actualmente conectado

package com.example.demo.commer;

import com.example.demo.entity.Userinfo;

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

/**
 * 当前用户登录相关的操作
 */
public class UserSessionUtils {
    /**
     * 得到当前的登录用户
     */
    public static Userinfo getUser(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;
    }
}

Paquete de clase de entidad vo

Cree un nuevo paquete de entidad vo y cree una clase de entidad UserinfoVO bajo el paquete, que hereda de la clase de entidad Userinfo

Agregar un nuevo atributo de entidad: el número total de artículos publicados por esta persona

package com.example.demo.entity.vo;

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

@Data
public class UserinfoVO extends Userinfo {

    private Integer artCount; // 此人发表的文章总数

}

ArticleMapper de capa de persistencia de datos

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);
}

Capa de servicio ArticleService

package com.example.demo.service;

import com.example.demo.mapper.ArticleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    public int getArtCountByUid(Integer uid){
        return articleMapper.getArtCountByUid(uid);
    }
}

controlador de capa de control

// 获取用户id
    @RequestMapping("/showinfo")
    public AjaxResult showinfo(HttpServletRequest request){
        UserinfoVO userinfoVO = new UserinfoVO();
        // 1.得到当前的登录用户 (从session中获取)
        Userinfo userinfo = UserSessionUtils.getUser(request);
        if(userinfo == null){
            return AjaxResult.fail(-1,"非法请求");
        }
        // Spring 提供的深拷贝方法
        BeanUtils.copyProperties(userinfo,userinfoVO);

        // 2.得到用户发表文章的总数
        userinfoVO.setArtCount(articleService.getArtCountByUid(userinfo.getId()));
        return AjaxResult.success(userinfoVO);
    }

Archivo de configuración de base de datos ArticleMapper.xml

<?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>

Darse cuenta del efecto:

 

Obtenga la función de cantidad de clasificación de la lista de artículos

Código front-end para obtener el número de categorías en la lista de artículos

// 获取我的文章列表数量
        function getMyArtList(){
                jQuery.ajax({
                    url:"/art/myList",
                    method:"post",
                    data:{},
                    success:function(result){
                        if(result != null && result.code == 200){
                            // 有两种情况:发表了文章和从未发表过任何文章
                            if(result.data != null && result.data.length > 0){
                                // 此用户发表了文章
                                var artListDiv = "";
                                for(var i=0; i< result.data.length;i++){
                                    var artItem = result.data[i];
                                    artListDiv += '<div class="blog">';
                                    artListDiv += '<div class="title">' + artItem.title + '</div>';
                                    artListDiv += '<div class="date">' + artItem.createtime + '</div>';
                                    artListDiv += '<div class="desc">';
                                    artListDiv += artItem.content;
                                    artListDiv += '</div>';
                                    artListDiv += '<a href="blog_content.html?id=' + artItem.id + '" class="detail">查看全文 &gt;&gt;</a>';
                                    artListDiv += '<a href="blog_edit.html?id=' + artItem.id + '" class="detail">修改 &gt;&gt;</a>';
                                    artListDiv += '<a href="javascript:myDel(' + artItem.id + ');" class="detail">删除 &gt;&gt;</a>';
                                    artListDiv += '</div>';
                                }
                                jQuery("#artDiv").html(artListDiv);
                            }else{
                                  // 当前用户从未发表任何文章
                                  jQuery("#artDiv").html("<h3>暂无文章</h3>");
                            }
                        }else{
                          
                            alert("查询文章列表出错,请重试")
                        }
                    }
                });
        }
        getMyArtList();

Obtenga el código de back-end de la cantidad de clasificación de la lista de artículos

Crear una clase de entidad de clasificación de lista de artículos Articleinfo

package com.example.demo.entity;

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

import java.time.LocalDateTime;

@Data
public class Articleinfo {
    /**
     * 文章列表实体属性
     */
    private  Integer id;
    private String title;
    private String content;
    // 设置时间格式化
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    private LocalDateTime createtime;
    // 设置时间格式化
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    private LocalDateTime updatetime;
    private Integer uid;
    private Integer rcount;
    private Integer state;
}

Capa de control ArticleController

package com.example.demo.controller;

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

@RequestMapping("/art")
@RestController
public class ArticleController {

    @Autowired
    public ArticleService articleService;

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

Darse cuenta del efecto:

Eliminar función de artículo

Eliminar el código del front-end del artículo

<script> 
// 删除文章
        function myDel(id){
            if(confirm("确定是否需要删除")){
                jQuery.ajax({
                    url:"art/del",
                    method:"post",
                    data:{"id":id},
                    success:function(result){
                        if(result != null && result.code == 200 && result.data == 1){
                            alert("删除成功");
                            // 刷新页面
                            location.href = location.href;
                        }else{
                            alert("删除失败");
                        }
                    }
                });
            }
        }
</script>

Eliminar el código de fondo del artículo

Configuración de ArticleMapper.xml

<!--    删除文章-->
    <delete id="del">
        delete from articleinfo where id = #{id} and uid = #{uid}
    </delete>

ArticleMapper de capa de persistencia de datos

 //  删除文章
   int del(@Param("id") Integer id,@Param("uid") Integer uid);

Capa de servicio ArticleService

 // 删除文章
    public int del(Integer id,Integer uid){
        return articleMapper.del(id,uid);
    }

Capa de control ArticleController

//删除文章
    @RequestMapping("/del")
    public AjaxResult del(HttpServletRequest request,Integer id){
        if(id == null || id <= 0 ){
            // 参数有误
            return  AjaxResult.fail(-1,"参数有误");
        }
        Userinfo user = UserSessionUtils.getUser(request);
        if(user == null){
            return  AjaxResult.fail(-2,"用户未登录");
        }
        return AjaxResult.success(articleService.del(id, user.getId()));
    }

función de cierre de sesión

Implementación del código frontal de la función de cierre de sesión

 // 退出登录,实现注销功能
        function logout(){
            if(confirm("确认是否注销?")){
                jQuery.ajax({
                    url:"/user/logout",
                    method:"post",
                    data:{},
                    success:function(result){
                        if(result != null && result.code == 200){
                            location.href = "/login.html";
                        }
                    }

                });
            }
        }

Implementación del código de fondo de la función de cierre de sesión

Capa de control UserController

 // 注销 (退出登录)功能
    @RequestMapping("/logout")
    public  AjaxResult logout(HttpSession session){
        session.removeAttribute(AppVariable.USER_SESSION_KEY);
        return AjaxResult.success(1);
    }

Efecto de logro: haga clic en el botón de cierre de sesión para saltar automáticamente a la interfaz de inicio de sesión 

Ver la función de página de detalles y realizar la función de volumen de lectura

La función de ver la página de detalles incluye darse cuenta de la cantidad de artículos del usuario y agregar 1 al volumen de lectura

La realización de la función de visualización de la página de detalles incluye principalmente los siguientes pasos:

1. Obtenga la identificación del artículo de rurl

2. Consulta los detalles del artículo actual y el uid del usuario desde el backend

3. Consulta la información del usuario según el uid consultado en el paso anterior

4. Solicite la interfaz de back-end para lograr el volumen de lectura +1

Consulta de información detallada y código de front-end de volumen de lectura

    <script> 
 // 获取当前URL参数的公共方法
            function getUrlValue(key){
                var params = location.search;
                if(params.length > 1){
                    params = location.search.substring(1);
                    var paramArr = params.split("&");
                    for(var i=0;i<paramArr.length;i++){
                        var kv = paramArr[i].split("=");
                        if(kv[0] == key){
                            return kv[1];
                        }
                    }
                   
                } 
                return "";
            }
            // 查询文章详情
            function getArtDetail(id){
                if(id == ""){
                    alert("非法参数");
                    return;
                }
                jQuery.ajax({
                    url:"art/detail",
                    method:"post",
                    data:{"id":id},
                success:function(result){
                    if(result != null && result.code == 200){
                        jQuery("#title").html(result.data.title);
                        jQuery("#updatetime").html(result.data.updatetime);
                        jQuery("#rcount").html(result.data.rcount);
                        // 调用文本编译器来显示正文信息
                        initEdit(result.data.content);
                        // 得到用户的id
                        showUser(result.data.uid);
                    }else{
                        alert("查询失败,请重试!");
                    }
                }
                });
            }
            getArtDetail(getUrlValue("id"));
           
            // 查询用户的详情信息
            function showUser(uid){
                jQuery.ajax({
                    url:"/user/getuserbyid",
                    method:"post",
                    data:{"id":id},
                    success:function(result){
                        if(result != null && result.data.id > 0 && result.code == 200){
                            jQuery("#username").text(result.data.username);
                            jQuery("#artCount").text(result.data.artCount);
                        }else{
                            alert("查询用户失败");
                        }
                    }
                });
            }

            // 阅读量加1
            function updateRCount(){
                // 先得到文章 id
                var id = getUrlValue("id");
                if(id != ""){
                     jQuery.ajax({
                        url:"/art/incr-rcount",
                        method:"post",
                        data:{"id":id},
                        success:function(result){}
                     });
                }
               
            }
            updateRCount()
            
    </script> 

Consultar información detallada y leer el código de back-end del volumen

ArticleMapper de capa de persistencia de datos

   // 文章详情页
    Articleinfo getDetail(@Param("id") Integer id);
    // 实现阅读量加1
    int incrmentRCount(@Param("id") Integer id);

Información de configuración de ArticleMapper.xml

<!--    查询文章详情页面信息-->
    <select id="getDetail" resultType="com.example.demo.entity.Articleinfo">
        select * from articleinfo where id=#{id}
    </select>
<!--    修改阅读量加1-->
    <update id="incrmentRCount">
        update articleinfo set rcount = rcount+1 where id=#{id}
    </update>

Capa de servicio ArticleService

 //  查询文章详情
    public Articleinfo getDetail(Integer id){
        return articleMapper.getDetail(id);
    }
    // 实现阅读量加1
    public int incrmentRCount (Integer id){
        return articleMapper.incrmentRCount(id);
    }

Capa de control ArticleController

// 查询文章详情信息
    @RequestMapping("/detail")
    public AjaxResult getDetail(Integer id){
        if(id == null && id <= 0){
            return AjaxResult.fail(-1,"非法参数");
        }
        return AjaxResult.success(articleService.getDetail(id));
    }

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

Darse cuenta del efecto:

Haga clic en el botón Ver texto completo para ingresar a la página de detalles del artículo, ver los detalles del artículo y el volumen de lectura del artículo

Agregar función de artículo

Código JS frontal

<script>
        var editor;
        function initEdit(md){
            // 编辑器设置
            editor = editormd("editorDiv", {
                // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.
                width: "100%",
                // 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度
                height: "calc(100% - 50px)",
                // 编辑器中的初始内容
                markdown: md,
                // 指定 editor.md 依赖的插件路径
                path: "editor.md/lib/",
                saveHTMLToTextarea: true //
            });
        }
        initEdit("# 在这里写下一篇博客"); // 初始化编译器的值
        // 提交
        function mysub(){
            if(comfirm("确认提交?")){
                // 1、非空效验
                var title = jQuery("#title");
                if(title.val() == ""){
                    alert("请先输入标题!");
                    title.focus();
                    return;
                }
                if(editor.getValue() == ""){
                    alert("请先输入正文!");
                 
                    return;
                }
              //2、请求后端进行博客添加操作
                jQuery.ajax({
                    url:"/art/add",
                    method:"post",
                    data:{"title":title.val(),"content":editor.getValue()},
                    success:function(result){
                        if(result!= null && result.code == 200 && result.data ==1){
                            if(confirm("文章添加成功! 是否继续添加文章?")){
                                // 刷新当前页面
                                location.href = location.href;
                            }else{
                                location.href= "myblog_list.html";
                            }
                        }else{
                            alert("文章添加失败!");
                        }
                    }
                });
                 alert(editor.getValue()); // 获取值
            // editor.setValue("#123") // 设置值
            }
            
        }

</script>

Código de implementación de back-end

archivo de configuración de la base de datos

<!--    实现添加文章功能-->
    <insert id="add">
        insert into articleinfo(title,content,uid) values(#{title},#{content},#{uid})
    </insert>

capa de persistencia de datos

 // 实现添加文章
    int add(Articleinfo articleinfo);

capa de servicio

 // 实现添加文章功能
    public int add(Articleinfo articleinfo){
        return articleMapper.add(articleinfo);
    }

capa de control

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

Darse cuenta del efecto:

Modificar la función del artículo

código de front-end

<script>
        // 文章 id
        var id ="";

        var editor;
// 获取当前URL参数的公共方法
         function getUrlValue(key){
                var params = location.search;
                if(params.length > 1){
                    params = location.search.substring(1);
                    var paramArr = params.split("&");
                    for(var i=0;i<paramArr.length;i++){
                        var kv = paramArr[i].split("=");
                        if(kv[0] == key){
                            return kv[1];
                        }
                    }
                   
                } 
                return "";
            } 
        function initEdit(md){
            // 编辑器设置
            editor = editormd("editorDiv", {
                // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. 
                width: "100%",
                // 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度
                height: "calc(100% - 50px)",
                // 编辑器中的初始内容
                markdown: md,
                // 指定 editor.md 依赖的插件路径
                path: "editor.md/lib/",
                saveHTMLToTextarea: true // 
            });
        }
        // 提交
        function mysub(){
            // 1.非空效验
            var title = jQuery("#title");
            if(title.val()==""){
                alert("请先输入标题!");
                title.focus();
                return;
            }
            if(editor.getValue()==""){
                alert("请先输入正文!");
                return;
            }
            // 2.进行修改操作
            jQuery.ajax({
                url:"/art/update",
                type:"POST",
                data:{"id":id,"title":title.val(),"content":editor.getValue()},
                success:function(result){
                    if(result!=null && result.code==200 && result.data==1){
                        alert("恭喜:修改成功!");
                        location.href = "myblog_list.html";
                    }else{
                        alert("抱歉:操作失败,请重试!");
                    }
                }
            });
        }
        // 文章初始化
        function initArt(){
            // 得到当前页面 url 中的参数 id(文章id)
            id = getUrlValue("id");
            if(id==""){
                alert("无效参数");
                location.href = "myblog_list.html";
                return;
            }
            // 请求后端,查询文章的详情信息
            jQuery.ajax({
                url:"art/detail",
                type:"POST",
                data:{"id":id},
                success:function(result){
                    if(result!=null && result.code==200){
                        jQuery("#title").val(result.data.title);
                        initEdit(result.data.content);
                    }else{
                        alert("查询失败,请重试!");  
                    }
                }
            });
        }
        initArt();
 
 // 退出登录,实现注销功能
         function logout(){
            if(confirm("确认是否注销?")){
                jQuery.ajax({
                    url:"/user/logout",
                    method:"post",
                    data:{},
                    success:function(result){
                        if(result != null && result.code == 200){
                            location.href = "/login.html";
                        }
                    }

                });
            }
        }
    </script>

código de fondo

Capa de control ArticleController

 // 实现修改文章功能
    @RequestMapping("/update")
    public AjaxResult update(HttpServletRequest request, Articleinfo articleinfo) {
        // 非空效验
        if (articleinfo == null && !StringUtils.hasLength(articleinfo.getTitle()) ||
                !StringUtils.hasLength(articleinfo.getContent()) || articleinfo.getId() == null) {
            // 非法参数
            return AjaxResult.fail(-1, "非法参数");
        }
        // 得到当前登录用户的id
        Userinfo userinfo = UserSessionUtils.getUser(request);
        if (userinfo == null && userinfo.getId() == null) {
            // 无效登录用户
            return AjaxResult.fail(-2, "无效用户");
        }
        // 核心代码:解决了修改文章归属人的问题
        articleinfo.setUid(userinfo.getId());
        return AjaxResult.success(articleService.update(articleinfo));
    }

lograr efecto

Función de lista de artículos de consulta de paginación

código de fondo

ArticleMapper de capa de persistencia de datos

// 实现文章分页
    // psize 为每页显示条数 offsize 为分数页数
    List<Articleinfo> getListByPage(@Param("psize") Integer psize,@Param("offsize") Integer offsize);

Archivo de configuración de base de datos ArticleMapper.xml

El punto principal a tener en cuenta: al implementar la función de paginación del artículo, use la declaración de consulta sql: seleccione * del límite de información del artículo A desplazamiento B;

<!--    实现文章分页功能-->
    <select id="getListByPage" resultType="com.example.demo.entity.Articleinfo">
        select * from articleinfo limit #{psize} offset #{offsize}
    </select>

Capa de servicio ArticleService

 // 实现文章分页功能
    public List<Articleinfo> getListByPage(Integer psize,Integer offsize){
        return articleMapper.getListByPage(psize, offsize);
    }

Capa de control ArticleController

 /**
     * // 实现文章的分页功能
     * @param pindex 当前页码(从1开始)
     * @param psize 每页显示条数
     * @return
     */
    @RequestMapping("/listbypage")
    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);
    }

código de front-end

 <script>
 // 获取当前URL参数的公共方法
         function getUrlValue(key){
                var params = location.search;
                if(params.length > 1){
                    params = location.search.substring(1);
                    var paramArr = params.split("&");
                    for(var i=0;i<paramArr.length;i++){
                        var kv = paramArr[i].split("=");
                        if(kv[0] == key){
                            return kv[1];
                        }
                    }
                   
                } 
                return "";
            } 
        // 当前页码
        var pindex = 1;
        // 每页显示条数
        var psize = 4;
        // 最大页数
        var pcount =1;
        // 1.先尝试得到当前 url 中的页码
        pindex = (getUrlValue("pindex")==""?1:getUrlValue("pindex"));
        // 2.查询后端接口得到当前页面的数据,进行展示
        function initPage(){
           jQuery.ajax({
            url:"/art/listbypage",
            type:"POST",
            data:{"pindex":pindex,"psize":psize},
            success:function(result){
                if(result!=null && result.code==200 && result.data.list.length>0){
                    var artListHtml = "";
                    for(var i=0;i<result.data.list.length;i++){
                        var articleinfo = result.data.list[i];
                        artListHtml +='<div class="blog">';
                        artListHtml +='<div class="title">'+articleinfo.title+'</div>';
                        artListHtml +='<div class="date">'+articleinfo.updatetime+'</div>';
                        artListHtml +='<div class="desc">'+articleinfo.content+'</div>';
                        artListHtml +='<a href="blog_content.html?id='+ articleinfo.id
                            +'" class="detail">查看全文 &gt;&gt;</a>';
                        artListHtml +='</div>';    
                    }
                    jQuery("#artListDiv").html(artListHtml);
                    pcount = result.data.pcount;
                }
            }
           });     
        }
        initPage();
        // 跳转到首页
        function goFirstPage(){
            if(pindex<=1){
                alert("当前已经在首页了");
                return;
            }
            location.href = "blog_list.html";
        }
        // 点击上一页按钮
        function goBeforePage(){
            if(pindex<=1){
                alert("当前已经在首页了");
                return;
            }
            pindex = parseInt(pindex) -1;
            location.href ="blog_list.html?pindex="+pindex;
        }
        function goNextPage(){
            if(pindex>=pcount){
               alert("已经在末页了");
               return; 
            }
            pindex = parseInt(pindex)+1;
            location.href ="blog_list.html?pindex="+pindex;
        }
        function goLastPage(){
            if(pindex>=pcount){
               alert("已经在末页了");
               return; 
            }
            location.href ="blog_list.html?pindex="+pcount;
        }
    </script>

Darse cuenta del efecto:

 

Cifrado de sal de contraseña

1. Almacenamiento de contraseña de texto sin formato original

Al iniciar sesión o registrarse con una contraseña de texto sin formato, también se almacena en texto sin formato cuando se almacena en la base de datos, lo que tiene cierto riesgo de fuga de contraseña. Porque cuando los delincuentes obtengan la base de datos que almacena las contraseñas, se filtrará nuestra privacidad. Por lo tanto, es necesario considerar cifrar la contraseña del usuario y, después de cifrar la contraseña del usuario, almacenar la contraseña cifrada en la base de datos.

2. Método de cifrado tradicional: MD5

El cifrado MD5 consiste en utilizar un algoritmo para cifrar la contraseña, generar un resultado de contraseña de 128 bits y obtener la contraseña cifrada. El cifrado MD5 es irreversible, es decir, el algoritmo de cifrado no se puede utilizar para descifrar la contraseña cifrada para obtener la contraseña original, por lo que es irreversible.

Desventajas: aunque el algoritmo de encriptación MD5 es irreversible, la cadena encriptada es fija y hay ciertas reglas a seguir , y aún existe el riesgo de fuga de contraseña.

          Porque: solo necesita saber de antemano la contraseña encriptada correspondiente a la contraseña de texto sin formato usando el cifrado MD5 para comparar la contraseña encriptada una por una y usar el craqueo de fuerza bruta, para que pueda conocer la contraseña sin cifrar y registrar la contraseña sin cifrar y la contraseña encriptada como esta La tabla mapeada se llama tabla arcoíris (una tabla de comparación MD5 que registra casi todas las cadenas).

3. Algoritmo de cifrado de sal

Agregar sal es usar un conjunto de cadenas aleatorias, que el sistema genera aleatoriamente, y las cadenas se insertan aleatoriamente en cualquier posición de la contraseña de texto sin formato, y no hay reglas en absoluto. Luego use MD5 para cifrar la contraseña de texto sin formato con sal y luego almacene la contraseña cifrada en la base de datos. El uso del algoritmo de cifrado de sal aumentará en gran medida la dificultad de descifrar y el costo de descifrar.

Código implementado manualmente usando encriptación salt

package com.example.demo.commer;

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


import java.util.UUID;
/*
* 加盐算法的实现
* */
public class PasswordUtils {
    // 1.加盐并生成密码,存储到数据库中
    public static String encrypt(String password){
        // a. 产生盐值(32位)
        String salt = UUID.randomUUID().toString().replace("-","");
        // b.生成加盐后的密码
        String saltPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes());
        // c.生成最后的加盐密码(保存在数据库中的密码)【约定格式:盐值+$+加盐密码】
        String finalPassword = salt +"$" +saltPassword;

        return finalPassword;
    }

    // 2.生成加盐密码并进行验证数据库密码(方法一的重载)
    public static String encrypt(String password,String salt){
        // a.使用盐值生成加密密码
        String saltPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes());
        // b.生成约定格式的加密密码
        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){
            // 1.得到盐值
            String salt = finalPassword.substring(0,32); // 截取最终密码前32位为盐值
            // 2.得到之前加密的步骤,将明文密码和已经得到的盐值进行加密,生成最终得到密码
            String confirmPassword = PasswordUtils.encrypt(inputPassword,salt);
            // 对比由盐值加明文生成的加盐密码和数据库密码是否匹配
            return confirmPassword.equals(finalPassword);
        }
        return false;
    }
}

En este punto, puede modificar la interfaz de registro y la interfaz de inicio de sesión para agregar el cifrado de sal para determinar la contraseña, de modo que la base de datos pueda almacenar la contraseña cifrada con sal.

El código modificado de la interfaz de inicio de sesión es el siguiente:

El código modificado de la interfaz de registro es el siguiente:

Darse cuenta del efecto:

Vuelva a registrar un nuevo usuario y vea la contraseña almacenada en la base de datos con cifrado de sal

 Verifique la contraseña almacenada de la base de datos. En este momento, la contraseña de Zhang San almacenada en la base de datos es la contraseña encriptada con sal. En la interfaz de inicio de sesión, vuelva a ingresar el nombre de usuario: Zhang San, contraseña: 123 e inicie sesión correctamente.

Algoritmo de cifrado salt método dos: usando el marco Spring Security 

Supongo que te gusta

Origin blog.csdn.net/qq_73471456/article/details/131432361
Recomendado
Clasificación