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

需求

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

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

定义自定义注解类

java 复制代码
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthenticationThird{


}

RAS工具类

pom文件引入依赖

java 复制代码
      <dependency>
            <groupId>com.github.shalousun</groupId>
            <artifactId>common-util</artifactId>
            <version>1.9.2</version>
        </dependency>
java 复制代码
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);
    }


}

私钥配置类

java 复制代码
@Configuration
@Data
public class ThridApiInfoConfig {

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

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

java 复制代码
@Data
public class BaseEncryptReq {

    private String encryptBoyStr;

    private String reqSource;

}

自定义异常类

java 复制代码
@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类

java 复制代码
@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类

java 复制代码
@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();
    }
}

定义全局异常处理类

java 复制代码
@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();
    }

接口响应实体类

java 复制代码
@Data
public class ResultDto {
    private Boolean success;
    private String  message;
}

测试效果

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

控制台输出

修改下加密报文

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

相关推荐
小江的记录本1 小时前
【JVM虚拟机】堆内存分代模型:年轻代(Eden+Survivor)、老年代、元空间Metaspace(附《思维导图》+《面试高频考点清单》)
java·前端·jvm·后端·python·spring·面试
tedcloud1232 小时前
DeepSeek-TUI部署教程:打造CLI AI助手环境
服务器·人工智能·word·excel·dreamweaver
无情的西瓜皮2 小时前
MCP协议实战:用Python从零搭建一个AI Agent工具服务器(保姆级教程)
服务器·人工智能·python·mcp
万能的知了3 小时前
服务器托管 vs 云主机 vs 裸金属:一个决策故事
运维·服务器·云计算
茫忙然5 小时前
U 盘搭建免驱 Linux 便携系统教程
linux·服务器
木头程序员7 小时前
SSM框架学习笔记
java·开发语言·mysql·spring·maven
李白你好7 小时前
页面资产梳理 · 技术指纹识别 · Spring 端点探测
java·后端·spring
lihao lihao7 小时前
linux匿名管道
linux·运维·服务器
STDD7 小时前
Glances:跨平台系统资源监控,浏览器实时查看服务器状态
运维·服务器
Forget_85508 小时前
HCIA——计算机网络诞生与发展
服务器·网络·计算机网络