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

news2024/11/24 16:29:17

需求

有个简单的需求,对于第三方接口我们需要做个简单的鉴权机制,这边使用的是非对称性加密的机制。我们提供三方公钥,他们通过公钥对接口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工具类对报文通过公钥来加密

 控制台输出

 

 修改下加密报文

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

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

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

相关文章

SpringBoot复习:(50)TransactionManager是哪里来的?是什么类型的?

运行结果&#xff1a; 可见它的类型是DataSourceTransactionManager.它是通过自动配置创建的。

iview中table表头内容换行展示

如图效果图展示表头内容换行展示&#xff0c;代码如下&#xff1a; 在data中定义表头column Columns:[ {title: this.labelFn(Name, Name),key: name,align: center,}, ]在methods中定义方法 labelFn (name, str) {// 在需要换行的地方加入换行符 \n &#xff0c;在搭配最底…

去掉数组中头部和尾部的0numpy.trim_zeros()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 去掉数组中头部和尾部的0 numpy.trim_zeros() 选择题 以下说法错误的是? import numpy as np anp.array([0,0,1,2,3,4,0,0]) print("【显示】a:");print(a) print("【执行1】p…

CSAPP Lec01

1. CMU 15213_15513 CSAPP 深入理解计算机系统 Lecture 01 Course Overview 中英字幕_哔哩哔哩_bilibili 从这个课程中可以学到什么&#xff1f;&#xff08;为什么要学这门课&#xff09; Great Reality #1&#xff08;数字类型&#xff09;: Ints are not Integers, Floats…

c语言——颠倒字符串顺序

//颠倒字符串顺序 //列如&#xff1a;我们将runningman利用递归翻转计算。 #include<stdio.h> void reverseSentence(); int main() {printf("字符串输入:");reverseSentence();return 0; }void reverseSentence() {char c;scanf("%c",&c);if(c!…

Java课题笔记~ JSP开发模型

MVC 1.JSP演化历史 1. 早期只有servlet&#xff0c;只能使用response输出标签数据&#xff0c;非常麻烦 2. 后来有了jsp&#xff0c;简化了Servlet的开发&#xff0c;如果过度使用jsp&#xff0c;在jsp中即写大量的java代码&#xff0c;有写html表&#xff0c;造成难于维护&…

档案库房智能管理系统的功能有哪些呢?

档案库房智能管理系统是一个基于人工智能技术的综合性档案管理解决方案&#xff0c;通过自动化、智能化的方式&#xff0c;优化了档案管理流程&#xff0c;提高了工作效率和信息安全性。 1.档案入库管理&#xff1a; 档案信息录入&#xff1a;系统可以通过扫描、识别和自动填写…

UDP/TCP协议

传输层协议 再谈端口号端口号范围划分认识知名端口号两个问题netstatpidof UDP协议UDP协议端格式UDP的特点面向数据报UDP的缓冲区UDP使用注意事项基于UDP的应用层协议 TCP协议TCP协议段格式确认应答(ACK)机制超时重传机制连接管理机制理解 CLOSE_WAIT 状态理解TIME_WAIT状态解决…

开发测试框架一 - 创建springboot工程及基础操作

一、创建及运行方式 1. 从官网导入&#xff1a; 注意&#xff1a;由于我的java版本是1.8&#xff1b;所以选中了spring2.7.14&#xff1b;如果你的java版本是9及以上&#xff0c;选中spring3相关的同时Java 版本也要对应起来 2. 创建第一个get请求 创建Controller package及…

【深入理解C语言】-- 关键字1

&#x1f407; &#x1f525;博客主页&#xff1a; 云曦 &#x1f4cb;系列专栏&#xff1a;深入理解C语言 &#x1f4a8;吾生也有涯&#xff0c;而知也无涯 &#x1f49b; 感谢大家&#x1f44d;点赞 &#x1f60b;关注&#x1f4dd;评论 文章目录 前言一、auto1.1 auto的介…

【MySQL】表的内连和外连

本期我们来谈谈表的连接 目录 一、内连接 二、外连接 2.1 左外连接 2.2 右外连接 一、内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选&#xff0c;我们上期学习的复合查询都是内连接&#xff0c;也是在开发过程中使用的最多的连接查询 使用内连接时…

proteus结合keil-arm编译器构建STM32单片机项目进行仿真

proteus是可以直接创建设计图和源码的&#xff0c;但是源码编译它需要借助keil-arm编译器&#xff0c;也就是我们安装keil-mdk之后自带的编译器。 下面给出一个完整的示例&#xff0c;主要是做一个LED灯闪烁的效果。 新建工程指定路径&#xff0c;Schematic,PCB layout都选择默…

记一次mybatis-plus关于Long和long自动添加过滤条件的bug记录

文章目录 1.前提条件2.相关知识3.原理解释&#xff1a;4.情景复现&#xff1a; 1.前提条件 接口代码是由mybatis-plus封装的page分页 有一个实体类属性类型是long和Long的区别&#xff1a;long会添加一个过滤条件未异常情况&#xff0c;改成Long则不会添加是正常情况,详细情况参…

leetcode358周赛

2815. 数组中的最大数对和 核心思想&#xff1a;维护每一个最大的数字的最大值&#xff0c;然后一边更新最大值&#xff0c;一边统计结果。其中求nums中的每一个数的数位最大值可以用map的方法&#xff0c;我自己做的时候是用的%10&#xff0c;感觉map这种方法很巧妙。 2816. …

如何在iPhone手机上修改手机定位和模拟导航?

如何在iPhone手机上修改手机定位和模拟导航&#xff1f; English 首先&#xff0c;你需要在Mac电脑上下载安装 Location Simulator/定位模拟工具 和 Runner 这两款应用程序。 完成安装后&#xff0c;打开软件&#xff0c;并用USB连接手机设备 修改iPhone手机定位和模拟导航 …

锐捷无线产品运维

目录 登录AP产品 Console登录&#xff08;只可以现场登录&#xff09; Web/Telnet/SSH登录&#xff08;可以现场、远程登录&#xff09; 配置AP的管理地址 通过Web界面远程登录 通过Telnet、SSH等命令行的方式登录 登录AC产品 Console登录&#xff08;只可以现场登录&a…

CSS中的position属性有哪些值,并分别描述它们的作用。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ static⭐ relative⭐ absolute⭐ fixed⭐ sticky⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那…

梯度下降介绍

什么是梯度 梯度是微积分中一个很重要的概念&#xff0c;在单变量的函数中&#xff0c;梯度其实就是函数的微分&#xff0c;代表着函数在某个给定点的切线的斜率&#xff1b;在多变量函数中&#xff0c;梯度是一个向量&#xff0c;向量有方向&#xff0c;梯度的方向就指出了函…

【2023年11月第四版教材】《第4章-信息系统管理(合集篇)》

第4章-信息系统管理之管理方法&#xff08;第四版新增章节&#xff09;&#xff08;第一部分&#xff09; 章节说明1 管理方法1.1 信息系统四个要素1.2 信息系统四大领域1.3 信息系统战略三角1.4 信息系统架构转换1.5 信息系统体系架构1.6 信息系统运行1.7 运行和监控1.8 管理和…

day42时间格式转化

题目描述&#xff1a; 解题思路 1.将字符串给以" "为间隔进行切割然后各个部分进行不同的处理。 字符串切割使用 strtok(char* a, char* b) 函数&#xff0c;该函数返回切割完成后第一个子串的指针。 //切割&#xff0c;返回第一个字串的指针。这里注意&#xff0c;…