From f9f3f4b7776d50fb7bab9c6e604d660ddf05b6f4 Mon Sep 17 00:00:00 2001 From: ronger Date: Wed, 5 Jan 2022 20:54:34 +0800 Subject: [PATCH] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E5=B7=B2=E7=9F=A5?= =?UTF-8?q?=E7=9A=84=E5=AE=89=E5=85=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/security/AuthorshipAspect.java | 178 ++++++++++++++++++ .../annotation/AuthorshipInterceptor.java | 21 +++ .../com/rymcu/forest/enumerate/Module.java | 12 ++ .../service/impl/ArticleServiceImpl.java | 45 +---- .../web/api/article/ArticleController.java | 35 ++-- .../web/api/common/CommonApiController.java | 6 +- .../api/portfolio/PortfolioController.java | 20 +- 7 files changed, 257 insertions(+), 60 deletions(-) create mode 100644 src/main/java/com/rymcu/forest/core/service/security/AuthorshipAspect.java create mode 100644 src/main/java/com/rymcu/forest/core/service/security/annotation/AuthorshipInterceptor.java create mode 100644 src/main/java/com/rymcu/forest/enumerate/Module.java diff --git a/src/main/java/com/rymcu/forest/core/service/security/AuthorshipAspect.java b/src/main/java/com/rymcu/forest/core/service/security/AuthorshipAspect.java new file mode 100644 index 0000000..b937046 --- /dev/null +++ b/src/main/java/com/rymcu/forest/core/service/security/AuthorshipAspect.java @@ -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 getParams(HttpServletRequest request) { + Map paramsMap = new HashMap<>(10); + Enumeration 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"); + } +} diff --git a/src/main/java/com/rymcu/forest/core/service/security/annotation/AuthorshipInterceptor.java b/src/main/java/com/rymcu/forest/core/service/security/annotation/AuthorshipInterceptor.java new file mode 100644 index 0000000..efb4bb1 --- /dev/null +++ b/src/main/java/com/rymcu/forest/core/service/security/annotation/AuthorshipInterceptor.java @@ -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(); +} diff --git a/src/main/java/com/rymcu/forest/enumerate/Module.java b/src/main/java/com/rymcu/forest/enumerate/Module.java new file mode 100644 index 0000000..c25fa6e --- /dev/null +++ b/src/main/java/com/rymcu/forest/enumerate/Module.java @@ -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; +} 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 be9d57b..7113a57 100644 --- a/src/main/java/com/rymcu/forest/service/impl/ArticleServiceImpl.java +++ b/src/main/java/com/rymcu/forest/service/impl/ArticleServiceImpl.java @@ -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
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
implements Arti @Transactional(rollbackFor = Exception.class) public Map delete(Integer id) throws BaseApiException { Map 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
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
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); diff --git a/src/main/java/com/rymcu/forest/web/api/article/ArticleController.java b/src/main/java/com/rymcu/forest/web/api/article/ArticleController.java index 09a798d..700cbe8 100644 --- a/src/main/java/com/rymcu/forest/web/api/article/ArticleController.java +++ b/src/main/java/com/rymcu/forest/web/api/article/ArticleController.java @@ -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> detail(@PathVariable Integer id, @RequestParam(defaultValue = "2") Integer type) { - ArticleDTO articleDTO = articleService.findArticleDTOById(id, type); + @GetMapping("/detail/{idArticle}") + public GlobalResult> 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> commons(@PathVariable Integer id) { - List commentDTOList = commentService.getArticleComments(id); + @GetMapping("/{idArticle}/comments") + public GlobalResult> commons(@PathVariable Integer idArticle) { + List 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); } diff --git a/src/main/java/com/rymcu/forest/web/api/common/CommonApiController.java b/src/main/java/com/rymcu/forest/web/api/common/CommonApiController.java index 7bd8e3b..6436038 100644 --- a/src/main/java/com/rymcu/forest/web/api/common/CommonApiController.java +++ b/src/main/java/com/rymcu/forest/web/api/common/CommonApiController.java @@ -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.*; diff --git a/src/main/java/com/rymcu/forest/web/api/portfolio/PortfolioController.java b/src/main/java/com/rymcu/forest/web/api/portfolio/PortfolioController.java index ffaaf73..bff6c14 100644 --- a/src/main/java/com/rymcu/forest/web/api/portfolio/PortfolioController.java +++ b/src/main/java/com/rymcu/forest/web/api/portfolio/PortfolioController.java @@ -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);