在SpringBoot当中使用Spring AOP来实现权限验证和消息管理
引言
1. 权限验证
在之前的Blog项目当中,其实已经体验过权限验证了,不过那时候用的是SpringSecurity API来实现的方法,当没使用API时,可以使用Spring的AOP来进行权限验证了.其实也可以通过使用拦截器来实现登录权限验证,但是AOP相对而言更加灵活
消息管理
2. 消息管理
通过Srping的AOP可以在用户对业务进行操作的时候执行时能发送消息,从而使用户能接收到来自业务的消息提醒.
实现步骤
1. 创建一个注解
- 登录验证
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface GlobalInterceptor { boolean checkLogin() default false; }
这里涉及到的注解相关知识就不再赘述(主要是在写这个知识点的时候已经能看懂代码了所以不想再赘述了)
2. 发送提醒消息
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RecordUserMessage { MessageTypeEnum messageType(); }
2. 创建Aspect切面类
- 登录验证
这里代码逻辑简单就不需要特别解释了
@Aspect @Component @Slf4j public class GlobalOperationAspect { @Resource private RedisComponent redisComponent; // 在ControllerMethod方法从@GlobalInterceptor自定义注解来标记切面,在ControllerMethod方法前执行该方法 @Before("@annotation(com.easylive.annotation.GlobalInterceptor)") public void interceptoDto(JoinPoint joinPoint) { log.error("开始执行登录校验切点拦截"); Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); GlobalInterceptor annotation = method.getAnnotation(GlobalInterceptor.class); if (Objects.isNull(annotation)) { return; } if (annotation.checkLogin()) { checkLogin(); } } private void checkLogin() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String token = request.getHeader(Constants.TOKEN_WEB); if (Objects.isNull(token)) { throw new BusinessException(ResponseCodeEnum.CODE_901); } TokenUserInfoDto tokenInfo = redisComponent.getTokenInfo(token); if (Objects.isNull(tokenInfo)) { throw new BusinessException(ResponseCodeEnum.CODE_901); } } }
- 发送提醒消息
@Aspect @Component @Slf4j public class UserMessageOperationAspect { @Resource private RedisComponent redisComponent; @Resource private UserMessageService userMessageService; private static final String PARAMETERS_VIDEO_ID = "videoId"; private static final String PARAMETERS_ACTION_TYPE = "actionType"; private static final String PARAMETERS_REPLY_COMMENTID = "replyCommentId"; private static final String PARAMETERS_CONTENT = "content"; private static final String PARAMETERS_AUDIT_REJECT_REASON = "reason"; @Around("@annotation(com.easylive.annotation.RecordUserMessage)") public ResponseVO interceptorDto(ProceedingJoinPoint joinPoint) throws Throwable { ResponseVO proceed = (ResponseVO) joinPoint.proceed(); Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); RecordUserMessage annotation = method.getAnnotation(RecordUserMessage.class); //在这里joinPoint.getArgs()获取的是ControllerMethod的实际参数值,method.getParameters()获取的是控制器方法的参数元数据(参数定义名称和类型) if (annotation != null) { saveMessage(annotation, joinPoint.getArgs(), method.getParameters()); } return proceed; } private void saveMessage(RecordUserMessage recordUserMessage, Object[] args, Parameter[] parameters) { String videoId = null; Integer actionType = null; Integer replyCommentId = null; String content = null; for (int i = 0; i < parameters.length; i++) { if (PARAMETERS_VIDEO_ID.equals(parameters[i].getName())) { videoId = (String) args[i]; } else if (PARAMETERS_ACTION_TYPE.equals(parameters[i].getName())) { actionType = (Integer) args[i]; } else if (PARAMETERS_REPLY_COMMENTID.equals(parameters[i].getName())) { replyCommentId = (Integer) args[i]; } else if (PARAMETERS_CONTENT.equals(parameters[i].getName())) { content = (String) args[i]; } } MessageTypeEnum messageTypeEnum = recordUserMessage.messageType(); /* *这里还要单独设置一个条件判断是否为收藏是因为原来的doAction方法里注解默认是写LIKE的 *靠参数actionType来区分互动类型,互动只提示点赞和收藏不提示投币 *所以默认LIKE然后根据actionType参数来变更消息类型 */ if (UserActionTypeEnum.VIDEO_COLLECT.getType().equals(actionType)) { messageTypeEnum = MessageTypeEnum.COLLECTION; } TokenUserInfoDto tokenUserInfoDto = getTokenUserInfoDto(); userMessageService.saveUserMessage(videoId, tokenUserInfoDto == null ? null : tokenUserInfoDto.getUserId(), messageTypeEnum, content, replyCommentId); } protected TokenUserInfoDto getTokenUserInfoDto() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String token = request.getHeader(Constants.TOKEN_WEB); return redisComponent.getTokenInfo(token); } }
异步实现发送提醒消息
@Override @Async public void saveUserMessage(String videoId, String sendUserId, MessageTypeEnum messageTypeEnum, String content, Integer replyCommentId) { VideoInfo videoInfo = videoInfoMapper.selectByVideoId(videoId); if (Objects.isNull(videoInfo)) { return; } UserMessageExtendDto extendDto = new UserMessageExtendDto(); extendDto.setMessageContent(content); String reciveUserId = videoInfo.getUserId(); // 收藏取消已经记录的不再记录 if (ArrayUtils.contains(new Integer[]{MessageTypeEnum.LIKE.getType(), MessageTypeEnum.COLLECTION.getType()}, messageTypeEnum.getType())) { UserMessageQuery userMessageQuery = new UserMessageQuery(); userMessageQuery.setUserId(reciveUserId); userMessageQuery.setVideoId(videoId); userMessageQuery.setMessageType(messageTypeEnum.getType()); Integer count = userMessageMapper.selectCount(userMessageQuery); if (count > 0) { return; } } UserMessage userMessage = new UserMessage(); userMessage.setUserId(reciveUserId); userMessage.setVideoId(videoId); userMessage.setMessageType(messageTypeEnum.getType()); userMessage.setReadType(MessageReadTypeEnum.NO_READ.getType()); userMessage.setCreateTime(new Date()); userMessage.setSendUserId(sendUserId); // 评论特殊处理 if (Objects.nonNull(replyCommentId)) { VideoComment videoComment = videoCommentMapper.selectByCommentId(replyCommentId); if (Objects.nonNull(videoComment)) { reciveUserId = videoComment.getUserId(); extendDto.setMessageContentReply(videoComment.getContent()); } } if (reciveUserId.equals(sendUserId)) { return; } // 系统消息特殊处理 if (MessageTypeEnum.SYS.getType().equals(messageTypeEnum.getType())) { VideoInfoPost videoInfoPost = videoInfoPostMapper.selectByVideoId(videoId); extendDto.setAuditStatus(videoInfoPost.getStatus()); } userMessage.setUserId(reciveUserId); userMessage.setExtendJson(JsonUtils.convertObj2Json(extendDto)); userMessageMapper.insert(userMessage); }
表结构
这里还有个UserMessageExtendDto类
这个类在数据库表里的主要功能是提供灵活性,在表结构当中这个以JSON形式存储在数据库当中,取出返回的时候从JSON序列化为Object,这个可以给数据库表提供灵活性.
结束
其实之前的MHBLOG项目当中也使用过AOP来进行日志记录,但是那次忘记记录了,这次在这个项目当中使用到的AOP相关知识其实也覆盖了上次使用过的知识点.总之就是这么多了.
本文作者:MingHaiZ
本文链接:https://www.cnblogs.com/MingHaiZ/p/18599650
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步