【springmvc系】利用RequestBodyAdviceAdapter做接口鉴权

2023-11-10

需求

有个简单的需求,对于第三方接口我们需要做个简单的鉴权机制,这边使用的是非对称性加密的机制。我们提供三方公钥,他们通过公钥对接口json报文使用加密后的报文请求,我们通过对接收过来的请求某一个加密报文字段来进行RSA解密校验

考虑到日后方便其他接口使用,我这边使用了拦截自定义注解+RequestBodyAdvice机制来处理。话不多说,来实操一把

定义自定义注解类

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


}

RAS工具类

pom文件引入依赖

      <dependency>
            <groupId>com.github.shalousun</groupId>
            <artifactId>common-util</artifactId>
            <version>1.9.2</version>
        </dependency>
import com.power.common.util.RSAUtil;

import java.util.Map;


public class RSATool {


    /**
     * 随机生成一对公私钥
     */
    public static Map<String, String> generatorRsaPariKey() {
        return RSAUtil.createKeys(1024);
    }

    /**
     * 通过公钥来加密获取对应base64字符串
     *
     * @param sourceData
     * @param publicKey
     * @return
     */
    public static String encryptStr(String sourceData, String publicKey) {
        return RSAUtil.encryptString(sourceData, publicKey);
    }


    /**
     * 通过RAS 公钥加密的字符串 使用私钥来解密
     *
     * @param encryptStr
     * @param privateKey
     * @return
     */
    public static String decryptStr(String encryptStr, String privateKey) {
        return RSAUtil.decryptString(encryptStr, privateKey);
    }


}

私钥配置类

@Configuration
@Data
public class ThridApiInfoConfig {

    @Value("${api.auth.privateKey}")
    public String authenticationThirdPrivateKey;
}

 第三方请求加密后封装报文类

@Data
public class BaseEncryptReq {

    private String encryptBoyStr;

    private String reqSource;

}

自定义异常类

@Setter
@Getter
public class  NoPassException extends RuntimeException {

    private String message;

    public NoPassException(String message) {
        this.message = message;
    }


}

RequestBodyAdvice这里类能干什么

对@RequestBody进行增强处理,比如所有请求的数据都加密之后放在 body 中,在到达 controller 的方法之前,需要先进行解密,那么就可以通过 RequestBodyAdvice 来进行统一的解密处理,无需在 controller 方法中去做这些通用的操作。

自定义的类需要实现 RequestBodyAdvice 接口,但是这个接口有个默认的实现类 RequestBodyAdviceAdapter,相当于一个适配器,方法体都是空的,所以我们自定义的类可以直接继承这个类,更方便一些

继承RequestBodyAdviceAdapter类

@Slf4j
@ControllerAdvice
public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter {
    @Autowired
    private ThridApiInfoConfig thridApiInfoConfig;

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.getMethod().isAnnotationPresent(AuthenticationThird.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        String encoding = "UTF-8";
        InputStream inputStream = null;
        try {
            //step1 获取http请求中原始的body
            String body = IOUtils.toString(inputMessage.getBody(), encoding);
            //step2 将对应字符串转为bean
            BaseEncryptReq baseEncryptReq = JSON.parseObject(body, BaseEncryptReq.class);

            if (!StringUtil.isNotBlank(baseEncryptReq.getEncryptBoyStr())) {
                throw new NoPassException("接口加密报文不能为空");
            }
            if (!StringUtil.isNotBlank(baseEncryptReq.getReqSource())) {
                throw new NoPassException("接口请求来源参数不能为空");
            }

            //todo 判断来源
            if (StringUtil.isNotBlank(baseEncryptReq.getReqSource()) && !"xxx".equals(baseEncryptReq.getReqSource())) {
                throw new NoPassException("接口请求来源参数不合法");
            }

            //step3 解密baseEncryptReq.encryptBoyStr属性,进行RSATool解密
            String decryptBody = RSATool.decryptStr(baseEncryptReq.getEncryptBoyStr(), thridApiInfoConfig.getAuthenticationThirdPrivateKey());
            //step 4 将解密之后的body数据重新封装为HttpInputMessage作为当前方法的返回值
            inputStream = IOUtils.toInputStream(decryptBody, encoding);
        } catch (NoPassException ex) {
            log.error("DecryptRequestBodyAdvice 处理解密异常");
            throw new NoPassException(ex.getMessage());
        } catch (Exception ex) {
            log.error("DecryptRequestBodyAdvice 处理解密异常");
            throw new NoPassException("接口解密异常");
        }
        InputStream finalInputStream = inputStream;
        return new HttpInputMessage() {
            @Override
            public InputStream getBody() throws IOException {
                return finalInputStream;
            }

            @Override
            public HttpHeaders getHeaders() {
                return inputMessage.getHeaders();
            }
        };
    }

}

 定义测试controller类

@Slf4j
@RestController
@RequestMapping("/testController")
public class TestController {


    @AuthenticationThird
    @PostMapping(value="/testAuth")
    public ResultDto testAuth(@RequestBody String param){
        log.info("解密后的报文:{}",param);
        return ResultDto.builder().success(true).message("测试鉴权").build();
    }
}

定义全局异常处理类

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理 NoPassException 异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(NoPassException.class)
    @ResponseBody
    public ResultDto handleNoPassException(NoPassException e) {
        log.error("自定义NoPassException异常:{},{}", e.getMessage(), e);
       return  ResultDto.builder().success(false).message(e.getMessage()).build();
    }

接口响应实体类

@Data
public class ResultDto {
    private Boolean success;
    private String  message;
}

测试效果

我们先用之前的RSATool工具类对报文通过公钥来加密

 控制台输出

 

 修改下加密报文

 至此就完成了一个简单通用的接口鉴权

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【springmvc系】利用RequestBodyAdviceAdapter做接口鉴权 的相关文章

随机推荐

  • Kotlin柯里化——函数调用链

    一 首先看一个小例子 做个铺垫 package net println kotlin chapter5 currying author wangdong description 柯里化 函数调用链 定义一个Hello的方法 传入三个参数 返
  • Android JNI(一):JNI基础概念

    本文讲述 NDK和JNI是什么 JNI的原理 JNI开发流程的步骤 认识JNI相关的代码语法 名称概念 什么是NDK NDK 其中NDK的全拼是 Native Develop Kit Android NDK 就是一套工具集合 允许你使用C
  • 蓝桥杯python组如何准备

    在蓝桥杯的程序设计比赛里新增加了python组 这是一个全新的组别 目前蓝桥杯官网已经开通了python的练习平台 链接http dasai lanqiao cn 如何准备2020年蓝桥杯python程序设计呢 我分为四个部分讲解 了解这四
  • AI无敌?人类的反击静悄悄。

    前几年 alphago横扫围棋棋坛 人类棋手不得不接受现实 那么 按照AI的进步速度 我们当时也提过火车站台的比喻 呼啸而过 望尘莫及 从此 人类棋手输给AI不再是新闻 而且随着相关论文的发布和国内外各个技术团队的跟进 超越顶尖人类棋手的围
  • Python_单下划线和双下划线

    属性 x 公有变量 x 私有变量 在py中不能完全做到私有 只能说 伪私有 只是一种良好的书写习惯 不希望被其他类或者子类访问 x 后置下划线 避免与py中的关键字冲突 方法 fun 私有方法 无法在外部直接访问 只能允许本身访问 子类也不
  • 目的:ubuntu配置使用opengl - 初探-创建一个空窗口

    目的 ubuntu配置使用openGL 初探 创建一个空窗口 环境 系统 Ubuntu18 04 环境 g 步骤 Ubuntu下使用openGL 搭建配置环境并测试窗口 1 openGL库 需要单独安装 由于本机是vmware虚拟机Ubun
  • 关于CittyEngine处于加载界面死机的解决方法

    1 CityEngine License无法打开 与已安装的ArcGISAdministrator冲突 在安装后破解是打开CityEngine License警告 试图执行不支持的操作 提示 CityEngine可以独立安装 并不一定要安装
  • 使用vector对数据进行排序(动态排序)

    排序思路 头函数 algorithm 中有一个函数是 upper bound start end value 它可以返回区间 start end 中第一个大于等于 value 的值的位置 再加上 vector 中自带的插入函数 insert
  • 电脑安装了lattice diamond后,再安装lattice radiant,若出现lattice radiant license checkout failed如何解决?

    我电脑安装了lattice diamond 再安装lattice radiant 设置完环境变量后 发现lattice radiant仍然会报错 如下图 检查环境变量和license都并没有什么错误 但是就是一直会出现以下情况 后面如何解决
  • UML 类图

    1 概述 目录 1 概述 1 1 UML概念 1 2 类图的概念 2 类的表示方式 2 1 普通类 2 2 抽象类 2 3 接口 3 类与类关系的表示 3 1 关联关系 Association 3 1 1 单向关联 3 1 2 双向关联 3
  • 【小程序】如何实现从底部弹出对话框

    前面两篇两篇文章介绍了如何在小程序中实现上下滑动效果以及如何用 Canvas 绘制一张图片 这一篇作为前两篇的延续 介绍如何从底部弹出一个对话框 相比而言 底部弹出对话框的功能比较通用 因此非常适合定义成组件 component 先来看一下
  • 【学习记录贴】Vue+Element-UI富文本编辑框及插入图片

    本贴会涉及以下几个技术点 Vue Element UI实现富文本编辑框 以及文本编辑框中事件拦截 插入图片 Element UI限制上传图片后 隐藏上传按钮 官网上是没有这个方法的 可以通过上传到指定张数后隐藏上传按钮来实现 form表单验
  • MyBatis-Plus删除操作知识点总结

    系列文章目录 Mybatis Plus知识点 MyBatis MyBatis Plus的基础运用 心态还需努力呀的博客 CSDN博客 Mybatis Plus SpringBoot结合运用 心态还需努力呀的博客 CSDN博客MyBaits
  • VScode自由切换输出结果窗口,输出到“终端”和“调试控制台”

    Author xiaozhu sai 软件 Visual Studio Code 点击右边的齿轮按钮 打开launch json文件 注意 console 属性即可 具体见一下代码 使用 IntelliSense 了解相关属性 悬停以查看现
  • C++ sort函数自定义排序规则

    在使用vector容器时经常要进行排序 使用排序函数sort非常方便 但是之前都是简单调用sort v begin v end 没有自定义排序规则使用sort函数的额第三个参数 下面对sort总一个简单总结 头文件 include
  • 计算机网络第2章(物理层)

    B站视频 计算机网络微课堂 有字幕无背景音乐版 网址 https www bilibili com video BV1c4411d7jb p 61 目录 2 1 物理层的基本概念 2 2 物理层下面的传输媒体 导引型传输媒体 非导引型传输媒
  • Vue弹窗传值

    场景 点击新增后 需要将这个页面的分类Id传到弹窗页面 新增的时候绑定这个分类 步骤 1 列表页面中弹窗标签中绑定 classifyId this classify
  • 演唱会为什么总是抢不到票?用Python做一个自动抢票脚本!想看谁的就看谁的!

    大麦网 是中国综合类现场娱乐票务营销平台 业务覆盖演唱会 话剧 音乐剧 体育赛事等领域 但是因为票数有限 还有黄牛们不能丢了饭碗 所以导致了 很多人都抢不到票 那么 今天带大家用Python来制作一个自动抢票的脚本小程序 文章末尾看运行效果
  • Java 基于文本界面的《员工管理系统》

    一 代码实现 1 设计分析 该管理系统使用了5个包 Package 类似于文件夹 1 bean 包含员工类 Employee 2 main 主程序的入口 3 service 主要是 业务逻辑层 的功能实现 4 util 存放工具类 此处存放
  • 【springmvc系】利用RequestBodyAdviceAdapter做接口鉴权

    需求 有个简单的需求 对于第三方接口我们需要做个简单的鉴权机制 这边使用的是非对称性加密的机制 我们提供三方公钥 他们通过公钥对接口json报文使用加密后的报文请求 我们通过对接收过来的请求某一个加密报文字段来进行RSA解密校验 考虑到日后