Spring AOP

Spring AOP 即面向切面,是对OOP面向对象的一种延伸。
AOP机制可以让开发者把业务流程中的通用功能抽取出来,单独编写功能代码。在业务流程执行过程中,Spring框架会根据业务流程要求,自动把独立编写的功能代码切入到流程的合适位置。

我们通过AOP机制可以实现:Authentication 权限检查、Caching 缓存、Context passing 内容传递、Error handling 错误处理、日志打印等功能,这里我们讲一下怎么用Spring AOP来实现权限检查。

SpringBoot 通过自定义注解实现权限检查

Maven依赖

<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.2</version>
    <optional>true</optional>
</dependency>

<!--Spring AOP-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

MyPermissionTag.class自定义注解

  • @Retention: 用来修饰注解,是注解的注解,称为元注解。
  • @Target:用来说明对象的作用范围
/**
 * 用户请求权限校验
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyPermissionTag {
    String value() default "";
    String name() default "";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里特别讲一下@Retention,按生命周期来划分可分为3类:

  • RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃(运行时去动态获取注解信息);
  • RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期(在编译时进行一些预处理操作);
  • RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在(做一些检查性的操作);

这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。

AuthInterceptor 权限检查的切面

这里简单介绍一下,切面的执行方法和其执行顺序:

  • @Around 通知方法将目标方法封装起来
  • @Before 通知方法会在目标方法调用之前执行
  • @After 通知方法会在目标方法返回或者异常后执行
  • @AfterReturning 通知方法会在目标方法返回时执行
  • @Afterthrowing 通知方法会在目标方法抛出异常时执行

这里以一个返回正常的情况为例:(异常替换最后一步即可)

HasLoginAspect.class

  • 注意要在启动类扫描这个class,并且添加 @EnableAspectJAutoProxy(proxyTargetClass = true)
package com.sharehoo.config.aspect;
import com.alibaba.fastjson.JSONObject;
import com.sharehoo.config.lang.Consts;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


/**
 * @Author miki
 * @Description 用户是否登录的鉴权注解 *
 * @Date 14:48 2020/11/30
 * @Param
 * @return
 **/


@Component
@Aspect
@SuppressWarnings({"unused"})
public class HasLoginAspect {
	
	public static final Logger logger = LoggerFactory.getLogger(HasLoginAspect.class);

	public HasLoginAspect(){
	    logger.info("创建aop切面拦截...");
    }

    /**
     * 参数处理
     *
     * @param point
     */
    @Before("@annotation(com.sharehoo.config.annotation.HasLogin)")
    public void beforeProReq(JoinPoint point) {
        logger.info("前置拦截-开始");

        logger.info("前置拦截-结束");
    }


    @Around("@annotation(com.sharehoo.config.annotation.HasLogin)")
    public Object checkRequestHead(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.debug("===>check access token start:{}", joinPoint.getArgs());
        long begin = System.nanoTime();

        HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        request.setCharacterEncoding("UTF-8");
        HttpServletResponse response =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

        HttpSession session = request.getSession();
        Object user= session.getAttribute(Consts.CURRENTUSER);

        String requestUri = request.getRequestURI();
        String requestMethod = request.getMethod();

        logger.info(
                "===>get the equest url:{}; and request method:{}; ",
                requestUri, requestMethod);

        long end = System.nanoTime();
        logger.info("API deal time log {}:{}",
                joinPoint.getTarget().getClass() + "." + joinPoint.getSignature().getName(),
                (end - begin) / 1000000);
        if(ObjectUtils.isEmpty(user)){
            response.sendRedirect("http://sharehoo.cn/login?prePage=http://sharehoo.cn" + requestUri);
            return null;
        }

        return joinPoint.proceed();
    }


    /**
     * 参数处理
     *
     * @param point
     */
    @After("@annotation(com.sharehoo.config.annotation.HasLogin)")
    public void afterProReq(JoinPoint point) {

        logger.info("登录校验拦截-结束");
    }
}
帖子状态

话题参与者

回复显示排序:

给大家安利一款免费的在线流程图设计软件:贼好用 ProcessFlow — 免费在线作图、实时协作 ProcessFlow是一个在线作图工具的聚合平台, 它可以在线画流程图、思维导图、UI原型图、UML、网络拓扑图、组织结构图等等, 您无需担心下载和更新的问题, 不管Mac还是Windows,一个浏览器就可以随时随地的发挥创意,规划工作,解放您的双手,让您腾出双手去成就别人的梦想。

Finally!
有没有特别的设计建议或更新的指南?
Finally!
您好,这边没有的。
tesla02 best answer
6 Jan,2019
Finally!
有没有特别的设计建议或更新的指南?
此帖子已被版主标记,收到太多否决票。

这是太大的预览图像,它应该更小,甚至五行。在一个页面上有30到60个页面,它是1800个项目 类别例如在附加组件类别有22749个项目,为什么不看到所有的项目,但只有那1800个项目?这是件坏事。

看起来您是个新人,欢迎免费注册社区,加入我们的大家庭,一起学习,一起成长。
提交你的评论
System.out.println("请输入你的代码块...");
话题
分类
喜欢
回复
浏览
活跃
原创社区
985
0
1.5k
原创社区
985
1
1.5k
20/12/25 22:32:21
javaWeb
985
1
1.5k
20/06/15 22:09:21
youtube
698
78
2.1k
3d
Settings

请插入代码块