基于SpringBoot和MySQL实现的论坛信息发布平台
1.项目简介
CISP 全称 Campus Information Sharing Platform(校内信息共享平台),这是本科毕业设计,一个类似于论坛的信息发布平台。
-
后端:SpringBoot
-
前端:LayUI框架 + freemarker动态模板
-
数据库使用MySQL
-
MVC三层架构
1.1 功能逻辑
1.1.1 注册
-
用户注册成功,将用户信息存入 MySQL,但此时该用户状态为未激活
-
向用户发送激活邮件,用户点击链接则激活账号(Spring Mail)
1.1.2 登录 | 登出
登录认证模块跳过了 Spring Secuity 自带的认证机制。主要逻辑如下:
-
进入登录界面,随机生成一个字符串来标识这个将要登录的用户,将这个字符串短暂的存入 Cookie(60 秒)
-
动态生成验证码,并将验证码及标识该用户的字符串短暂存入 Redis(60 秒)
-
为登录成功(验证用户名、密码、验证码)的用户随机生成登录凭证且设置状态为有效,并将登录凭证及其状态等信息永久存入 Redis,再在 Cookie 中存一份登录凭证
-
使用拦截器在所有的请求执行之前,从 Cookie 中获取登录凭证,只要 Redis 中该凭证有效并在有效期内,本次请求就会一直持有该用户信息(使用 ThreadLocal 持有用户信息,保证多台服务器上用户的登录状态同步)
-
勾选记住我,则延长 Cookie 中登录凭证的有效时间
-
用户登出,将凭证状态设为无效,并更新 Redis 中该登录凭证的相关信息
下图是登录模块的功能逻辑图,并没有使用 Spring Security 提供的认证逻辑(我觉得这个模块是最复杂的,这张图其实很多细节还没有画全):
1.1.3 显示评论及相关信息
评论部分前端的名称显示有些缺陷,有兴趣的小伙伴欢迎提 PR 解决 ~
关于评论模块需要注意的就是评论表的设计,把握其中字段的含义,才能透彻了解这个功能的逻辑。
评论 Comment 的目标类型(帖子,评论) entityType 和 entityId 以及对哪个用户进行评论/回复 targetId 是由前端传递给 DiscussPostController 的。
1.2 开发环境
-
操作系统 :Windows 10
-
构建工具 :Apache Maven
-
集成开发工具 :Intellij IDEA
-
应用服务器 :Apache Tomcat
-
接口测试工具 :Postman
-
压力测试工具 :Apache JMeter
-
版本控制工具 :Git
-
Java 版本 :8
2.数据库设计
2.1 表结构
article表
category表
comment表
user表
2.2 E-R图
3.项目开发
3.1 项目配置
```yaml
配置freemarker
spring: freemarker: # 设置模板后缀名 suffix: .html # 设置文档类型 content-type: text/html # 设置页面编码格式 charset: UTF-8 # 设置页面缓存 cache: false # 设置ftl文件路径 template-loader-path: - classpath:/templates # 设置静态文件路径,js,css等 mvc: static-path-pattern: /static/ resources: static-locations: ["/templates/","/static/"] http: encoding: charset: utf-8 force: true enabled: true #配置数据源 datasource: url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath :mapper/ .xml type-aliases-package: com.zbin.cisp.domain configuration: map-underscore-to-camel-case: true server: port: 8080 ```
3.2 拦截器实现
java
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
boolean flag;
if (request.getRequestURI().startsWith("/admin")
&& request.getSession().getAttribute("adminUser") == null) {
response.sendRedirect("/admin");
flag = false;
} else if (request.getSession().getAttribute("user") == null
&& request.getSession().getAttribute("adminUser") == null) {
response.sendRedirect("/login");
flag = false;
} else {
flag = true;
}
return flag;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { }
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { }
}
3.3 工具类
文件处理
java
public class FileUtil {
private static final String ACCESS_KEY = "pl7KvcAWGCe1eI2RPKKyrp7zxU_o8PM6rGAb7SG7";
private static final String SECRET_KEY = "aQtmNi3Zvo_qDJ-8tBQ1tObNxJ-M95Bkr2ndIpDK";
private static final String PREFIX_URL = "http://cdn.iwzb.top/";
private static final String BUCKET = "cisp";
public static String upload(MultipartFile originFile) {
try {
String filename = "";
if (originFile.getOriginalFilename() != null) {
filename = originFile.getOriginalFilename();
}
File file = new File(filename);
FileUtils.copyInputStreamToFile(originFile.getInputStream(), file);
UploadManager uploadManager = getUploadManager();
String token = getToken();
Response response = uploadManager.put(file.getAbsolutePath(), newName(file.getName()), token);
//解析上传成功的结果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
if (file.delete()) {
return PREFIX_URL + putRet.key;
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static boolean delete(String key) {
try {
BucketManager bkm = getBucketManager();
bkm.delete(BUCKET, key);
return true;
} catch (Exception e) {
return false;
}
}
private static UploadManager getUploadManager() {
Configuration cfg = new Configuration(Zone.zone0());
return new UploadManager(cfg);
}
private static BucketManager getBucketManager() {
Configuration cfg = new Configuration(Zone.zone0());
Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
return new BucketManager(auth, cfg);
}
private static String newName(String oldName) {
String[] datas = oldName.split("\\.");
String type = datas[datas.length - 1];
return UUID.randomUUID().toString() + "." + type;
}
private static String getToken() {
Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
return auth.uploadToken(BUCKET);
}
}
加密工具类
java
public class PasswordUtil {
/**
* 加密密码
*/
public static String bryptPwd(String pwd) {
return BCrypt.hashpw(pwd, BCrypt.gensalt());
}
/**
* 校验密码
*/
public static boolean validPwd(String pwd, String hashed) {
try {
return BCrypt.checkpw(pwd, hashed);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
3.4 主要功能实现
java
@Controller
@RequestMapping("/article")
public class ArticleController {
@Resource
ArticleService articleService;
@Resource
CategoryService categoryService;
@Resource
CommentService commentService;
@Resource
UserService userService;
@RequestMapping("/uploadImg")
@ResponseBody
public ReturnJson upload(HttpServletRequest request, MultipartFile file) {
String imgUrl = FileUtil.upload(file);
Map<String, String> imgMap = new HashMap<>();
imgMap.put("src", imgUrl);
imgMap.put("title", file.getOriginalFilename());
return new ReturnJson("上传成功", imgMap);
}
@RequestMapping("/add")
@ResponseBody
public ReturnJson add(HttpServletRequest request, @RequestBody Article article) {
try {
User user;
if (request.getSession().getAttribute("adminUser") != null) {
user = (User) request.getSession().getAttribute("adminUser");
} else {
user = (User) request.getSession().getAttribute("user");
}
if ("禁言".equals(user.getStatus())) {
return new ReturnJson(1, "您被禁言,无法发布文章!");
}
article.setUserId(user.getId());
if (article.getId() == null) {
articleService.create(article);
} else {
articleService.update(article);
}
return new ReturnJson(0, "发布成功");
} catch (Exception e) {
return new ReturnJson(1, "发布失败");
}
}
@RequestMapping("/addCategory")
@ResponseBody
public ReturnJson addCategory(HttpServletRequest request, @RequestBody Category category) {
try {
if (category.getName() != null) {
categoryService.create(category);
return new ReturnJson(0, "新增分类成功");
} else {
return new ReturnJson(1, "分类名不能为空");
}
} catch (Exception e) {
return new ReturnJson(1, "新增分类失败");
}
}
@RequestMapping("/delCategory")
@ResponseBody
public ReturnJson delCategory(HttpServletRequest request, @RequestBody Category category) {
try {
if (category.getId() != null) {
categoryService.deleteById(category.getId());
return new ReturnJson(0, "删除分类成功");
} else {
return new ReturnJson(1, "删除分类失败");
}
} catch (Exception e) {
return new ReturnJson(1, "新增分类失败");
}
}
@RequestMapping("/updateCategory")
@ResponseBody
public ReturnJson updateCategory(HttpServletRequest request, @RequestBody Category category) {
try {
if (category.getId() != null && category.getName() != null) {
categoryService.updateById(category);
return new ReturnJson(0, "修改分类成功");
} else {
return new ReturnJson(1, "修改分类失败");
}
} catch (Exception e) {
return new ReturnJson(1, "修改分类失败");
}
}
@RequestMapping("/addComment")
@ResponseBody
public ReturnJson addComment(@RequestBody Comment comment) {
try {
User user = userService.getUserById(comment.getUserId());
if ("禁言".equals(user.getStatus())) {
return new ReturnJson(1, "您被禁言,无法发表评论!");
}
commentService.create(comment);
return new ReturnJson("评论成功");
} catch (Exception e) {
return new ReturnJson(1, "评论失败");
}
}
@RequestMapping("/delComment")
@ResponseBody
public ReturnJson delComment(@RequestBody Comment comment) {
try {
commentService.delete(comment);
return new ReturnJson("删除评论成功");
} catch (Exception e) {
return new ReturnJson(1, "删除评论失败");
}
}
@RequestMapping("/delete")
@ResponseBody
public ReturnJson deleteArticle(@RequestBody String param) {
try {
JSONObject json = JSON.parseObject(param);
Integer id = json.getInteger("id");
articleService.delete(id);
return new ReturnJson("删除成功");
} catch (Exception e) {
return new ReturnJson(1, "删除失败");
}
}
@RequestMapping("/setTop")
@ResponseBody
public ReturnJson setTopArticle(@RequestBody String param) {
try {
JSONObject json = JSON.parseObject(param);
Integer articleId = json.getInteger("value");
articleService.setTopStatus(articleId);
return new ReturnJson("置顶成功");
} catch (Exception e) {
return new ReturnJson(1, "置顶失败");
}
}
}
4.项目展示
4.1 普通用户
主页
登录页
主页
个人中心
主页文章分类
文章详情
文章评论
发表文章
4.2 管理员
登录
主页
文章管理
修改文章
查看文章
删除文章
分类管理
用户管理
禁言处理
添加用户
模糊查询
数据统计
参考文献
- 商务网站广告发布模块的设计与实现(吉林大学·金兰)
- 一个通用论坛系统的设计与实现(山东大学·张正)
- 基于PHP的BBS论坛管理系统的设计与实现(吉林大学·齐越)
- 基于Web技术的校园论坛设计与实现(内蒙古科技大学·李鹏飞)
- 基于JavaEE的ITPUB软件开发技术论坛的设计与实现(吉林大学·孙睿)
- 基于J2EE的汽车零部件信息发布平台(内蒙古科技大学·王峰)
- 一个通用论坛系统的设计与实现(山东大学·张正)
- 基于SSH框架的博客用户分享平台的设计与实现(河北工业大学·刘磊)
- 网上论坛系统设计与实现(西安电子科技大学·胡秉玺)
- 基于ASP.NET MVC和实体框架的农业论坛的设计与实现(辽宁科技大学·高起跃)
- 基于J2EE的汽车零部件信息发布平台(内蒙古科技大学·王峰)
- 基于SSH框架的博客用户分享平台的设计与实现(河北工业大学·刘磊)
- 基于B/S结构的新闻发布系统(吉林大学·郭蕊)
- 基于J2EE的汽车零部件信息发布平台(内蒙古科技大学·王峰)
- 基于JavaEE的ITPUB软件开发技术论坛的设计与实现(吉林大学·孙睿)
本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:代码工厂 ,原文地址:https://bishedaima.com/yuanma/35477.html