个人博客系统
一、项目演示
博客系统
二、项目介绍
三个角色:游客 用户 管理员
游客可以浏览文章, 游客可以登录注册成用户,发布文章 管理自己的文章,评论和回复,点赞评论回复文章等
管理员可以对整个系统用户管理,文章管理,分类管理,角色权限管理,评论管理等等
2、项目技术
语言:java
框架:SpringBoot、MyBatis、JQuery、html
数据库:MySQL
三、系统部分功能截图
四、部分代码展示
package com.example.forum.config.shiro;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Validator;
import com.example.forum.common.constant.CommonConstant;
import com.example.forum.entity.Permission;
import com.example.forum.entity.Role;
import com.example.forum.service.PermissionService;
import com.example.forum.service.RoleService;
import com.example.forum.service.UserService;
import com.example.forum.entity.User;
import com.example.forum.enums.CommonParamsEnum;
import com.example.forum.enums.TrueFalseEnum;
import com.example.forum.enums.UserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
public class MyRealm extends AuthorizingRealm {
@Autowired
@Lazy
private UserService userService;
@Autowired
@Lazy
private RoleService roleService;
@Autowired
@Lazy
private PermissionService permissionService;
/**
* 认证信息(身份验证) Authentication 是用来验证用户身份
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
log.info("认证-->MyShiroRealm.doGetAuthenticationInfo()");
//1.验证用户名
User user;
String account = (String) token.getPrincipal();
if (Validator.isEmail(account)) {
user = userService.findByEmail(account);
} else {
user = userService.findByUserName(account);
}
if (user == null) {
//用户不存在
log.info("用户不存在! 登录名:{}, 密码:{}", account, token.getCredentials());
return null;
}
Role role = roleService.findByUserId(user.getId());
if (role != null) {
user.setRole(role.getRole());
}
//2.判断账号是否被封号
if (!Objects.equals(user.getStatus(), UserStatusEnum.NORMAL.getCode())) {
throw new LockedAccountException("账号被封禁");
}
//3.首先判断是否已经被禁用已经是否已经过了10分钟
Date loginLast = DateUtil.date();
if (null != user.getLoginLast()) {
loginLast = user.getLoginLast();
}
Long between = DateUtil.between(loginLast, DateUtil.date(), DateUnit.MINUTE);
if (StringUtils.equals(user.getLoginEnable(), TrueFalseEnum.FALSE.getValue()) && (between < CommonParamsEnum.TEN.getValue())) {
log.info("账号已锁定! 登录名:{}, 密码:{}", account, token.getCredentials());
throw new LockedAccountException("账号被锁定,请10分钟后再试");
}
//4.封装authenticationInfo,准备验证密码
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user, // 用户名
user.getUserPass(), // 密码
ByteSource.Util.bytes(CommonConstant.PASSWORD_SALT), // 盐
getName() // realm name
);
System.out.println("realName:" + getName());
return authenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = (User) principals.getPrimaryPrincipal();
Role role = roleService.findByRoleId(user.getId());
authorizationInfo.addRole(role.getRole());
List<Permission> permissions = permissionService.listPermissionsByRoleId(role.getId());
//把权限的URL全部放到authorizationInfo中去
Set<String> urls = permissions.stream().map(p -> p.getUrl()).collect(Collectors.toSet());
authorizationInfo.addStringPermissions(urls);
return authorizationInfo;
}
}
package com.example.forum.controller.admin;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.forum.controller.common.BaseController;
import com.example.forum.entity.Category;
import com.example.forum.dto.JsonResult;
import com.example.forum.service.CategoryService;
import com.example.forum.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
/**
* <pre>
* 后台分类管理控制器
* </pre>
*
*/
@Slf4j
@Controller
@RequestMapping(value = "/admin/category")
public class CategoryController extends BaseController {
@Autowired
private CategoryService categoryService;
/**
* 查询所有分类并渲染category页面
*
* @return 模板路径admin/admin_category
*/
@GetMapping
public String categories(@RequestParam(value = "page", defaultValue = "0") Integer pageNumber,
@RequestParam(value = "size", defaultValue = "10") Integer pageSize,
@RequestParam(value = "sort", defaultValue = "cateSort") String sort,
@RequestParam(value = "order", defaultValue = "desc") String order, Model model) {
Page page = PageUtil.initMpPage(pageNumber, pageSize, sort, order);
Page<Category> categoryPage = categoryService.findAll(page);
model.addAttribute("categories", categoryPage.getRecords());
model.addAttribute("pageInfo", PageUtil.convertPageVo(page));
return "admin/admin_category";
}
/**
* 新增/修改分类目录
*
* @param category category对象
* @return 重定向到/admin/category
*/
@PostMapping(value = "/save")
@ResponseBody
public JsonResult saveCategory(@ModelAttribute Category category) {
categoryService.insertOrUpdate(category);
return JsonResult.success("保存成功");
}
/**
* 删除分类
*
* @param cateId 分类Id
* @return JsonResult
*/
@DeleteMapping(value = "/delete")
@ResponseBody
public JsonResult checkDelete(@RequestParam("id") Long cateId) {
//1.判断这个分类有文章
Integer count = categoryService.countPostByCateId(cateId);
if (count != 0) {
return JsonResult.error("该分类已经有了文章,无法删除");
}
categoryService.delete(cateId);
return JsonResult.success("删除成功");
}
/**
* 跳转到修改页面
*
* @param cateId cateId
* @param model model
* @return 模板路径admin/admin_category
*/
@GetMapping(value = "/edit")
public String toEditCategory(Model model,
@RequestParam(value = "page", defaultValue = "0") Integer pageNumber,
@RequestParam(value = "size", defaultValue = "10") Integer pageSize,
@RequestParam(value = "sort", defaultValue = "cateSort") String sort,
@RequestParam(value = "order", defaultValue = "desc") String order,
@RequestParam("id") Long cateId) {
Page page = PageUtil.initMpPage(pageNumber, pageSize, sort, order);
//更新的分类
Category category = categoryService.get(cateId);
if (category == null) {
return this.renderNotFound();
}
model.addAttribute("updateCategory", category);
// 所有分类
Page<Category> categoryPage = categoryService.findAll(page);
model.addAttribute("categories", categoryPage.getRecords());
model.addAttribute("pageInfo", PageUtil.convertPageVo(page));
return "admin/admin_category";
}
}
package com.example.forum.controller.admin;
import cn.hutool.http.HtmlUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.forum.controller.common.BaseController;
import com.example.forum.entity.Comment;
import com.example.forum.entity.Post;
import com.example.forum.entity.User;
import com.example.forum.exception.MyBusinessException;
import com.example.forum.dto.JsonResult;
import com.example.forum.dto.QueryCondition;
import com.example.forum.enums.*;
import com.example.forum.service.CommentService;
import com.example.forum.service.PostService;
import com.example.forum.service.UserService;
import com.example.forum.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Objects;
/**
* <pre>
* 后台回复管理控制器
* </pre>
*/
@Slf4j
@Controller
@RequestMapping(value = "/admin/comment")
public class CommentController extends BaseController {
@Autowired
private CommentService commentService;
@Autowired
private PostService postService;
@Autowired
private UserService userService;
/**
* 渲染回复管理页面
*
* @param model model
* @param pageNumber page 当前页码
* @param pageSize size 每页显示条数
* @return 模板路径admin/admin_comment
*/
@GetMapping
public String comments(Model model,
@RequestParam(value = "keywords", defaultValue = "") String keywords,
@RequestParam(value = "page", defaultValue = "1") Integer pageNumber,
@RequestParam(value = "size", defaultValue = "15") Integer pageSize,
@RequestParam(value = "sort", defaultValue = "createTime") String sort,
@RequestParam(value = "order", defaultValue = "desc") String order) {
Page page = PageUtil.initMpPage(pageNumber, pageSize, sort, order);
Comment condition = new Comment();
// condition.setAcceptUserId(loginUserId);
condition.setCommentContent(keywords);
Page<Comment> comments = commentService.findAll(page, new QueryCondition<>(condition));
List<Comment> commentList = comments.getRecords();
commentList.forEach(comment -> comment.setPost(postService.get(comment.getPostId())));
commentList.forEach(comment -> comment.setUser(userService.get(comment.getUserId())));
model.addAttribute("comments", commentList);
model.addAttribute("pageInfo", PageUtil.convertPageVo(page));
model.addAttribute("keywords", keywords);
model.addAttribute("sort", sort);
model.addAttribute("order", order);
return "admin/admin_comment";
}
/**
* 我发送的回复
*
* @param model model
* @param pageNumber page 当前页码
* @param pageSize size 每页显示条数
* @return 模板路径admin/admin_comment
*/
@GetMapping("/send")
public String sendComments(Model model,
@RequestParam(value = "keywords", defaultValue = "") String keywords,
@RequestParam(value = "page", defaultValue = "1") Integer pageNumber,
@RequestParam(value = "size", defaultValue = "15") Integer pageSize,
@RequestParam(value = "sort", defaultValue = "createTime") String sort,
@RequestParam(value = "order", defaultValue = "desc") String order) {
User user = getLoginUser();
Page page = PageUtil.initMpPage(pageNumber, pageSize, sort, order);
Comment condition = new Comment();
condition.setUserId(user.getId());
condition.setCommentContent(keywords);
Page<Comment> comments = commentService.findAll(page, new QueryCondition<>(condition));
List<Comment> commentList = comments.getRecords();
commentList.forEach(comment -> comment.setPost(postService.get(comment.getPostId())));
commentList.forEach(comment -> comment.setUser(userService.get(comment.getUserId())));
model.addAttribute("comments", commentList);
model.addAttribute("pageInfo", PageUtil.convertPageVo(page));
model.addAttribute("keywords", keywords);
model.addAttribute("sort", sort);
model.addAttribute("order", order);
return "admin/admin_comment";
}
/**
* 我发送的回复
*
* @param model model
* @param pageNumber page 当前页码
* @param pageSize size 每页显示条数
* @return 模板路径admin/admin_comment
*/
@GetMapping("/receive")
public String receiveComments(Model model,
@RequestParam(value = "keywords", defaultValue = "") String keywords,
@RequestParam(value = "page", defaultValue = "1") Integer pageNumber,
@RequestParam(value = "size", defaultValue = "15") Integer pageSize,
@RequestParam(value = "sort", defaultValue = "createTime") String sort,
@RequestParam(value = "order", defaultValue = "desc") String order) {
User user = getLoginUser();
Page page = PageUtil.initMpPage(pageNumber, pageSize, sort, order);
Comment condition = new Comment();
condition.setAcceptUserId(user.getId());
condition.setCommentContent(keywords);
Page<Comment> comments = commentService.findAll(page, new QueryCondition<>(condition));
List<Comment> commentList = comments.getRecords();
commentList.forEach(comment -> comment.setPost(postService.get(comment.getPostId())));
commentList.forEach(comment -> comment.setUser(userService.get(comment.getUserId())));
model.addAttribute("comments", commentList);
model.addAttribute("pageInfo", PageUtil.convertPageVo(page));
model.addAttribute("keywords", keywords);
model.addAttribute("sort", sort);
model.addAttribute("order", order);
return "admin/admin_comment";
}
/**
* 删除回复
*
* @param commentId commentId
* @return string 重定向到/admin/comment
*/
@DeleteMapping(value = "/delete")
@ResponseBody
public JsonResult moveToAway(@RequestParam("id") Long commentId) {
//回复
Comment comment = commentService.get(commentId);
//检查权限
basicCheck(comment);
commentService.delete(commentId);
return JsonResult.success("删除成功");
}
/**
* 回复回复,并通过回复
*
* @param commentId 被回复的回复
* @param commentContent 回复的内容
* @return 重定向到/admin/comment
*/
@PostMapping(value = "/reply")
@ResponseBody
public JsonResult replyComment(@RequestParam("id") Long commentId,
@RequestParam("commentContent") String commentContent) {
//博主信息
User loginUser = getLoginUser();
//被回复的回复
Comment lastComment = commentService.get(commentId);
User user = userService.get(lastComment.getUserId());
String at = user != null ? user.getUserDisplayName() : "楼上";
if (lastComment == null) {
return JsonResult.error("回复不存在");
}
Post post = postService.get(lastComment.getPostId());
if (post == null) {
return JsonResult.error("文章不存在");
}
//保存回复
Comment comment = new Comment();
comment.setUserId(loginUser.getId());
comment.setPostId(lastComment.getPostId());
String lastContent = "<a href='#comment-id-" + lastComment.getId() + "'>@" + at + "</a> ";
comment.setCommentContent(lastContent + HtmlUtil.escape(commentContent));
comment.setCommentParent(commentId);
comment.setAcceptUserId(lastComment.getUserId());
comment.setPathTrace(lastComment.getPathTrace() + lastComment.getId() + "/");
commentService.insertOrUpdate(comment);
return JsonResult.success("回复成功");
}
/**
* 批量删除
*
* @param ids 回复ID列表
* @return
*/
@DeleteMapping(value = "/batchDelete")
@ResponseBody
public JsonResult batchDelete(@RequestParam("ids") List<Long> ids) {
//批量操作
//1、防止恶意操作
if (ids == null || ids.size() == 0 || ids.size() >= 100) {
return new JsonResult(ResultCodeEnum.FAIL.getCode(), "参数不合法!");
}
//2、检查用户权限
//文章作者、回复人、管理员才可以删除
List<Comment> commentList = commentService.findByBatchIds(ids);
for (Comment comment : commentList) {
basicCheck(comment);
}
//3、删除
commentService.batchDelete(ids);
return JsonResult.success("删除成功");
}
/**
* 检查文章是否存在和用户是否有权限控制
*
* @param comment
*/
private void basicCheck(Comment comment) {
Long loginUserId = getLoginUserId();
if (comment == null) {
throw new MyBusinessException("回复不存在");
}
//文章
Post post = postService.get(comment.getPostId());
if (post == null) {
throw new MyBusinessException("回复所在文章不存在");
}
//检查权限,文章的作者和收到回复和管理员的可以删除
if (!Objects.equals(post.getUserId(), loginUserId) && !Objects.equals(comment.getAcceptUserId(), loginUserId) && !loginUserIsAdmin()) {
throw new MyBusinessException("没有权限");
}
}
}
五、底部获取项目(9.9¥带走)
有问题,或者需要协助调试运行项目的也可以