授权验证方式有很多、但AOP最为优雅。

news2024/11/17 21:22:02

前言

有时候项目中需要对接口进行校验,增加鉴权,确保 API 不被恶意调用。

项目中都是这样
在这里插入图片描述
这样,部分需要查询一些信息,下文需要使用
在这里插入图片描述

这样的代码很多,重复率太高。看着我蛋疼,对此优化一下。

方案

1 传统做法每个控制层加 if 判断

   if (!distributorService.validToken(tokenDto)) {
            return new Result(false, ResultCode.UNAUTHORIZED.val(), ResultCode.UNAUTHORIZED.msg());
     }

这样每个控制层都需要增加代码,代码重复量很多。

2 使用过滤器进行拦截校验,部分接口不需要校验可以设置白名单等注解跳过

这样看着也可以,但对特定场景可能不太使用,一些模块需要校验,一些需要都查询数据。
或者给用户分配不同的角色,然后不同的角色对某一个方法有不同的权限,有些角色可以访问该方法,有的不能访问。这时候我们可以利用aop实现权限验证。

3 使用 Aop进行统一权限验证

实现方式呢,既然使用aop了,aop可以对注解进行代理。

控制层

这里或者可能根据平台id去查平台下的一些信息,部分接口会用 部分接口不会用,但所有的接口都做校验,一些接口需要根据查平台下的一些信息。

那我们如何实现呢?

  • 业务里if大法好
  • 使用aop优雅的实现


 @DBLoadBalance
    @ApiOperation(value = "根据出发城市获取目的城市", notes = "根据出发城市获取目的城市")
    @PostMapping(value = "/endCity")
    @Pog(module = Constants.ModuleName.Distribution,type = PlatTypeEnum.validTokenFindPid,description = "根据出发城市获取目的城市")
    public Result getEndCity(@RequestBody(required = false) PlatformTokenDto<DistributorCitySearchRequest> tokenDto) {
   

//校验token 业务逻辑
 if (!distributorService.validToken(tokenDto)) {
           throw  new BaseException( ResultCode.UNAUTHORIZED.val(), ResultCode.UNAUTHORIZED.msg());
  }

// 查询平台id 下信息 servie需要使用,这里只放在这里说明用
 List<PlatformDistributeAccount> byDistributeId = platformDistributeAccountDao.findByDistributeId(pid);
        if (CollectionUtils.isEmpty(byDistributeId)) {
            log.infoLog(String.format(" 没有查到 平台id : %s  对应 信息 ", pid));
            throw new BaseException(ResultCode.BAD_REQUEST.val(), "错误配置,请校验后再请求!");
        }



        return distributorXXXXXXXXXService.getxxxxxxxxEndCity(tokenDto);
    }



 @ApiOperation(value = "平台创建分销商订单", notes = "平台创建分销商订单")
    @PostMapping(value = "/create")
    @Pog(module = Constants.ModuleName.Distribution,type = PlatTypeEnum.validToken,description = "平台创建分销商订单")
    public Result createOrder(@RequestBody PlatformTokenDto<DistributorOrderCreateRequest> tokenDto) {
        loggerHelper.infoLog("参数 -> " + JSON.toJSONString(tokenDto));

//校验token 业务逻辑,创单不需要查平台id下信息,会直接根据之前接查结果,传过来,值校验token
 if (!distributorService.validToken(tokenDto)) {
           throw  new BaseException( ResultCode.UNAUTHORIZED.val(), ResultCode.UNAUTHORIZED.msg());
  }

}

以上这样的接口很多,传统方法我们每个接口都加if加判断 ,以及查询一些信息。

但使用 AOP 就方便优雅很多。

具体做法

实现思路就是首先自定义一个注解,在方法上添加该注解,跟据注解的值来判断能否访问该方法。

定义注解,不同校验类型,校验类型,校验和查询类型

枚举类


public enum PlatTypeEnum {
    /**
     *
     */
    valid_Token(0, "校验"),
    valid_Token_FIND_PID(1, "查询 + 校验"),

    ;

    private int value;

    private String desc;

    public static final int validToken = 0;

    public static final int validTokenFindPid = 1;

    PlatTypeEnum(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }
 //省略.....
}

定义注解

package com.xxxxxxxxxxx;


import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Pog {

    /**
     * 模块
     */
    String module() default "";

    /**
     * 类型
     * @return
     */
    int type() default 0;

    /**
     * 描述
     */
    String description() default "";

}

校验实体

@ApiModel(value = "平台分销商TokenDto", description = "平台分销商TokenDto")
public class PlatformTokenDto<T> {

    @ApiModelProperty("请求平台id")
    private String pid;

    @ApiModelProperty("请求时间")
    private long timestamp;

    @ApiModelProperty("请求加密串")
    private String token;

    @ApiModelProperty("请求数据")
    private T data;

}

AOP 类

根据注解类型对其进行权限验证或者权限验证+查询数据,当然如果还有其他的类型,可以继续扩展。

package com.xxxxxx.config;

import com.xxxxxx.enums.PlatTypeEnum;
import com.xxxxxx.constant.ResultCode;
import com.xxxxxxx.exception.BaseException;
import com.xxxxx.helper.LoggerHelper;
import com.xxxxx.distributor.dto.PlatformTokenDto;
import com.xxxx.distributor.service.DistributorService;
import com.xxxxxxx.platformdistribute.annotations.Pog;
import com.xxxxxxxxx.platformdistributeaccount.dao.PlatformDistributeAccountDao;
import com.xxxxxxxx.entity.PlatformDistributeAccount;
import com.xxxxxxxxxxx.Constants;
import org.apache.commons.collections4.CollectionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.List;


@Aspect
@Component
public class PogAspect {


    /**
     * 校验切入点
     */
    @Pointcut("@annotation(com.xxxxxxxxx.platformdistribute.annotations.Pog)")
    public void logPointCut() {
    }

    @Autowired
    private PlatformDistributeAccountDao platformDistributeAccountDao;

    @Autowired
    private DistributorService distributorService;

    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) {

        //ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        //HttpServletRequest request = attributes.getRequest();

        Object[] args = joinPoint.getArgs();

//这里获取参数实体,直接强转类型,下文根据注解查出 可以设置值
        PlatformTokenDto tokenDto = (PlatformTokenDto)(args[0]);

//这里用一授权判断,因为所有接口都需要校验,当然不需要校验可以再根据下文注解类型进行判断。
        if (!distributorService.validToken(tokenDto)) {
           throw  new BaseException( ResultCode.UNAUTHORIZED.val(), ResultCode.UNAUTHORIZED.msg());
        }

        doSetPlatformDistributeAccounts(joinPoint,tokenDto);


    }


    //@AfterReturning(returning = "ret", pointcut = "logPointCut()")
    public void doAfterReturning(Object ret) {
    }

    //@AfterReturning(pointcut = "logPointCut()")
    public void doAfter(JoinPoint joinPoint) {
    }


    private void doSetPlatformDistributeAccounts(JoinPoint joinPoint, PlatformTokenDto tokenDto) {
        String methodName = joinPoint.getSignature().getName();
        Method method = currentMethod(joinPoint, methodName);
        Pog pog = method.getAnnotation(Pog.class);
        if (pog == null) {
            return;
        }

        log.infoLog(String.format(" pog: %s ", pog));

        if (pog.type() != PlatTypeEnum.valid_Token_FIND_PID.getValue()) {
            return;
        }

        String pid = tokenDto.getPid();

//查询信息
        List<PlatformDistributeAccount> byDistributeId = platformDistributeAccountDao.findByDistributeId(pid);
        if (CollectionUtils.isEmpty(byDistributeId)) {
            log.infoLog(String.format(" 没有查到 平台id : %s  对应 vcode ", pid));
            throw new BaseException(ResultCode.BAD_REQUEST.val(), "错误配置,请校验后再请求!");
        }

	//设置值
	//这里也可以使用 ThreadLocal 进行下文拿值(用完记得remove),看业务需要决定。
	//我这里直接是放在token dto里了
        tokenDto.setPlatformDistributeAccounts(byDistributeId);

    }


    @AfterThrowing(value = "logPointCut()", throwing = "throwable")
    public void doAfterThrowing(Throwable throwable) {

        log.infoLog(throwable.getMessage());

    }

    /**
     * 获取当前执行的方法
     *
     * @param joinPoint  连接点
     * @param methodName 方法名称
     * @return 方法
     */
    private Method currentMethod(JoinPoint joinPoint, String methodName) {
        /**
         * 获取目标类的所有方法,找到当前要执行的方法
         */
        Method[] methods = joinPoint.getTarget().getClass().getMethods();
        Method resultMethod = null;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                resultMethod = method;
                break;
            }
        }
        return resultMethod;
    }

}

具体那种校验权限根据业务去使用,上面实现的方法很多,但考虑代码重复和优雅还是aop较为优雅。

业务中根据具体场景去使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/353415.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

剑指offer 7 数组中和为0的三个数

此问题属于nsum问题&#xff0c;题目链接&#xff1a;力扣 要求在数组中找到不重复的三元组&#xff0c;三个数加起来为0&#xff0c;且每个下标只能用一次。而且需要返回所有这样的不重复数组。 1. 排序 双指针 1. 「不重复」的本质是什么&#xff1f;我们保持三重循环的大…

SpringBoot——日志文件

基本概念 日志文件记录了程序的报错信息&#xff0c;执行时间&#xff0c;用户的登录状态&#xff0c;操作时间等等 通过日志&#xff0c;我们可以轻松的找到程序的问题&#xff0c;得到程序的相关信息 springBoot启动时控制台打印的这些&#xff0c;就是程序的日志 创建日志…

Kafka报错:Controller 219 epoch 110 failed to change state for partition

集群里面kafka报错&#xff1a;Controller 219 epoch 110 failed to change state for partition maxwell_atlas-0 from OfflinePartition to OnlinePartitionkafka.common.stateChangeFailedException: Failed to elect leader for partition maxwell_atlas-0 under strategy …

SpringWeb

SpringWeb 概述 springWeb 是 spring 框架的一个模块&#xff0c;springWeb 和 spring 无需通过中间整 合层进行整合。 springWeb 是一个基于 mvc 的 web 框架,方便前后端数据的传输. SpringWeb 拥有控制器&#xff0c;接收外部请求&#xff0c;解析参数传给服务层. SpringM…

盘点界面组件DevExtreme 2023年值得期待的一些新功能!

DevExtreme拥有高性能的HTML5 / JavaScript小部件集合&#xff0c;使您可以利用现代Web开发堆栈&#xff08;包括React&#xff0c;Angular&#xff0c;ASP.NET Core&#xff0c;jQuery&#xff0c;Knockout等&#xff09;构建交互式的Web应用程序&#xff0c;该套件附带功能齐…

一文学会进程控制

目录进程的诞生fork函数fork的本质fork的常规用法fork调用失败的原因进程的死亡进程退出的场景常见的进程退出方法正常终止&#xff08;代码跑完&#xff09;echo $?main函数返回调用exit调用_exitexit和_exit的区别进程等待进程等待的重要性进程等待的函数waitwaitpid进程退出…

uniapp中条件编译

官方&#xff1a;https://uniapp.dcloud.net.cn/tutorial/platform.html#%E8%B7%A8%E7%AB%AF%E5%85%BC%E5%AE%B9 #ifndef H5 代码段… #endif 表示除了H5其他都可以编译 #ifdef H5 代码段… #endef 表示只能编译H5&#xff0c;其他的都不能编译 其他编译平台请查看官方文档。 …

连接器产业深度分析报告,国产化替代如何突出重围?(附厂商名录)

前言 2022年3-4月&#xff0c;上海疫情的封城举措&#xff0c;使得其它地区连接器类产品难以进入上海产业链&#xff0c;车载连接器的终端供应受阻&#xff0c;最终影响到全国多家车企生产&#xff1b; 同年12月&#xff0c;欧洲理事会批准—2024年12月28日之前&#xff0c;各类…

MySQL数据库调优————索引调优技巧

长字段的索引调优 当某张表需要给一个长字段创建索引时&#xff0c;因为索引长度越长&#xff0c;效率越差&#xff0c;所以我们需要对其进行优化。 创建额外的长字段的Hash值列 当长字段需要创建索引时&#xff0c;我们可以为其创建额外的一列&#xff0c;用其Hash值作为值…

如何利用Power Virtual Agents机器人实现成绩查询服务

今天我们继续介绍如何利用Power Virtual Agents来实现成绩查询服务。设计思路是在PVA聊天机器人的对话框中输入学生的姓名和学号来进行成绩的查询。首先&#xff0c;在Microsoft 365的OneDrive中制作一个Excel格式的成绩单。 可以将学生的学号、姓名、各学科成绩进行添加。 在P…

【初探人工智能】2、雏形开始长成

【初探人工智能】2、雏形开始长成【初探人工智能】2、雏形开始长成安装Flask封装Web接口雏形设置接收参数功能验证聊天写代码代码补全生成图片写在后面笔者初次接触人工智能领域&#xff0c;文章中错误的地方还望各位大佬指正&#xff01; 【初探人工智能】2、雏形开始长成 在…

限时活动|凭徽章领披萨大奖,玩转Moonbeam治理论坛

动动手指&#xff0c;无需每天打卡&#xff0c;用刷手机的零碎时间领一份Web3惊喜&#xff01; 本次挑战的目标是鼓励大家参与社区治理、熟悉论坛操作。有关参与方式和原因的信息在Twitter上共享&#xff1a;有兴趣可以和ThinkWildCrypto一起探索论坛以解锁其功能、了解最近和正…

【虹科干货】如何有效运用虹科任意波形发生器工作模式?

图 1&#xff1a;显示从存储器到输出的数据路径的 AWG 概念框图 01引言 任意波形发生器 (AWG) 的强大功能之一是它们可以生成几乎无限数量的波形。 AWG 的工作模式控制这些波形输出方式的时序。 在本应用说明中&#xff0c;我们将研究虹科Spectrum M4i.66xx 系列 AWG 工作模式…

JVM的GC机制和常见GC算法

文章目录[toc]1. 堆内存的分代2. GC分类3. 什么是GC3.1 需要GC的内存区域3.2 GC回收的对象3.3 判断对象存活的两种算法3.3.1 引用计数3.3.2 可达性分析3.4 什么时候触发GC4. 常见的GC算法4.1 标记-清除算法4.2 复制算法4.3 标记-压缩算法1. 堆内存的分代 堆中内存分为新生代和老…

String类 [上]

一、编码的基础介绍 编码&#xff1a;是信息从一种形式或格式转换为另一种形式的过程。 ASCLL 编码表:主要表示的是英文的编码表 Unicode&#xff1a;是为了解决传统的字符编码方案的局限而产生的&#xff0c;它为每种语言中的每个字符设定了统一并且唯一的二进制编码二进制编码…

小白式linux系统怎么安装宝塔面板

有很多小白同学问我linux系统服务器怎么远程连接。那么今天我们重点来教教大家如何用电脑远程服务器配上图文教程&#xff0c;让不懂的新手小白一看就会&#xff0c;分分钟上手教程怎么安装宝塔面板&#xff1f;这个其实很简单接下来跟着我操作。以linux centos7.6 举例Centos安…

[计算机操作系统(慕课版)]第二章 进程的描述与控制(学习笔记)

2.1 前驱图和程序执行 2.1.1 前驱图 前驱图是指一个有向无循环图可记为DAG前驱图用于描述进程之间执行的先后顺序。前驱图的每个节点用来表示一个进程或程序段乃至一条语句节点间的有向边表示两个节点之间存在的偏序或前驱关系。进程或程序之间的前驱关系可用→来表示。如果进…

有了这些接口测试用例+工具,测试效率想不提升都难

写在前面&#xff1a;在日常开发过程中&#xff0c;有人做前端开发&#xff0c;有人负责后端开发。接口的主要作用就是连接前后台。但是&#xff0c;由于前端和后端开发的速度可能不一样&#xff0c;尤其是后端开发好了&#xff0c;但前端还未开发。这种时候我们需要做接口测试…

【原创】java+swing+mysql银行ATM管理系统

本文主要介绍使用javaswingmysql去设计一个银行ATM管理系统&#xff0c;模仿实现存款、取款、转账、余额查询等功能。 功能分析&#xff1a; 隐含ATM管理系统一般分为管理员和用户角色&#xff0c;管理员可以进行用户管理、账单管理&#xff0c;用户可以进行转取存款等功能如…

面试不到10分钟就被赶出来了,问的实在是太变态了...

干了两年外包&#xff0c;本来想出来正儿八经找个互联网公司上班&#xff0c;没想到算法死在另一家厂子。 自从加入这家外包公司&#xff0c;每天都在加班&#xff0c;钱倒是给的不少&#xff0c;所以也就忍了。没想到11月一纸通知&#xff0c;所有人不许加班&#xff0c;薪资…