SpringAop實現許可權校驗與日誌列印

賣琴的發表於2020-01-15

使用springboot+aop實現使用者的許可權校驗與日誌的列印

Base切面

/**
 * @Description 基礎切面類
 * @author xpWang
 * @date 2020/1/6 16:13
 */
public class BaseAspect {
    protected Method method;
    protected Class clazz;
    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || "
            + "@annotation(org.springframework.web.bind.annotation.GetMapping) || "
            + "@annotation(org.springframework.web.bind.annotation.PostMapping)||"
            + "@annotation(org.springframework.stereotype.Controller)||"
            + "@annotation(org.springframework.web.bind.annotation.RestController)")
    void requestMapping() {
    }
    protected void init(JoinPoint joinPoint) throws NoSuchMethodException {
        clazz = joinPoint.getTarget().getClass();
        Signature signature = joinPoint.getSignature();
        MethodSignature msg = (MethodSignature) signature;
        method = clazz.getMethod(msg.getName(), msg.getParameterTypes());
    }
}

許可權校驗父類

/**
 * @Description 使用者資訊相關切面
 * @author xpWang
 * @date 2020/1/6 16:13
 */
@Slf4j
public class BaseTokenAspect extends BaseAspect{

    @Autowired
    private IAuthinfoService authinfoService;
    protected String token;
    protected AuthInfo authInfo;

    @Override
    protected void init(JoinPoint joinPoint) throws NoSuchMethodException {
        super.init(joinPoint);
        token = RequestUtil.getToken();
        if (StringUtils.isNotEmpty(token)) {
            try {
                authInfo = authinfoService.getAuthInfo(token);
            } catch (Exception e) {
                log.warn("authinfoService getAuthInfo exceprtion :"+e);
            }
        }
    }

    protected ErrorCode checkToken()  {
        log.info("check auth.");
        if (StringUtils.isEmpty(token)) {
            return ErrorCode.TOKEN_NOTOKEN;
        }
        return authinfoService.checkToken(token);
    }

    protected Object[] getAuthinfoArgs(JoinPoint joinPoint) throws Exception {
        Object[] args = joinPoint.getArgs();
        int index=checkParameterHasAuth(method);
        if (index!=-1){
            log.info("set authinfo");
            if (authInfo!=null){
                args[index]=authInfo;
            }else{
                log.warn("there is no cache");
            }
        }
        return args;
    }

    /**
     * @Description 檢查方法引數是否有cn.com.taiji.system.domain.rbac.AuthInfo
     * @return int 返回引數AuthInfo所在Index,如果沒有返回-1
     * @author xpWang
     * @date 2020/1/6 11:43
     */
    private int checkParameterHasAuth(Method method){
        Class<?>[] parameterTypes = method.getParameterTypes();
        for(int i=0;i<parameterTypes.length;i++){
            if (parameterTypes[i].isAssignableFrom(AuthInfo.class)){
                return i;
            }
        }
        return -1;
    }


}

token驗證切面


/**
 * @Description 驗證token注入auth
 * @author xpWang
 * @date 2020/1/6 16:14
 */
@Aspect
@Component
@Slf4j
@Order(1)
public class TokenMethodAspect extends BaseTokenAspect{

 @Around("requestMapping()&&@annotation(cn.com.taiji.framework.annotation.TokenMethod)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            this.init(joinPoint);
            Annotation[] annotations = method.getAnnotationsByType(TokenMethod.class);
            if (ObjectUtils.isEmpty(annotations)) {
                return joinPoint.proceed();
            }
            log.info("ClassName:\t" + clazz.getSimpleName()+"\tMethodName:\t" + method.getName());
            ErrorCode errorCode=checkToken();
            if (ErrorCode.TOKEN_CHECK_SUCCESS!=errorCode){
                log.error(errorCode.toJsonString());
                return new ObjectResponse(errorCode);
            }
            Object[] args=this.getAuthinfoArgs(joinPoint);
            return joinPoint.proceed(args);
        } catch (NoSuchMethodException e) {
            return new ObjectResponse().error("token aspect init exception.");
        } catch (Exception ex){
            return new ObjectResponse().error("token aspect doarround exception",ex);
        }
    }
}

許可權校驗切面


/**
 * @Description 驗證token,permission注入auth
 * @author xpWang
 * @date 2020/1/6 16:14
 */

@Aspect
@Component
@Slf4j
@Order(2)
public class TokenPermissionMethodAspect extends BaseTokenAspect {
    /**
     * @Description 驗證token, 注入Auth
     * @author xpWang
     * @date 2020/1/6 14:53
     */
    @Around("requestMapping()&&@annotation(cn.com.taiji.framework.annotation.TokenPermissionMethod)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        if (!RequestUtil.isNeedPermission()){
            log.debug("checkAuthPermission isNeedPermission is false.");
            return joinPoint.proceed();
        }
        try {
            this.init(joinPoint);
            log.info("ClassName:\t" + clazz.getSimpleName() + "\tMethodName:\t" + method.getName());
            ErrorCode errorCode = checkToken();
            if (ErrorCode.TOKEN_CHECK_SUCCESS != errorCode) {
                log.error(errorCode.toJsonString());
                return new ObjectResponse(errorCode);
            }
            ErrorCode perErrorCode = checkAuthPermission();
            if (ErrorCode.SUCCESS!=perErrorCode){
                log.error(perErrorCode.toJsonString());
                return new ObjectResponse(perErrorCode);
            }
            log.info("permission verify success.");
            TokenPermissionMethod annotation = method.getAnnotation(TokenPermissionMethod.class);
            if (annotation.onlyCheck()){
                return new ObjectResponse(ErrorCode.PERMISSION_CHECK_SUCCESS);
            }
            Object[] args = this.getAuthinfoArgs(joinPoint);
            return joinPoint.proceed(args);
        } catch (NoSuchMethodException e) {
            return new ObjectResponse().error("token aspect init exception.");
        } catch (Exception ex) {
            return new ObjectResponse().error("TokenPermissionMethodAspect aspect doAround exception", ex);
        }
    }

    /**
     * @Description 檢查使用者許可權
     * @author xpWang
     * @date 2020/1/6 15:49
     */
    private ErrorCode checkAuthPermission(){
        if (authInfo == null) {
            return ErrorCode.PERMISSION_NO_CACHE;
        }
        Integer perId = RequestUtil.getPerId();
        Integer nodeId = RequestUtil.getNodeId();
        log.debug("checkAuthPermission perId:\t"+perId+"\tnodeId:\t"+nodeId);
        //check permission
        if (perId != null &&
                (authInfo.getPermissions() == null || !ArrayUtils.contains(authInfo.getPermissions(), perId))) {
            return ErrorCode.PERMISSION_FAILED;
        }

        if (nodeId != null &&
                (authInfo.getNodes() == null || !ArrayUtils.contains(authInfo.getNodes(), nodeId))) {
            return ErrorCode.PERMISSION_FAILED;
        }
        return ErrorCode.SUCCESS;

    }

}

controller日誌記錄
這裡的方法引數列印有問題,需要修改

@Aspect
@Component
@Slf4j
@Order(1)
public class ControllerLogAspect extends BaseAspect{

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || "
            + "@annotation(org.springframework.web.bind.annotation.GetMapping) || "
            + "@annotation(org.springframework.web.bind.annotation.PostMapping)")
    void requestLog() {
    }

    @Around("requestLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long begin=System.currentTimeMillis();
        init(joinPoint);
        log.debug("BEGIN:\tClassName:\t" + clazz.getSimpleName()+"\tMethodName:\t" + method.getName()+"\targs:\t"+JSON.toJSONString(joinPoint.getArgs(), SerializerFeature.WriteMapNullValue));
        //log.debug("BEGIN:\tClassName:\t" + clazz.getSimpleName()+"\tMethodName:\t" + method.getName()+"\targs:\t");
        Object result=joinPoint.proceed();
        log.debug("END:\tClassName:\t" + clazz.getSimpleName()+"\tMethodName:\t" + method.getName()+"\tCost:"+(System.currentTimeMillis()-begin)+"ms\tResult"+JSON.toJSONString(result));
        return result;
    }
}

annotation

/**
 * @Description  需要進行Token以及許可權驗證的Controller註解
 * @author xpWang
 * @date 2020/1/6 11:27
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TokenPermissionMethod {
    boolean onlyCheck() default false;
}

/**
 * @Description  需要進行Token驗證的Controller註解
 * @author xpWang
 * @date 2020/1/6 11:27
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TokenMethod {

}

controller使用


/**
 * @Description 使用者登入資訊相關服務
 * @author xpWang
 * @date 2019/12/31 10:23
 */
@RestController
@RequestMapping("/authinfo")
@Api(tags = "賬戶許可權管理系統-使用者認證", value = "authinfo")
public class AuthInfoController extends BaseController {

    @Autowired
    private IAuthinfoService authinfoService;


    @PostMapping("/login")
    @ApiOperation(notes = "登入介面", value = "login")
    public AjaxResponse login(@RequestBody UserDO user) {
        ObjectResponse response=new ObjectResponse();
        AuthInfo auth = null;
        try {
            auth = authinfoService.login(user);
            if (auth == null) {
                return response.error("Incorrect user name or password");
            }
            return response.success("login success", auth);
        } catch (Exception e) {
            return response.error("login exception : " , e);
        }
    }


    /**
     * @Description AOP判斷了
     * @author xpWang
     * @date 2020/1/6 16:14
     */
    @PostMapping("/checkAuth")
    @ApiOperation(notes = "登入資訊許可權校驗", value = "checkAuth")
    @TokenPermissionMethod(onlyCheck = true)
    public AjaxResponse checkAuth() {
        return new ObjectResponse(ErrorCode.PERMISSION_CHECK_SUCCESS);
    }

    /**
     * 根據TOKEN獲取賬號資訊
     */
    @PostMapping("/getAuthInfo")
    @ApiOperation(notes = "賬號分頁查詢", value = "getAuthInfo")
    @TokenMethod
    public AjaxResponse getAuthInfo(AuthInfo authInfo) {
        ObjectResponse response=new ObjectResponse();
        try {
            if (authInfo != null) {
                return response.success("success.", authInfo);
            } else {
                return response.error("there is no authinfo in cache.");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return response.error("getAuthInfo exception:\t",e);
        }
    }

    public AuthInfoController(){
        System.out.println("init AuthInfoController.");
    }

}

相關文章