用户登录注册模块的实现:【SpringBoot】JavaWeb博客系统——用户登录与注册
这是一个简易的博客系统,是这学期JavaWeb的期末项目,相对于常用的博客系统而已,还有很多功能尚未完善,例如没有独立的权限管理模块,点赞,收藏,归档,分类、数据统计等功能(这些没完成的部分等课设的应该会视情况而去完善)。该博客系统实现的主要功能看下面的项目介绍。
该博客系统前后大概花了十天左右的时间。说实话,写前端真的是一件令人头大的事,十天时间里面,有七天左右在写界面。而且在实现具体功能的时候,也总是在前端部分出现各种莫名奇妙的玄学Bug。
该博客系统主要有以下几个功能:
上述功能除了查阅博客详情,搜索博客以及留言板留言可以在游客状态(未登录状态)下进行访问,其余必须为登陆后的用户才有权限操作。
前端:Thymeleaf模板引擎、Semantic UI框架、Jquery框架以及editormd、simditor、cropper,prism,typo等插件
后端:Springboot、持久层采用Mybatis-Plus
数据库:MySQL
项目管理工具:Maven
留言模块的回复用如上图所示的层级关系展示,一条父留言为根节点,之后回复该留言,或者对该留言中的回复进行回复的留言都以兄弟节点的关系形式表示。所以上图的三条留言中,jiang留言(cid=1)的parentMessageId和replyMessageId都为null,因为它是作为根节点的。Jxj留言(cid=2)的parentMessageId和replyMessageId都为1,因为它回复的留言以及根留言都是(cid=1)这条留言。最后jiang4869的这条回复的parentMessageId和replyMessageId分别为(1,2),因为它回复的是jxj的留言,所以replyMessageId为2,它所在的根节点jiang的留言cid=1,所以parentMessageId=1。
用户登录界面
用户注册界面
登录成功后的首页
博客列表
博客编辑界面
博客详情界面
个人信息界面
个人博客管理界面
个人信息修改界面
头像上传界面
留言板
后台管理界面
pom文件的坐标
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.6.RELEASEversion>
<relativePath/>
parent>
<groupId>cn.jxj4869groupId>
<artifactId>blogartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>blogname>
<description>project for blogdescription>
<properties>
<java.version>1.8java.version>
<thymeleaf.version>3.0.11.RELEASEthymeleaf.version>
<thymeleaf-layout-dialect.version>2.1.1thymeleaf-layout-dialect.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.3.1.tmpversion>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.3.1version>
dependency>
<dependency>
<groupId>org.freemarkergroupId>
<artifactId>freemarkerartifactId>
<version>2.3.30version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.8version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>com.sun.jerseygroupId>
<artifactId>jersey-coreartifactId>
<version>1.18.1version>
dependency>
<dependency>
<groupId>com.sun.jerseygroupId>
<artifactId>jersey-clientartifactId>
<version>1.18.1version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.3.6version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpmimeartifactId>
<version>4.5version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpcoreartifactId>
<version>4.4.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
配置文件
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost4:3306/blog?serverTimezone=UTC&characterEncoding=utf8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
initialization-mode: always
# schema:
# - classpath:sql/user.sql
# - classpath:sql/employee.sql
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
#跨服务器上传文件的地址,与文件服务器的接口对应
file.upload.path.editormd=http://localhost:8888/fileupload/md/
file.upload.path.simditor=http://localhost:8888/fileupload/simditor/
file.upload.path=http://localhost:8888/fileupload/
#文件上传相关参数
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=300MB
spring.servlet.multipart.max-request-size=300MB
#邮件发送的配置
spring.mail.username=youremail
spring.mail.password=password
spring.mail.host=smtp.qq.com
#配置465端口,保证在服务器上正常使用
spring.mail.properties.mail.smtp.socketFactory.port=465
spring.mail.default-encoding=UTF-8
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.port=465
spring.mail.properties.mail.smtp.socketFactory.class = javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.smtp.socketFactory.fallback = false
#禁用thymeleaf缓存
spring.thymeleaf.cache=false
#开启对隐藏域方法的支持
spring.mvc.hiddenmethod.filter.enabled=true
配置类
DruidConfig.java
package cn.jxj4869.blog.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DruidConfig {
@ConfigurationProperties("spring.datasource")
@Bean
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
}
MybatisPlusConfig.java
package cn.jxj4869.blog.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
log4j配置文件
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=log/log.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
SQL脚本
create database blog;
use blog;
create table `blog_user`
(
`uid` int not null primary key auto_increment,
`userName` varchar(30) not null unique,
`nickName` nvarchar(100) not null,
`password` varchar(20) not null,
`email` nvarchar(100) unique not null,
`avatar` varchar(100) not null,
`qq` varchar(13) default null,
`createTime` datetime default now(),
`updateTime` datetime default now()
) charset = utf8;
create table `blog_message`
(
`mid` int not null primary key auto_increment,
`uid` int default null,
`createTime` datetime not null,
`content` nvarchar(500) not null,
`parentMessageId` int default null,
`replyMessageId` int default null,
`email` varchar(30) not null,
`userName` nvarchar(20) not null,
`remind` bool default false,
constraint foreign key (`uid`) references `blog_user` (`uid`) ON DELETE CASCADE ON UPDATE RESTRICT,
constraint foreign key (`parentMessageId`) references `blog_message` (`mid`) ON DELETE CASCADE ON UPDATE RESTRICT,
constraint foreign key (`replyMessageId`) references `blog_message` (`mid`) ON DELETE CASCADE ON UPDATE RESTRICT
) charset = utf8;
create table `blog_blogType`
(
`id` int not null primary key auto_increment,
`typeName` nvarchar(100) not null
) charset = utf8;
create table `blog_blog`
(
`bid` int not null primary key auto_increment,
`uid` int not null,
`title` nvarchar(200) not null,
`content` mediumblob default null,
`summary` nvarchar(300) not null,
`firstPicture` varchar(500) not null,
`views` int default 0,
`createTime` datetime default now(),
`updateTime` datetime default now(),
`published` bool default true,
`isComment` bool default true,
`typeId` int default null,
constraint foreign key (`typeId`) references `blog_blogType` (`id`),
constraint foreign key (`uid`) references `blog_user` (`uid`),
) charset = utf8;
create table `blog_comment`
(
`cid` int not null primary key auto_increment,
`uid` int not null,
`bid` int not null,
`createTime` datetime not null,
`content` nvarchar(500) not null,
`parentCommentId` int default null,
`replyCommentId` int default null,
`email` varchar(30) not null,
`remind` bool default false,
constraint foreign key (`uid`) references `blog_user` (`uid`) ON DELETE CASCADE ON UPDATE RESTRICT,
constraint foreign key (`parentCommentId`) references `blog_comment` (`cid`) ON DELETE CASCADE ON UPDATE RESTRICT,
constraint foreign key (`replyCommentId`) references `blog_comment` (`cid`) ON DELETE CASCADE ON UPDATE RESTRICT,
constraint foreign key (`bid`) references `blog_blog` (`bid`) ON DELETE CASCADE ON UPDATE RESTRICT
) charset = utf8;
create trigger tri_blogTypeDelete
before delete
on `blog_blogType`
for each row
begin
declare id int;
select bt.id into id from blog_blogType bt where bt.id = old.id;
update `blog_blog` set `typeid`=null where `blog_blog`.typeId = id;
end;
前端插件
均采用最新版本
部署在Linux系统下。
pom坐标
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.6.RELEASEversion>
<relativePath/>
parent>
<groupId>cn.jxj4869groupId>
<artifactId>fileuploadserver1artifactId>
<version>0.0.1-SNAPSHOTversion>
<name>fileuploadserver1name>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
配置文件
# 文件上传位置 这里是路径是相对于项目而言,可以根据实际情况更改
file.upload.save-path=/root/blog/upload/
#文件访问路径
file.upload.url=/uploads/**
file.save-path=./
server.port=8888
server.tomcat.basedir=/root/blog/upload
#文件大小设置
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=30MB
spring.servlet.multipart.max-request-size=100MB
#用于返回结果的参数
server.localhost=http://localhost:8888
资源映射代码
package cn.jxj4869.fileuploadserver1.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cglib.core.WeakCacheKey;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MySpringMvcConfig implements WebMvcConfigurer {
@Value("${file.upload.save-path}")
private String savePath;
@Value("${file.upload.url}")
private String url;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(url).addResourceLocations("file:"+savePath);
}
}
FileController
package cn.jxj4869.fileuploadserver1.controller;
import cn.jxj4869.fileuploadserver1.domain.Info;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.*;
@Controller
public class FileController {
@Value("${file.upload.save-path}")
private String savePath;
@Value("${server.localhost}")
private String localhost;
@RequestMapping("/fileupload")
@ResponseBody
private Info fileupload(HttpServletRequest request, @RequestParam("upload") MultipartFile[] uploads) throws IOException {
System.out.println("文件上传");
String path="/root/blog/upload";
File file = new File(path);
file.setWritable(true,false);
if (!file.exists()) {
file.mkdir();
}
List<String> list=new LinkedList<>();
String filepath = localhost + request.getContextPath() + "/uploads/";
for (MultipartFile upload : uploads) {
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
upload.transferTo(new File(path, filename));
list.add( filepath + filename);
}
System.out.println("aa");
Info info = new Info();
info.put("success", 1);
info.put("msg", "success");
info.put("url", list.toArray());
System.out.println(info.toString());
return info;
}
@RequestMapping("/fileupload/md")
@ResponseBody
private Info fileuploadMd(HttpServletRequest request, @RequestParam("upload") MultipartFile[] uploads) throws IOException {
System.out.println("文件上传");
String path="/root/blog/upload";
File file = new File(path);
file.setWritable(true,false);
if (!file.exists()) {
file.mkdir();
}
String filepath = localhost + request.getContextPath() + "/uploads/";
for (MultipartFile upload : uploads) {
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
upload.transferTo(new File(path, filename));
filepath = filepath + filename;
}
System.out.println("aa");
Info info = new Info();
info.put("success", 1);
info.put("msg", "success");
info.put("url", filepath);
System.out.println(info.toString());
return info;
}
@RequestMapping("/fileupload/simditor")
@ResponseBody
private Info simditor(HttpServletRequest request, @RequestParam("upload") MultipartFile[] uploads) throws IOException {
System.out.println("文件上传");
String path="/root/blog/upload";
File file = new File(path);
file.setWritable(true,false);
if (!file.exists()) {
file.mkdir();
}
List<String> list=new LinkedList<>();
String filepath = localhost + request.getContextPath() + "/uploads/";
for (MultipartFile upload : uploads) {
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
upload.transferTo(new File(path, filename));
list.add( filepath + filename);
}
System.out.println("aa");
Info info = new Info();
info.put("success", true);
info.put("msg", "success");
info.put("file_path", list.toArray());
System.out.println(info.toString());
return info;
}
}
源码已上传到 github。