🐛 修复已知的安全问题

This commit is contained in:
ronger 2022-01-05 20:54:34 +08:00
parent f3c1969edc
commit f9f3f4b777
7 changed files with 257 additions and 60 deletions

View File

@ -0,0 +1,178 @@
package com.rymcu.forest.core.service.security;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rymcu.forest.core.service.security.annotation.AuthorshipInterceptor;
import com.rymcu.forest.dto.TokenUser;
import com.rymcu.forest.entity.Article;
import com.rymcu.forest.entity.Portfolio;
import com.rymcu.forest.enumerate.Module;
import com.rymcu.forest.jwt.def.JwtConstants;
import com.rymcu.forest.service.ArticleService;
import com.rymcu.forest.service.PortfolioService;
import com.rymcu.forest.util.UserUtils;
import com.rymcu.forest.web.api.exception.BaseApiException;
import com.rymcu.forest.web.api.exception.ErrorCode;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 检查用户修改信息权限
*
* @author ronger
*/
@Aspect
@Component
public class AuthorshipAspect {
Logger logger = LoggerFactory.getLogger(AuthorshipAspect.class);
@Pointcut("@annotation(com.rymcu.forest.core.service.security.annotation.AuthorshipInterceptor)")
public void authorshipPointCut() {
}
@Resource
private ArticleService articleService;
@Resource
private PortfolioService portfolioService;
/**
* 检查用户修改信息权限
*
* @param joinPoint 连接点
* @return 方法执行结果
* @throws Throwable 调用出错
*/
@Before(value = "authorshipPointCut()")
public void doBefore(JoinPoint joinPoint) throws BaseApiException {
logger.info("检查作者身份 start ...");
String methodName = joinPoint.getSignature().getName();
Method method = currentMethod(joinPoint, methodName);
AuthorshipInterceptor log = method.getAnnotation(AuthorshipInterceptor.class);
if (Objects.nonNull(log)) {
boolean isArticle = true;
if (Module.PORTFOLIO.equals(log.moduleName())) {
isArticle = false;
}
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
String idArticle = "";
Integer idAuthor = 0;
if (isAjax(request)) {
Object[] objects = joinPoint.getArgs();
JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(objects[0]));
if (Objects.nonNull(jsonObject)) {
if (isArticle) {
idArticle = jsonObject.getString("idArticle");
Article article = articleService.findById(idArticle);
if (Objects.nonNull(article)) {
idAuthor = article.getArticleAuthorId();
}
} else {
idArticle = jsonObject.getString("idPortfolio");
Portfolio portfolio = portfolioService.findById(idArticle);
if (Objects.nonNull(portfolio)) {
idAuthor = portfolio.getPortfolioAuthorId();
}
}
}
} else {
Map params = getParams(request);
if (params.isEmpty()) {
params = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
} else {
params.putAll((Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE));
}
if (isArticle) {
idArticle = (String) params.get("idArticle");
Article article = articleService.findById(idArticle);
if (Objects.nonNull(article)) {
idAuthor = article.getArticleAuthorId();
}
} else {
idArticle = (String) params.get("idPortfolio");
Portfolio portfolio = portfolioService.findById(idArticle);
if (Objects.nonNull(portfolio)) {
idAuthor = portfolio.getPortfolioAuthorId();
}
}
}
if (idAuthor > 0) {
String authHeader = request.getHeader(JwtConstants.AUTHORIZATION);
if (StringUtils.isNotBlank(authHeader)) {
TokenUser tokenUser = UserUtils.getTokenUser(authHeader);
if (Objects.nonNull(tokenUser)) {
if (!idAuthor.equals(tokenUser.getIdUser())) {
throw new BaseApiException(ErrorCode.ACCESS_DENIED);
}
} else {
throw new BaseApiException(ErrorCode.ACCESS_DENIED);
}
}
} else {
throw new BaseApiException(ErrorCode.ACCESS_DENIED);
}
}
logger.info("检查作者身份 end ...");
}
/**
* 获取当前执行的方法
*
* @param joinPoint 连接点
* @param methodName 方法名称
* @return 方法
*/
private Method currentMethod(JoinPoint joinPoint, String methodName) {
/**
* 获取目标类的所有方法找到当前要执行的方法
*/
Method[] methods = joinPoint.getTarget().getClass().getMethods();
Method resultMethod = null;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
resultMethod = method;
break;
}
}
return resultMethod;
}
private Map<String, String> getParams(HttpServletRequest request) {
Map<String, String> paramsMap = new HashMap<>(10);
Enumeration<String> paraNames = request.getParameterNames();
while (paraNames.hasMoreElements()) {
String key = paraNames.nextElement();
if ("password".equals(key)) {
continue;
}
paramsMap.put(key, request.getParameter(key));
}
return paramsMap;
}
private boolean isAjax(HttpServletRequest request) {
String requestedWith = request.getHeader("x-requested-with");
if (requestedWith != null && "XMLHttpRequest".equalsIgnoreCase(requestedWith)) {
return true;
}
String contentType = request.getContentType();
return StringUtils.isNotBlank(contentType) && contentType.contains("application/json");
}
}

View File

@ -0,0 +1,21 @@
package com.rymcu.forest.core.service.security.annotation;
import com.rymcu.forest.enumerate.Module;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created on 2022/1/5 19:46.
*
* @author ronger
* @email ronger-x@outlook.com
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthorshipInterceptor {
Module moduleName();
}

View File

@ -0,0 +1,12 @@
package com.rymcu.forest.enumerate;
/**
* Created on 2022/1/5 20:05.
*
* @author ronger
* @email ronger-x@outlook.com
*/
public enum Module {
ARTICLE,
PORTFOLIO;
}

View File

@ -12,7 +12,10 @@ 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.*;
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.web.api.exception.BaseApiException;
import com.rymcu.forest.web.api.exception.ErrorCode;
import lombok.extern.slf4j.Slf4j;
@ -142,10 +145,6 @@ public class ArticleServiceImpl extends AbstractService<Article> implements Arti
if (DEFAULT_STATUS.equals(newArticle.getArticleStatus())) {
isUpdate = true;
}
if (!isAuthor(newArticle.getArticleAuthorId())) {
map.put("message", "非法访问!");
return map;
}
newArticle.setArticleTitle(articleTitle);
newArticle.setArticleTags(articleTags);
newArticle.setArticleStatus(article.getArticleStatus());
@ -236,12 +235,6 @@ public class ArticleServiceImpl extends AbstractService<Article> implements Arti
@Transactional(rollbackFor = Exception.class)
public Map delete(Integer id) throws BaseApiException {
Map<String, String> map = new HashMap(1);
Article article = articleMapper.selectByPrimaryKey(id);
// 鉴权
if (!isAuthor(article.getArticleAuthorId())) {
map.put("message", "非法访问!");
return map;
}
int result;
// 判断是否有评论
boolean isHavComment = articleMapper.existsCommentWithPrimaryKey(id);
@ -322,15 +315,10 @@ public class ArticleServiceImpl extends AbstractService<Article> implements Arti
Map map = new HashMap(2);
Article article = articleMapper.selectByPrimaryKey(idArticle);
if (Objects.nonNull(article)) {
if (isAuthor(article.getArticleAuthorId()) || hasAdminPermission()) {
article.setArticleTags(tags);
articleMapper.updateArticleTags(idArticle, tags);
tagService.saveTagArticle(article, "");
map.put("success", true);
} else {
map.put("success", false);
map.put("message", "非法访问!");
}
article.setArticleTags(tags);
articleMapper.updateArticleTags(idArticle, tags);
tagService.saveTagArticle(article, "");
map.put("success", true);
} else {
map.put("success", false);
map.put("message", "更新失败,文章不存在!");
@ -338,23 +326,6 @@ public class ArticleServiceImpl extends AbstractService<Article> implements Arti
return map;
}
private boolean hasAdminPermission() throws BaseApiException {
User user = UserUtils.getCurrentUserByToken();
if (Objects.nonNull(user)) {
Integer userRoleWeight = userService.findRoleWeightsByUser(user.getIdUser());
return userRoleWeight <= ADMIN_ROLE_WEIGHTS;
}
return false;
}
private boolean isAuthor(Integer idUser) throws BaseApiException {
User user = UserUtils.getCurrentUserByToken();
if (Objects.nonNull(user)) {
return user.getIdUser().equals(idUser);
}
return false;
}
@Override
public Map updatePerfect(Integer idArticle, String articlePerfect) {
Map map = new HashMap(2);

View File

@ -4,11 +4,13 @@ import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.rymcu.forest.core.result.GlobalResult;
import com.rymcu.forest.core.result.GlobalResultGenerator;
import com.rymcu.forest.core.service.security.annotation.AuthorshipInterceptor;
import com.rymcu.forest.dto.ArticleDTO;
import com.rymcu.forest.dto.CommentDTO;
import com.rymcu.forest.entity.Article;
import com.rymcu.forest.entity.ArticleThumbsUp;
import com.rymcu.forest.entity.Sponsor;
import com.rymcu.forest.enumerate.Module;
import com.rymcu.forest.service.ArticleService;
import com.rymcu.forest.service.ArticleThumbsUpService;
import com.rymcu.forest.service.CommentService;
@ -40,9 +42,9 @@ public class ArticleController {
@Resource
private SponsorService sponsorService;
@GetMapping("/detail/{id}")
public GlobalResult<Map<String, Object>> detail(@PathVariable Integer id, @RequestParam(defaultValue = "2") Integer type) {
ArticleDTO articleDTO = articleService.findArticleDTOById(id, type);
@GetMapping("/detail/{idArticle}")
public GlobalResult<Map<String, Object>> detail(@PathVariable Integer idArticle, @RequestParam(defaultValue = "2") Integer type) {
ArticleDTO articleDTO = articleService.findArticleDTOById(idArticle, type);
Map map = new HashMap<>(1);
map.put("article", articleDTO);
return GlobalResultGenerator.genSuccessResult(map);
@ -55,20 +57,22 @@ public class ArticleController {
}
@PutMapping("/post")
@AuthorshipInterceptor(moduleName = Module.ARTICLE)
public GlobalResult updateArticle(@RequestBody ArticleDTO article, HttpServletRequest request) throws BaseApiException, UnsupportedEncodingException {
Map map = articleService.postArticle(article, request);
return GlobalResultGenerator.genSuccessResult(map);
}
@DeleteMapping("/delete/{id}")
public GlobalResult delete(@PathVariable Integer id) throws BaseApiException {
Map map = articleService.delete(id);
@DeleteMapping("/delete/{idArticle}")
@AuthorshipInterceptor(moduleName = Module.ARTICLE)
public GlobalResult delete(@PathVariable Integer idArticle) throws BaseApiException {
Map map = articleService.delete(idArticle);
return GlobalResultGenerator.genSuccessResult(map);
}
@GetMapping("/{id}/comments")
public GlobalResult<Map<String, Object>> commons(@PathVariable Integer id) {
List<CommentDTO> commentDTOList = commentService.getArticleComments(id);
@GetMapping("/{idArticle}/comments")
public GlobalResult<Map<String, Object>> commons(@PathVariable Integer idArticle) {
List<CommentDTO> commentDTOList = commentService.getArticleComments(idArticle);
Map map = new HashMap<>(1);
map.put("comments", commentDTOList);
return GlobalResultGenerator.genSuccessResult(map);
@ -83,15 +87,16 @@ public class ArticleController {
return GlobalResultGenerator.genSuccessResult(map);
}
@GetMapping("/{id}/share")
public GlobalResult share(@PathVariable Integer id) throws BaseApiException {
Map map = articleService.share(id);
@GetMapping("/{idArticle}/share")
public GlobalResult share(@PathVariable Integer idArticle) throws BaseApiException {
Map map = articleService.share(idArticle);
return GlobalResultGenerator.genSuccessResult(map);
}
@PostMapping("/{id}/update-tags")
public GlobalResult updateTags(@PathVariable Integer id, @RequestBody Article article) throws BaseApiException, UnsupportedEncodingException {
Map map = articleService.updateTags(id, article.getArticleTags());
@PostMapping("/update-tags")
@AuthorshipInterceptor(moduleName = Module.ARTICLE)
public GlobalResult updateTags(@RequestBody Article article) throws BaseApiException, UnsupportedEncodingException {
Map map = articleService.updateTags(article.getIdArticle(), article.getArticleTags());
return GlobalResultGenerator.genSuccessResult(map);
}

View File

@ -7,9 +7,11 @@ import com.rymcu.forest.core.result.GlobalResultGenerator;
import com.rymcu.forest.core.result.GlobalResultMessage;
import com.rymcu.forest.core.service.log.annotation.VisitLogger;
import com.rymcu.forest.dto.*;
import com.rymcu.forest.entity.Portfolio;
import com.rymcu.forest.entity.User;
import com.rymcu.forest.service.*;
import com.rymcu.forest.service.ArticleService;
import com.rymcu.forest.service.JavaMailService;
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 org.springframework.web.bind.annotation.*;

View File

@ -2,9 +2,11 @@ package com.rymcu.forest.web.api.portfolio;
import com.rymcu.forest.core.result.GlobalResult;
import com.rymcu.forest.core.result.GlobalResultGenerator;
import com.rymcu.forest.core.service.security.annotation.AuthorshipInterceptor;
import com.rymcu.forest.dto.PortfolioArticleDTO;
import com.rymcu.forest.dto.PortfolioDTO;
import com.rymcu.forest.entity.Portfolio;
import com.rymcu.forest.enumerate.Module;
import com.rymcu.forest.service.PortfolioService;
import com.rymcu.forest.web.api.exception.BaseApiException;
import org.springframework.web.bind.annotation.*;
@ -23,9 +25,9 @@ public class PortfolioController {
@Resource
private PortfolioService portfolioService;
@GetMapping("/detail/{id}")
public GlobalResult detail(@PathVariable Integer id,@RequestParam(defaultValue = "0") Integer type) {
PortfolioDTO portfolio = portfolioService.findPortfolioDTOById(id, type);
@GetMapping("/detail/{idPortfolio}")
public GlobalResult detail(@PathVariable Integer idPortfolio,@RequestParam(defaultValue = "0") Integer type) {
PortfolioDTO portfolio = portfolioService.findPortfolioDTOById(idPortfolio, type);
Map map = new HashMap<>(1);
map.put("portfolio", portfolio);
return GlobalResultGenerator.genSuccessResult(map);
@ -38,36 +40,42 @@ public class PortfolioController {
}
@PutMapping("/post")
@AuthorshipInterceptor(moduleName = Module.PORTFOLIO)
public GlobalResult update(@RequestBody Portfolio portfolio) throws BaseApiException {
portfolio = portfolioService.postPortfolio(portfolio);
return GlobalResultGenerator.genSuccessResult(portfolio);
}
@GetMapping("/{id}/unbind-articles")
public GlobalResult unbindArticles(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer rows, @RequestParam(defaultValue = "") String searchText,@PathVariable Integer id) throws BaseApiException {
Map map = portfolioService.findUnbindArticles(page, rows, searchText, id);
@GetMapping("/{idPortfolio}/unbind-articles")
@AuthorshipInterceptor(moduleName = Module.PORTFOLIO)
public GlobalResult unbindArticles(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer rows, @RequestParam(defaultValue = "") String searchText,@PathVariable Integer idPortfolio) throws BaseApiException {
Map map = portfolioService.findUnbindArticles(page, rows, searchText, idPortfolio);
return GlobalResultGenerator.genSuccessResult(map);
}
@PostMapping("/bind-article")
@AuthorshipInterceptor(moduleName = Module.PORTFOLIO)
public GlobalResult bindArticle(@RequestBody PortfolioArticleDTO portfolioArticle) {
Map map = portfolioService.bindArticle(portfolioArticle);
return GlobalResultGenerator.genSuccessResult(map);
}
@PutMapping("/update-article-sort-no")
@AuthorshipInterceptor(moduleName = Module.PORTFOLIO)
public GlobalResult updateArticleSortNo(@RequestBody PortfolioArticleDTO portfolioArticle) {
Map map = portfolioService.updateArticleSortNo(portfolioArticle);
return GlobalResultGenerator.genSuccessResult(map);
}
@DeleteMapping("/unbind-article")
@AuthorshipInterceptor(moduleName = Module.PORTFOLIO)
public GlobalResult unbindArticle(Integer idArticle,Integer idPortfolio) {
Map map = portfolioService.unbindArticle(idPortfolio,idArticle);
return GlobalResultGenerator.genSuccessResult(map);
}
@DeleteMapping("/delete")
@AuthorshipInterceptor(moduleName = Module.PORTFOLIO)
public GlobalResult delete(Integer idPortfolio) throws BaseApiException {
Map map = portfolioService.deletePortfolio(idPortfolio);
return GlobalResultGenerator.genSuccessResult(map);