项目笔记

news/2024/11/16 23:46:41/文章来源:https://www.cnblogs.com/qimoxuan/p/18413002

核心业务流程

自定义上传题目流程:

用户答题流程:

AI 创建题目流程:

时序图:

 

架构设计

 在对登录用户的权限进行判断时,不再通过条件判断,编写一大串代码去实现,可以通过写一个Java注解,如

package com.yupi.qidada.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 权限校验**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {/*** 必须有某个角色** @return*/String mustRole() default "";}

这是一个Java注解,用于权限验证。它有两个属性:mustRole和excludeRole。mustRole表示必须有某个角色才能访问该方法,excludeRole表示没有某个角色才能访问该方法。这个注解可以应用于方法上,用于限制访问权限。

实现原理:

  1. 使用@Target和@Retention注解来定义注解的作用范围和生命周期。@Target(ElementType.METHOD)表示这个注解只能应用于方法上,@Retention(RetentionPolicy.RUNTIME)表示这个注解在运行时可用。
  2. 使用@interface关键字定义一个名为AuthCheck的注解,包含两个属性:mustRole和excludeRole。
  3. 在AuthCheck注解中,使用default关键字为mustRole属性设置一个默认值。
  4. 使用ElementType.METHOD表示这个注解只能应用于方法上。
  5. 使用RetentionPolicy.RUNTIME表示这个注解在运行时可用。

用途:
这个注解可以用于权限验证,限制访问权限。例如,在一个方法上添加@AuthCheck(mustRole="admin"),表示只有具有admin角色的用户才能访问该方法。

package com.yupi.qidada.aop;import com.yupi.qidada.annotation.AuthCheck;
import com.yupi.qidada.common.ErrorCode;
import com.yupi.qidada.exception.BusinessException;
import com.yupi.qidada.model.entity.User;
import com.yupi.qidada.model.enums.UserRoleEnum;
import com.yupi.qidada.service.UserService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;/*** 权限校验 AOP**/
@Aspect
@Component
public class AuthInterceptor {@Resourceprivate UserService userService;/*** 执行拦截** @param joinPoint* @param authCheck* @return*/@Around("@annotation(authCheck)")public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {String mustRole = authCheck.mustRole();RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();// 当前登录用户User loginUser = userService.getLoginUser(request);UserRoleEnum mustRoleEnum = UserRoleEnum.getEnumByValue(mustRole);// 不需要权限,放行if (mustRoleEnum == null) {return joinPoint.proceed();}// 必须有该权限才通过UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(loginUser.getUserRole());if (userRoleEnum == null) {throw new BusinessException(ErrorCode.NO_AUTH_ERROR);}// 如果被封号,直接拒绝if (UserRoleEnum.BAN.equals(userRoleEnum)) {throw new BusinessException(ErrorCode.NO_AUTH_ERROR);}// 必须有管理员权限if (UserRoleEnum.ADMIN.equals(mustRoleEnum)) {// 用户没有管理员权限,拒绝if (!UserRoleEnum.ADMIN.equals(userRoleEnum)) {throw new BusinessException(ErrorCode.NO_AUTH_ERROR);}}// 通过权限校验,放行return joinPoint.proceed();}
}

这是一个基于Spring AOP(面向切面编程)的权限校验拦截器。它主要用于在方法执行前,对请求进行权限校验。

1. 首先,定义了一个名为`AuthInterceptor`的类,并使用`@Aspect`和`@Component`注解进行标注,表示这是一个切面类,并且会自动被Spring容器管理。

2. 在`AuthInterceptor`类中,定义了一个名为`doInterceptor`的方法,该方法使用`@Around`注解进行标注,表示这是一个环绕通知,会在目标方法执行前后进行拦截。

3. 在`doInterceptor`方法中,首先获取`AuthCheck`注解中的`mustRole`属性值,然后通过`UserService`获取当前登录用户的信息。

4. 判断`mustRole`属性值是否为空,如果为空,则表示不需要权限,直接放行。

5. 如果`mustRole`属性值不为空,则需要进行权限校验。首先判断当前登录用户的角色是否与`mustRole`属性值相等,如果相等,则表示用户具有该权限,放行。

6. 如果当前登录用户的角色与`mustRole`属性值不相等,则判断用户是否被封号。如果被封号,则抛出`BusinessException`异常。

7. 如果用户没有被封号,则判断用户是否具有管理员权限。如果具有管理员权限,则放行。

8. 如果用户不具有管理员权限,则抛出`BusinessException`异常。

示例:

/*** 应用审核* @param reviewRequest* @param request* @return*/
@PostMapping("/review")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> doAppReview(@RequestBody ReviewRequest reviewRequest, HttpServletRequest request) {ThrowUtils.throwIf(reviewRequest == null, ErrorCode.PARAMS_ERROR);Long id = reviewRequest.getId();Integer reviewStatus = reviewRequest.getReviewStatus();// 校验ReviewStatusEnum reviewStatusEnum = ReviewStatusEnum.getEnumByValue(reviewStatus);if (id == null || reviewStatusEnum == null) {throw new BusinessException(ErrorCode.PARAMS_ERROR);}// 判断是否存在App oldApp = appService.getById(id);ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);// 已是该状态if (oldApp.getReviewStatus().equals(reviewStatus)) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "请勿重复审核");}// 更新审核状态User loginUser = userService.getLoginUser(request);App app = new App();app.setId(id);app.setReviewStatus(reviewStatus);app.setReviewerId(loginUser.getId());app.setReviewTime(new Date());boolean result = appService.updateById(app);ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);return ResultUtils.success(true);
}

 

全局执行器

为了简化外部调用,需要根据不同的应用类别和评分策略,选择对应的策略执行,因此需要一个全局执行器。

2 种实现方式:

1)编程式,在内部计算选用何种策略:

@Service
@Deprecated
public class ScoringStrategyContext {@Resourceprivate CustomScoreScoringStrategy customScoreScoringStrategy;@Resourceprivate CustomTestScoringStrategy customTestScoringStrategy;/*** 评分** @param choiceList* @param app* @return* @throws Exception*/public UserAnswer doScore(List<String> choiceList, App app) throws Exception {AppTypeEnum appTypeEnum = AppTypeEnum.getEnumByValue(app.getAppType());AppScoringStrategyEnum appScoringStrategyEnum = AppScoringStrategyEnum.getEnumByValue(app.getScoringStrategy());if (appTypeEnum == null || appScoringStrategyEnum == null) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用配置有误,未找到匹配的策略");}// 根据不同的应用类别和评分策略,选择对应的策略执行switch (appTypeEnum) {case SCORE:switch (appScoringStrategyEnum) {case CUSTOM:return customScoreScoringStrategy.doScore(choiceList, app);case AI:break;}break;case TEST:switch (appScoringStrategyEnum) {case CUSTOM:return customTestScoringStrategy.doScore(choiceList, app);case AI:break;}break;}throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用配置有误,未找到匹配的策略");}
}

优点是直观清晰,缺点是不利于扩展和维护。

2)声明式,在每个策略类中通过接口声明对应的生效条件,适合比较规律的策略选取场景。

接口:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ScoringStrategyConfig {int appType();int scoringStrategy();
}

给策略实现类补充注解:

@ScoringStrategyConfig(appType = 0, scoringStrategy = 0)

全局执行器:

@Service
public class ScoringStrategyExecutor {// 策略列表
    @Resourceprivate List<ScoringStrategy> scoringStrategyList;/*** 评分** @param choiceList* @param app* @return* @throws Exception*/public UserAnswer doScore(List<String> choiceList, App app) throws Exception {Integer appType = app.getAppType();Integer appScoringStrategy = app.getScoringStrategy();if (appType == null || appScoringStrategy == null) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用配置有误,未找到匹配的策略");}// 根据注解获取策略for (ScoringStrategy strategy : scoringStrategyList) {if (strategy.getClass().isAnnotationPresent(ScoringStrategyConfig.class)) {ScoringStrategyConfig scoringStrategyConfig = strategy.getClass().getAnnotation(ScoringStrategyConfig.class);if (scoringStrategyConfig.appType() == appType && scoringStrategyConfig.scoringStrategy() == appScoringStrategy) {return strategy.doScore(choiceList, app);}}}throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用配置有误,未找到匹配的策略");}
}

因为用了 ScoringStrategyConfig 注解,所以这个实现类被加上了 component 注解,因此可以被spring 管理扫描到。 然后 @Resoure 注入的时候,会通过 ScoringStrategy 类型找到所有实现 ScoringStrategy 接口的实现类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/797644.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

织梦DedeCms的安全问题如何优化

织梦DedeCMS的安全问题可以通过一系列的措施来进行优化,以提高系统的安全性,减少被黑客攻击的风险。以下是一些推荐的安全优化措施: 1. 删除安装文件删除安装目录:安装完成后,删除install目录或将其重命名,并设置为不可访问的状态,以防止恶意用户利用安装脚本进行攻击。…

十三,Spring Boot 中注入 Servlet,Filter,Listener

十三,Spring Boot 中注入 Servlet,Filter,Listener @目录十三,Spring Boot 中注入 Servlet,Filter,Listener1. 基本介绍2. 第一种方式:使用注解方式注入:Servlet,Filter,Listener2.1 使用注解方式注入:Servlet2.2 使用注解方式注入:Filter2.3 使用注解方式注入:Li…

计算机执行汇编代码的原理

计算机执行汇编代码的原理 汇编语言(Assembly Language)是一种低级编程语言,它与机器语言(Machine Language)密切相关。汇编语言由人类可读的指令构成,这些指令会被转化为机器可以理解的二进制代码,即机器码。本文将介绍计算机如何执行汇编代码的基本原理,并通过图文说…

MySQL 大表拆分

概述 在实际工作中,在关系数据库(MySQL、PostgreSQL)的单表数据量上亿后,往往会出现查询和分析变慢甚至无法执行统计分析的情况。这时就需要将大表拆分为多个小表,将小表分布在多个数据库上,形成一个数据库集群。这样的话,一条 SQL 统计语句就可以在多台服务器上并发执行…

条件编译 - 代码裁剪的工具 --进阶C语言

目录条件编译 - 代码裁剪的工具为何要有条件编译条件编译都在那些地方用?见一见条件编译的代码宏是否被定义 vs 宏是否为真or假编译器也能够自动帮你加上宏GCCVS2023-VS2019#ifdef/#ifndef#if注意事项让#if和#ifdef/#ifndef完全一样条件编译也支持嵌套一个使用#if defined能起…

这些年没来得及学习的一些 HTML5 标签

认识并学习下还没来得及学习的一些 HTML5 标签 <ruby> 标签 HTML <ruby> 元素被用来展示东亚文字注音或字符注释。 比如: <ruby>兄弟<rt>xiongdi</rt></ruby><rt> 元素包含字符的发音,字符在 ruby 注解中出现,它用于描述东亚字符的…

软件工程第二次作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/SE2024这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/SE2024/homework/13253这个作业的目标 变大变强学号 102201542项目名称:e了个mo github作业仓库链接:(https://github.com/zqh666nb/e-mo) !!!运行ma…

win11 nimi主机安装软路由

硬件 带两个LAN口的mini主机 软件 win11家庭版 openwrt一 安装hyper-v虚拟机 由于是win11家庭版,启用或关闭 Windows 功能里没有hyper-v win11家庭版启用Hyper-V的方法 创建脚本pushd "%~dp0" dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.…

帝国cms认证码怎么取消

取消帝国CMS中的认证码(也称为验证码),可以通过以下几种方法来实现: 1. 通过后台设置取消 如果你只是想在某些特定的操作中取消认证码,比如在会员注册、登录或后台管理时不需要验证码,可以尝试通过后台设置来取消:登录后台:登录帝国CMS的后台管理系统。进入系统设置:在…

dedecms修改文档标题最大长度

在DedeCMS中修改文档标题的最大长度可以通过以下几种方法实现: 方法一:通过后台设置登录后台:登录到DedeCMS的后台管理系统。进入系统设置:在后台管理界面,找到“系统”->“系统基本参数”。修改文档标题长度:在“系统基本参数”设置中,找到“其他选项”->“文档标…

织梦在导航栏下拉菜单中怎么调用当前栏目子类

在织梦CMS (DEDECMS) 中,要在导航栏的下拉菜单中调用当前栏目的子类,可以使用channelartlist标签来实现。下面是如何实现这一功能的具体步骤: 步骤 1: 使用channelartlist标签 在织梦CMS中,可以利用channelartlist标签来调用当前栏目的子类。下面是一个示例代码:<ul>…

帝国cms怎么上传图片

在帝国CMS中上传图片可以通过多种方式实现,具体取决于你是想在文章中插入图片还是批量上传图片到服务器。下面是两种常见的上传图片的方法: 1. 在文章编辑时上传图片 当你在撰写或编辑文章时,可以直接上传图片并插入到文章内容中:登录后台:登录帝国CMS的后台管理系统。进入…