diff --git a/pom.xml b/pom.xml
index 8d90720..ea0eb05 100644
--- a/pom.xml
+++ b/pom.xml
@@ -101,7 +101,7 @@
com.alibaba
fastjson
- 1.2.79
+ 2.0.3
@@ -241,6 +241,10 @@
com.google.guava
guava
+
+ org.json
+ json
+
diff --git a/src/main/java/com/rymcu/forest/config/ShiroLoginFilter.java b/src/main/java/com/rymcu/forest/config/ShiroLoginFilter.java
index 6f6d87d..babf323 100644
--- a/src/main/java/com/rymcu/forest/config/ShiroLoginFilter.java
+++ b/src/main/java/com/rymcu/forest/config/ShiroLoginFilter.java
@@ -2,6 +2,7 @@ package com.rymcu.forest.config;
import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.SerializerFeature;
import com.rymcu.forest.core.result.GlobalResultGenerator;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.slf4j.Logger;
@@ -51,7 +52,7 @@ public class ShiroLoginFilter extends FormAuthenticationFilter {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setHeader("sessionstatus", "timeOut");
httpServletResponse.addHeader("loginPath", this.getLoginUrl());
- httpServletResponse.getWriter().write(JSONObject.toJSONString(GlobalResultGenerator.genErrorResult("未登录或已登录超时,请重新登录"),true));
+ httpServletResponse.getWriter().write(JSONObject.toJSONString(GlobalResultGenerator.genErrorResult("未登录或已登录超时,请重新登录"), SerializerFeature.PrettyFormat));
return false;
}else {
if (log.isTraceEnabled()) {
diff --git a/src/main/java/com/rymcu/forest/service/CommentService.java b/src/main/java/com/rymcu/forest/service/CommentService.java
index 92b2b9d..a6bc0ed 100644
--- a/src/main/java/com/rymcu/forest/service/CommentService.java
+++ b/src/main/java/com/rymcu/forest/service/CommentService.java
@@ -3,6 +3,7 @@ package com.rymcu.forest.service;
import com.rymcu.forest.core.service.Service;
import com.rymcu.forest.dto.CommentDTO;
import com.rymcu.forest.entity.Comment;
+import com.rymcu.forest.web.api.exception.BaseApiException;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@@ -26,7 +27,7 @@ public interface CommentService extends Service {
* @param request
* @return
*/
- Map postComment(Comment comment, HttpServletRequest request);
+ Map postComment(Comment comment, HttpServletRequest request) throws BaseApiException;
/**
* 获取评论列表数据
diff --git a/src/main/java/com/rymcu/forest/service/impl/ArticleServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/ArticleServiceImpl.java
index 7113a57..68f919d 100644
--- a/src/main/java/com/rymcu/forest/service/impl/ArticleServiceImpl.java
+++ b/src/main/java/com/rymcu/forest/service/impl/ArticleServiceImpl.java
@@ -12,10 +12,7 @@ import com.rymcu.forest.mapper.ArticleMapper;
import com.rymcu.forest.service.ArticleService;
import com.rymcu.forest.service.TagService;
import com.rymcu.forest.service.UserService;
-import com.rymcu.forest.util.Html2TextUtil;
-import com.rymcu.forest.util.NotificationUtils;
-import com.rymcu.forest.util.UserUtils;
-import com.rymcu.forest.util.Utils;
+import com.rymcu.forest.util.*;
import com.rymcu.forest.web.api.exception.BaseApiException;
import com.rymcu.forest.web.api.exception.ErrorCode;
import lombok.extern.slf4j.Slf4j;
@@ -112,7 +109,7 @@ public class ArticleServiceImpl extends AbstractService implements Arti
String articleTitle = article.getArticleTitle();
String articleTags = article.getArticleTags();
String articleContent = article.getArticleContent();
- String articleContentHtml = article.getArticleContentHtml();
+ String articleContentHtml = XssUtils.filterHtmlCode(article.getArticleContentHtml());
User user = UserUtils.getCurrentUserByToken();
if (Objects.isNull(user)) {
throw new BaseApiException(ErrorCode.INVALID_TOKEN);
@@ -358,7 +355,7 @@ public class ArticleServiceImpl extends AbstractService implements Arti
if (!type.equals(articleList)) {
ArticleContent articleContent = articleMapper.selectArticleContent(article.getIdArticle());
if (type.equals(articleView)) {
- article.setArticleContent(articleContent.getArticleContentHtml());
+ article.setArticleContent(XssUtils.filterHtmlCode(articleContent.getArticleContentHtml()));
// 获取所属作品集列表数据
List portfolioArticleDTOList = articleMapper.selectPortfolioArticles(article.getIdArticle());
portfolioArticleDTOList.forEach(this::genPortfolioArticles);
@@ -366,7 +363,7 @@ public class ArticleServiceImpl extends AbstractService implements Arti
} else if (type.equals(articleEdit)) {
article.setArticleContent(articleContent.getArticleContent());
} else {
- article.setArticleContent(articleContent.getArticleContentHtml());
+ article.setArticleContent(XssUtils.filterHtmlCode(articleContent.getArticleContentHtml()));
}
}
return article;
diff --git a/src/main/java/com/rymcu/forest/service/impl/CommentServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/CommentServiceImpl.java
index 0602e87..6773cc1 100644
--- a/src/main/java/com/rymcu/forest/service/impl/CommentServiceImpl.java
+++ b/src/main/java/com/rymcu/forest/service/impl/CommentServiceImpl.java
@@ -9,9 +9,8 @@ import com.rymcu.forest.entity.Comment;
import com.rymcu.forest.mapper.CommentMapper;
import com.rymcu.forest.service.ArticleService;
import com.rymcu.forest.service.CommentService;
-import com.rymcu.forest.util.Html2TextUtil;
-import com.rymcu.forest.util.NotificationUtils;
-import com.rymcu.forest.util.Utils;
+import com.rymcu.forest.util.*;
+import com.rymcu.forest.web.api.exception.BaseApiException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -42,6 +41,7 @@ public class CommentServiceImpl extends AbstractService implements Comm
private List genComments(List commentDTOList) {
commentDTOList.forEach(commentDTO -> {
commentDTO.setTimeAgo(Utils.getTimeAgo(commentDTO.getCreatedTime()));
+ commentDTO.setCommentContent(XssUtils.filterHtmlCode(commentDTO.getCommentContent()));
if (commentDTO.getCommentAuthorId() != null) {
Author author = commentMapper.selectAuthor(commentDTO.getCommentAuthorId());
if (author != null) {
@@ -63,23 +63,24 @@ public class CommentServiceImpl extends AbstractService implements Comm
@Override
@Transactional(rollbackFor = Exception.class)
- public Map postComment(Comment comment, HttpServletRequest request) {
+ public Map postComment(Comment comment, HttpServletRequest request) throws BaseApiException {
+ comment.setCommentAuthorId(Objects.requireNonNull(UserUtils.getCurrentUserByToken()).getIdUser());
Map map = new HashMap(1);
- if(comment.getCommentArticleId() == null){
- map.put("message","非法访问,文章主键异常!");
+ if (comment.getCommentArticleId() == null) {
+ map.put("message", "非法访问,文章主键异常!");
return map;
}
- if(comment.getCommentAuthorId() == null){
- map.put("message","非法访问,用户未登录!");
+ if (comment.getCommentAuthorId() == null) {
+ map.put("message", "非法访问,用户未登录!");
return map;
}
- if(StringUtils.isBlank(comment.getCommentContent())){
- map.put("message","回帖内容不能为空!");
+ if (StringUtils.isBlank(comment.getCommentContent())) {
+ map.put("message", "回帖内容不能为空!");
return map;
}
Article article = articleService.findById(comment.getCommentArticleId().toString());
if (article == null) {
- map.put("message","文章不存在!");
+ map.put("message", "文章不存在!");
return map;
}
String ip = Utils.getIpAddress(request);
@@ -87,29 +88,29 @@ public class CommentServiceImpl extends AbstractService implements Comm
comment.setCommentIP(ip);
comment.setCommentUA(ua);
comment.setCreatedTime(new Date());
+ comment.setCommentContent(XssUtils.filterHtmlCode(comment.getCommentContent()));
commentMapper.insertSelective(comment);
- StringBuilder commentSharpUrl = new StringBuilder(article.getArticlePermalink());
- commentSharpUrl.append("#comment-").append(comment.getIdComment());
- commentMapper.updateCommentSharpUrl(comment.getIdComment(), commentSharpUrl.toString());
+ String commentSharpUrl = article.getArticlePermalink() + "#comment-" + comment.getIdComment();
+ commentMapper.updateCommentSharpUrl(comment.getIdComment(), commentSharpUrl);
String commentContent = comment.getCommentContent();
- if(StringUtils.isNotBlank(commentContent)){
+ if (StringUtils.isNotBlank(commentContent)) {
Integer length = commentContent.length();
- if(length > MAX_PREVIEW){
+ if (length > MAX_PREVIEW) {
length = 200;
}
- String commentPreviewContent = commentContent.substring(0,length);
+ String commentPreviewContent = commentContent.substring(0, length);
commentContent = Html2TextUtil.getContent(commentPreviewContent);
// 评论者不是作者本人则进行消息通知
if (!article.getArticleAuthorId().equals(comment.getCommentAuthorId())) {
- NotificationUtils.saveNotification(article.getArticleAuthorId(),comment.getIdComment(), NotificationConstant.Comment, commentContent);
+ NotificationUtils.saveNotification(article.getArticleAuthorId(), comment.getIdComment(), NotificationConstant.Comment, commentContent);
}
// 判断是否是回复消息
if (comment.getCommentOriginalCommentId() != null && comment.getCommentOriginalCommentId() != 0) {
Comment originalComment = commentMapper.selectByPrimaryKey(comment.getCommentOriginalCommentId());
// 回复消息时,评论者不是上级评论作者则进行消息通知
if (!comment.getCommentAuthorId().equals(originalComment.getCommentAuthorId())) {
- NotificationUtils.saveNotification(originalComment.getCommentAuthorId(),comment.getIdComment(), NotificationConstant.Comment, commentContent);
+ NotificationUtils.saveNotification(originalComment.getCommentAuthorId(), comment.getIdComment(), NotificationConstant.Comment, commentContent);
}
}
}
diff --git a/src/main/java/com/rymcu/forest/service/impl/PortfolioServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/PortfolioServiceImpl.java
index ea40a5f..c0de668 100644
--- a/src/main/java/com/rymcu/forest/service/impl/PortfolioServiceImpl.java
+++ b/src/main/java/com/rymcu/forest/service/impl/PortfolioServiceImpl.java
@@ -15,6 +15,7 @@ import com.rymcu.forest.service.PortfolioService;
import com.rymcu.forest.service.UserService;
import com.rymcu.forest.util.UserUtils;
import com.rymcu.forest.util.Utils;
+import com.rymcu.forest.util.XssUtils;
import com.rymcu.forest.web.api.common.UploadController;
import com.rymcu.forest.web.api.exception.BaseApiException;
import org.apache.commons.lang.StringUtils;
@@ -75,6 +76,7 @@ public class PortfolioServiceImpl extends AbstractService implements
portfolio.setPortfolioAuthorId(user.getIdUser());
portfolio.setCreatedTime(new Date());
portfolio.setUpdatedTime(portfolio.getCreatedTime());
+ portfolio.setPortfolioDescriptionHtml(XssUtils.filterHtmlCode(portfolio.getPortfolioDescription()));
portfolioMapper.insertSelective(portfolio);
PortfolioIndexUtil.addIndex(
PortfolioLucene.builder()
diff --git a/src/main/java/com/rymcu/forest/service/impl/TagServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/TagServiceImpl.java
index 09b4680..7b6bfdf 100644
--- a/src/main/java/com/rymcu/forest/service/impl/TagServiceImpl.java
+++ b/src/main/java/com/rymcu/forest/service/impl/TagServiceImpl.java
@@ -13,6 +13,7 @@ import com.rymcu.forest.service.TagService;
import com.rymcu.forest.util.BaiDuAipUtils;
import com.rymcu.forest.util.CacheUtils;
import com.rymcu.forest.util.UserUtils;
+import com.rymcu.forest.util.XssUtils;
import com.rymcu.forest.web.api.common.UploadController;
import com.rymcu.forest.web.api.exception.BaseApiException;
import org.apache.commons.lang.StringUtils;
@@ -127,6 +128,7 @@ public class TagServiceImpl extends AbstractService implements TagService {
Integer result;
Map map = new HashMap(1);
+ tag.setTagDescription(XssUtils.filterHtmlCode(tag.getTagDescription()));
if (tag.getIdTag() == null) {
if (StringUtils.isBlank(tag.getTagTitle())) {
map.put("message", "标签名不能为空!");
diff --git a/src/main/java/com/rymcu/forest/service/impl/TopicServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/TopicServiceImpl.java
index 8ee2856..bb81f9b 100644
--- a/src/main/java/com/rymcu/forest/service/impl/TopicServiceImpl.java
+++ b/src/main/java/com/rymcu/forest/service/impl/TopicServiceImpl.java
@@ -1,5 +1,6 @@
package com.rymcu.forest.service.impl;
+import cn.hutool.http.HtmlUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.rymcu.forest.core.service.AbstractService;
@@ -10,6 +11,7 @@ import com.rymcu.forest.entity.Tag;
import com.rymcu.forest.entity.Topic;
import com.rymcu.forest.mapper.TopicMapper;
import com.rymcu.forest.service.TopicService;
+import com.rymcu.forest.util.XssUtils;
import com.rymcu.forest.web.api.common.UploadController;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
@@ -47,7 +49,8 @@ public class TopicServiceImpl extends AbstractService implements TopicSer
@Override
@Transactional(rollbackFor = Exception.class)
public Map saveTopic(Topic topic) {
- Integer result = 0;
+ Integer result;
+ topic.setTopicDescriptionHtml(XssUtils.filterHtmlCode(topic.getTopicDescriptionHtml()));
Map map = new HashMap(1);
if (topic.getIdTopic() == null) {
if (StringUtils.isBlank(topic.getTopicTitle())) {
diff --git a/src/main/java/com/rymcu/forest/util/XssUtils.java b/src/main/java/com/rymcu/forest/util/XssUtils.java
new file mode 100644
index 0000000..6760653
--- /dev/null
+++ b/src/main/java/com/rymcu/forest/util/XssUtils.java
@@ -0,0 +1,81 @@
+package com.rymcu.forest.util;
+
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.http.HtmlUtil;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+/**
+ * Created on 2022/5/10 17:06.
+ *
+ * @author ronger
+ * @email ronger-x@outlook.com
+ * @packageName com.rymcu.forest.util
+ */
+public class XssUtils {
+ private static final String regex = "([\\s|\\S]+?
)|([\\s|\\S]+?
)";
+
+ /**
+ * 滤除content中的危险 HTML 代码, 主要是脚本代码, 滚动字幕代码以及脚本事件处理代码
+ * @param content 需要滤除的字符串
+ * @return 过滤的结果
+ */
+ public static String replaceHtmlCode(String content) {
+ if (StringUtils.isBlank(content)) {
+ return null;
+ }
+ if (0 == content.length()) {
+ return "";
+ }
+ // 需要滤除的脚本事件关键字
+ String[] eventKeywords = {
+ "onmouseover", "onmouseout", "onmousedown", "onmouseup", "onmousemove", "onclick", "ondblclick",
+ "onkeypress", "onkeydown", "onkeyup", "ondragstart", "onerrorupdate", "onhelp", "onreadystatechange",
+ "onrowenter", "onrowexit", "onselectstart", "onload", "onunload", "onbeforeunload", "onblur",
+ "onerror", "onfocus", "onresize", "onscroll", "oncontextmenu", "alert"
+ };
+ content = HtmlUtil.removeHtmlTag(content, "script");
+ content = HtmlUtil.removeHtmlTag(content, "marquee");
+ // 滤除脚本事件代码
+ for (int i = 0; i < eventKeywords.length; i++) {
+ // 去除相关属性
+ content = HtmlUtil.removeHtmlAttr(content, eventKeywords[i]);
+ }
+ return content;
+ }
+
+ public static String filterHtmlCode(String content) {
+ if(StringUtils.isBlank(content)) {
+ return content;
+ }
+ // 拿到匹配的pre标签List
+ List resultFindAll = ReUtil.findAll(regex, content, 0, new ArrayList<>());
+ // size大于0,就做替换
+ if (resultFindAll.size() > 0) {
+ // 生成一个待替换唯一字符串
+ String preTagReplace = UUID.randomUUID().toString() + System.currentTimeMillis();
+ // 判断替换字符串是否唯一
+ while (ReUtil.findAll(preTagReplace, content, 0, new ArrayList<>()).size() > 0) {
+ preTagReplace = UUID.randomUUID().toString() + System.currentTimeMillis();
+ }
+ Pattern pattern = Pattern.compile(preTagReplace);
+ // 替换pre标签内容
+ String preFilter = ReUtil.replaceAll(content, regex, preTagReplace);
+ // 拦截xss
+ final String[] filterResult = {replaceHtmlCode(preFilter)};
+
+ // 依次将替换后的pre标签换回来
+ resultFindAll.forEach(obj -> filterResult[0] = ReUtil.replaceFirst(pattern, filterResult[0], obj));
+ return filterResult[0];
+ } else {
+ return replaceHtmlCode(content);
+ }
+ }
+
+}
+
+
diff --git a/src/main/java/com/rymcu/forest/web/api/comment/CommentController.java b/src/main/java/com/rymcu/forest/web/api/comment/CommentController.java
index 7ca1ed4..56d8cb6 100644
--- a/src/main/java/com/rymcu/forest/web/api/comment/CommentController.java
+++ b/src/main/java/com/rymcu/forest/web/api/comment/CommentController.java
@@ -26,7 +26,7 @@ public class CommentController {
private CommentService commentService;
@PostMapping("/post")
- public GlobalResult postComment(@RequestBody Comment comment, HttpServletRequest request) throws BaseApiException, UnsupportedEncodingException {
+ public GlobalResult postComment(@RequestBody Comment comment, HttpServletRequest request) throws BaseApiException {
Map map = commentService.postComment(comment,request);
return GlobalResultGenerator.genSuccessResult(map);
}