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

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

引入依赖

   
       org.springframework.boot
       spring-boot-starter-aop
   

自定义一个注解

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中的类别我写了两个,一个表示作用于方法,一个表示作用于类上:
Springboot中使用AOP切面思想完成用户权限验证_第1张图片

配置aop切面


import cn.eblcu.questionbank.infrastructure.util.HttpReqUtil;
import cn.eblcu.questionbank.infrastructure.util.SupperTokenUtils;
import cn.eblcu.questionbank.ui.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
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 java.util.Map;

@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() {
    }

    @Around("pointcut() && (@within(cn.eblcu.questionbank.client.CheckToken) || @annotation(cn.eblcu.questionbank.client.CheckToken))")
    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;
        }
    }

    @Before("@within(cn.eblcu.questionbank.client.CheckToken) || @annotation(cn.eblcu.questionbank.client.CheckToken)")
    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);
    }

    @After("@within(cn.eblcu.questionbank.client.CheckToken) || @annotation(cn.eblcu.questionbank.client.CheckToken)")
    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 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校验的总开关

效果

Springboot中使用AOP切面思想完成用户权限验证_第2张图片当我在类上或者方法上加了这个注解后,就会执行aop中定义的逻辑去校验token。

你可能感兴趣的:(springboot学习,springboot切面,aop,sop切面进行权限验证)