Springboot中使用AOP切面思想完成用户权限验证

aop切面编程真是个好东西,他类似于过滤器又不同于过滤器,因为这次项目中需要对用户的token进行验证。同事写了一个过滤器,但是他要加黑名单(不是所有请求都需要验证token),这样一来这个黑名单就很难维护。所以我想到了aop,可以使用自定义注解的形式来验证用户的token信息。
另外springboot中使用aop也很方便,几个注解就搞定了

引入依赖
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-aop</artifactId>
   </dependency>
自定义一个注解
import org.springframework.web.bind.annotation.Mapping;

import java.lang.annotation.*;

@Documented
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Mapping
public @interface CheckToken {}

其中@Target中的类别我写了两个,一个表示作用于方法,一个表示作用于类上:
在这里插入图片描述

配置aop切面

@Configuration
@Aspect
@Slf4j
public class AopConfig {

    @Value("${tokenConfig.ssoUrl}")
    private String ssoUrl;
    @Value("${tokenConfig.open}")
    private boolean open;

    @Pointcut("@within(org.springframework.stereotype.Controller) || @within(org.springframework.web.bind.annotation.RestController)")
    public void pointcut() {
    }

     public Object simpleAop(final ProceedingJoinPoint joinPoint) throws Throwable {

        try {
            // 调用原有的方法
            Long befor = System.currentTimeMillis();
            Object o = joinPoint.proceed();
            Long after = System.currentTimeMillis();
            log.info("调用方法结束===================共耗时:" + (after - befor) + "毫秒");
            log.info("方法返回:return:====================" + o);
            return o;
        } catch (Throwable throwable) {
            throw throwable;
        }
    }

    public void doBefore(JoinPoint joinPoint) throws Exception {

        log.info("进入方法>>>>>>>" + getFunctionName(joinPoint));
        Object[] args = joinPoint.getArgs();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        if (open){
            int userId = SupperTokenUtils.getUserByToken(request);
            if(userId==-1){
                log.info("用户信息无效,请注册或登录!");
                throw new BusinessException("-1", "用户信息无效,请注册或登录!");
            }
            checked(request, response);
        }
        String url = request.getRequestURL().toString();
        log.info("请求URL:【{}】,\n请求参数:【{}】", url, args);
        Long befor = System.currentTimeMillis();
        log.info("方法开始时间:【{}】", befor);
    }

     public void doAfter() {
        // todo 可以做一些事情
        Long after = System.currentTimeMillis();
        log.info("方法结束时间:" + after);
    }


    private String getFunctionName(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getName();
//        CheckToken annotation = signature.getMethod().getAnnotation(CheckToken.class);

    }

    /**
     * 获取目标主机的ip
     */
    private String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    private boolean checked(HttpServletRequest request, HttpServletResponse response) throws BusinessException {
        boolean isDoFilter = true;
        String token = request.getHeader("token");
        if (token == null) {
            //2表示没有传token
            throw new BusinessException("-1", "请求参数token为空!");
        }
        try {
            HttpResponse httpResponse = HttpReqUtil.postObjectReq(ssoUrl, token);
            Map<String, Object> responsemap = HttpReqUtil.parseHttpResponse(httpResponse);
            String isPassedStr = responsemap.get("isPassed").toString();
            int i = Integer.valueOf(isPassedStr).intValue();
            /**
             * 返回0表示验证通过,-1表示不通过
             */
            if (i == -1)
                isDoFilter = false;
        } catch (Exception e) {
            e.printStackTrace();
            log.info("请求验证token失败");
            throw new BusinessException("3", "sso平台故障!");
        }
        if (!isDoFilter) {
            throw new BusinessException("-1", "token验证失败!!");
        }
        return true;
    }

}

@Pointcut:切入点我写的是controller和restController都算在内
@Around:满足切入点的同时还要满足自定义的注解,也就是必须写了自定义注解才会触发
ssoUrl:表示单点登录的主机地址,用来验证用户token
open:设置了控制token校验的总开关

效果

在这里插入图片描述当我在类上或者方法上加了这个注解后,就会执行aop中定义的逻辑去校验token。

另外需要捕获异常情况的话可以参考:https://blog.csdn.net/qq_29807783/article/details/120154329;
不过项目一般都有统一的异常捕获机制,很少有在aop这里进行异常处理的。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值