项目——博客系统

Posted 迷宫白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目——博客系统相关的知识,希望对你有一定的参考价值。

文章目录


前言

这个博客系统前端分为8个页面,分别是注册页,登录页,编辑页,修改页,个人主页,博客正文页,草稿列表页,博客列表页

项目优点

  1. 框架:使用ssm(SpringBoot + SpringMVC + MyBatis)
  2. 密码:用户登录用的密码是使用加盐算法处理然后存储到数据库
  3. 用户登录状态持久化:将session持久化到redis
  4. 功能升级:在博客列表页实现一个分页功能
  5. 使用拦截器进行用户的登录验证,统一数据返回格式,统一异常处理

项目创建

创建一个SpringBoot项目,添加需要的依赖

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.3.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

创建相应的目录,文件,表,导入前端资源

首先在数据库建表,这里直接提供sql语句

-- 创建数据库
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(32) 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';


-- 添加一个用户信息
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,state)
    values('喜讯','今天是正月初六',9,2);

  1. 实体层(model):创建实体类UserInfo,ArticleInfo
  2. 控制层(controller):创建控制器UserController和ArticleInfo
  3. 服务层(servlce):创建服务类:UserService和ArticleService
  4. 持久层(mapper):创建接口UserMapper和ArticleMapper,并创建对应的xml文件,在yml文件里配置好
  5. 工具层(common):统一返回类(ResponseAdvice,AjaxResult等)

注意:创建完相应的类就得将需要加的注解加上去,这里就不一一展示了
创建完后的目录结构

yml配置文件的内容

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

将前端的东西导入static

实现common工具类


由于工具类后面我们基本都会用到,所以这里先实现一下

实现拦截器验证用户登录

  1. 步骤1创建一个Constant类,定义session的key
  2. 步骤2:创建一个普通类实现HandlerInterceptor接口,重写preHandle
  3. 步骤3:创建一个普通类实现 WebMvcConfigurer接口,重写addInterceptors
public class Constant 
    //登录信息存储到session中的key值
    public static final String SESSION_USERINFO_KEY = "session_userinfo_key";


@Component
public class LoginInterceptor implements HandlerInterceptor 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
        //判断登录业务
        HttpSession session = request.getSession(false);//会根据请求发送来的sessionId去服务器找对应的会话
        if(session.getAttribute(Constant.SESSION_USERINFO_KEY)!=null) //根据key拿到value
            return true;
        
        response.setStatus(401);
        return false;
    


@Configuration
public class AppConfig implements WebMvcConfigurer 
    //不拦截的url
    List<String> excludes = new ArrayList<>() 
        add("/**/*.html");
        add("/js/**");
        add("/editor.md/**");
        add("/css/**");
        add("/img/**"); // 放行 static/img 下的所有文件
    ;

    @Autowired
    LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) 
        //配置拦截器
        InterceptorRegistration registration = registry.addInterceptor(loginInterceptor);
        registration.addPathPatterns("/**");
        registration.excludePathPatterns(excludes);
    

实现统一数据返回格式

步骤1:创建一个普通的类,实现业务成功返回的方法和业务失败返回的方法
步骤2:创建一个普通的类实现ResponseAdvice接口,重写supports方法和beforeBodyWrite方法
代码

public class AjaxResult 

    /**
     * 业务执行成功返回的方法
     * @param data
     * @return
     */
    public static HashMap<String,Object> success(Object data) 
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",200);
        result.put("msg","");
        result.put("data",data);
        return result;
    

    /**
     * 业务执行成功时返回的代码
     * @param data
     * @param msg
     * @return
     */
    public static HashMap<String,Object> success(Object data,String msg) 
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",200);
        result.put("msg",msg);
        result.put("data",data);
        return result;
    

    /**
     * 业务执行失败返回的方法
     * @param code
     * @param data
     * @param msg
     * @return
     */
    public static HashMap<String,Object> fail(int code,Object data,String msg) 
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",code);
        result.put("msg",msg);
        result.put("data",data);
        return result;
    

    /**
     * 业务执行失败返回的方法
     * @param code
     * @param msg
     * @return
     */
    public static HashMap<String,Object> fail(int code,String msg) 
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",code);
        result.put("msg",msg);
        result.put("data","");
        return result;
    

public class ResponseAdvice implements ResponseBodyAdvice 
    @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 HashMap) 
            return body;
        
        //如果body是字符串类型,需要特殊处理
        if(body instanceof String) 
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.writeValueAsString(AjaxResult.success(body));
        
        return AjaxResult.success(body);
    


实现加盐加密类

创建一个普通类,实现两个方法,encrypt方法和decrypt方法。

  • encrypt方法根据用户输入的密码,进行加盐加密,返回最终加密的密码
  • decrypt方法根据用户输入的密码和数据库存的加密的密码进行验证
public class SecurityUtil 

    /**
     * 对password进行加盐加密
     * @param password
     * @return
     */
    public static String encrypt(String password) 
        
    

    /**
     * 验证password和数据库拿出来的finalPassword进行验证
     * @param password
     * @param finalPassword
     * @return
     */
    public static String decrypt(String password,String finalPassword) 
        
    

实现encrypt方法

加盐思路:用UUID类生成一个32长度的字符串作为盐值,然后将盐值+password进行md5加密生成一个32长度的字符串,然后盐值+这个有md5加密的字符串,就是最终的结果

 /**
     * 对password进行加盐加密
     * @param password
     * @return
     */
    public static String encrypt(String password) 
        //每次生成内容不同,但是长度固定为32的字符串
        String salt = UUID.randomUUID().toString().replace("-","");
        //盐值+password进行md5加密
        String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes());
        //返回盐值+密码,总共64位,存到数据库中
        return salt+finalPassword;
    

实现decrypt方法

这个方法用来解密验证密码
思路:这个方法有两个参数password:待验证的密码,finalPassword:最终正确的密码(在数据库查询的密码),从finalPassword中提取出盐值,然后盐值+password进行md5加密生成一个字符串,然后盐值+字符串和finalPassword判断是否相等

/**
     * 验证password和数据库拿出来的finalPassword进行验证
     * password:待验证的密码
     * finalPassword:最终正确的密码(在数据库查询的密码)
     * @param password
     * @param finalPassword
     * @return
     */
    public static boolean decrypt(String password,String finalPassword) 
        //非空效验
        if(!StringUtils.hasLength(password) || !StringUtils.hasLength(finalPassword)) 
            return false;
        
        if(finalPassword.length()!=64) 
            return false;
        
        //提取出盐值
        String salt = finalPassword.substring(0,32);
        //使用盐值+密码生成一个32位的密码
        String securityPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes());
        //使用上一个32位的密码拼接上盐值进行密码验证
        return (salt+securityPassword).equals(finalPassword);
    

实现SessionUtil类

这个工具类用来查询当前用户登录的session信息

public class SessionUtil 
    
    public static UserInfo getLoginUser(HttpServletRequest request) 
        HttpSession session = request.getSession(false);
        if(session!=null && session.getAttribute(Constant.SESSION_USERINFO_KEY)!=null) 
            UserInfo userInfo = (UserInfo) session.getAttribute(Constant.SESSION_USERINFO_KEY);
            return userInfo;
        
        return null;
    


实现注册页面

这里开始就涉及到前后端交互了,要约定好前后端交互的接口

实现前端代码

那先来编写前端代码(打开reg.html进行编写代码)
记得要引入jquery

mysub方法

function mysub() 
            //1.非空效验
            var username = jQuery("#username");
            var password = jQuery("#password");
            var password2 = jQuery("#password2");
            if(username.val()=="") 
                alert("用户名为空");
                username.focus();
                return false;
            
            if(password.val()=="") 
                alert("密码为空");
                password.focus();
                return false;
            
            if(password2.val()=="") 
                alert("确认密码为空");
                password2.focus();
                return false;
            
            if(password.val()!=password2.val()) 
                alert("两次输入的密码不一致");
                return false;
            
            //2.发送ajax请求
            jQuery.ajax(以上是关于项目——博客系统的主要内容,如果未能解决你的问题,请参考以下文章

Java项目:学生管理系统(java+Springboot+Maven+mybatis+Vue+Mysql)

Java项目:博客论坛管理系统设计和实现(java+springboot+jsp+mysql+Spring)

Java项目:学生选课管理系统(java+SpringBoot+Mybatis-plus+Thymeleaf+mysql)

Java项目:图书借阅管理系统(java+SpringBoot+Mybatis+Html+maven+Mysql)

尝试前后端分离博客项目

基于JSP+Mybatis实现的CRM客户关系管理系统