一、 需求说明
签名验证是一种技术,用于确保数据完整性和身份验证。在Java应用程序中,签名通常是由开发人员提供的一个字符串,它基于请求的内容和一些密钥信息生成。这个签名可以被认为是一种指纹,它唯一地标识了请求的内容,同时也确保了请求的完整性。在接收到请求后,应用程序会使用相同的密钥信息和相同的算法来生成签名,然后将其与请求中提供的签名进行比较。如果两个签名匹配,则请求被认为是有效的。
这种技术可以有效地防止恶意请求和数据篡改。如果没有签名验证机制,攻击者可能会伪造请求或篡改数据,这可能会导致安全漏洞和数据泄露。因此,请求AppKey签名验证是一种重要的安全机制,它可以帮助开发人员保护他们的应用程序免受未经授权的访问。
二、 软件详细设计
本章节主要介绍软件详细设计过程、主要包含软件结构详细设计、功能流程详细设计、各个模块调用时序图,以及各接口详细设计;
2.1、软件结构详细设计
通用签名校验软件结构如图:1所示
图1:软件结构图
签名验证软件主要包含三大功能:
- 1、签名切面验证,提供方法注解方式验证请求签名是否正确;
- 2、认证Filter,提供请求Filter验证请求头签名是否正确;
- 3、Feign、RestTemplate远程调用自动添加签名请求头;
签名认证一般会包含两个主要功能提供签名校验方法和发送签名请求;
软件从下至上主要分三部分功能:
1、 基础接口层
- SignValidator:签名参数校验,验证签名参数是否合法,是否缺少签名参数,以及参数签名是否正确;
- AppKeyGenerator: 给签名工具类使用,用于生成AppKey、AppSecret等。
- AopLifecycle:在签名校验前后添加自定义业务逻辑,需要根据自己业务确定是否需要覆盖;
- AppSecretProvider: 传入AppKey获取该AppKey对应的AppSecret,默认采用配置文件方式读取密钥对;可根据自己业务逻辑覆盖;
- AuthLifecycle: 开启认证拦截器可用过该回调接口处理自己业务逻辑,如签名验证通过后自动映射为当前登录用户的Token;
- SignAlogrithm:签名算法提供类,根据签名参数计算签名,用于生成
2、 通用功能层
- SignAop: 通过方法注解的方式添加请求参数签名校验,常用场景如开发某个Controller为对外暴露接口添加@OpenApi注解可对请求的参数进行签名校验;
- AuthFilter:认证过滤器,提供认证级别的签名校验;业务场景如在系统通过签名方式完成认证,即可通过启用该过滤器完成签名认证;需要根据自己业务逻辑完成认证相关代码;
- FeignInterceptor:远程调用签名请求头自动添加拦截器,支持Feign和RestTemplate 两种方式使用;
3、 自动注入层
- EnableAuthFilter:启用认证Filter;
- EnableFeignInterceptor:启用Feign远程调用请求拦截器;
- EnableRestTemplateInterceptor: 启用RestTemplate请求签名拦截器;
- 默认情况加只会注入基础服务类,也就是AOP签名切面及其相关基础服务;AuthFilter、Feign、RestTemplate 相关实体Bean都不会注册;
2.2、功能流程详细设计
本章节详细介绍软件具体工作流程。详细工作时序图如图2所示:
图2: AOP签名校验时序图
AOP切面为最基础的接口签名使用方式,这种方式比较简单也利于理解,但是缺点是不能添加权限控制,需要开发的接口都需要添加@OpenApi 注解;这种模式也是默认启用自动注册到SpringBean容器中的;使用一般需要以下几步:
1、 系统业务拦截器中放行需要签名的接口,一般会采用通用前缀如:/openapi/**
2、 配置或注册AppSecret;默认支持配置方式,通过在yml文件中配置如下配置:
spring:
open-api:
keys:
- appKey: xxxx
appSecret: xxx
也可以根据自己业务逻辑自行实现AppSecretProvider接口并将Bean注入到Spring容器中;
3、 签名认证回调接口实现,这一部分需要根据自身业务添加相应的业务代码;如果添加请求TraceId,设置MDC请求上下文等;提供默认实现,根据自身业务需求注册回调Bean;
4、 调用业务逻辑
5、 业务执行完成回调:如清理MDC上下文等;
通常添加了签名接口,一般都会需求请求相关签名接口,请求签名接口需要按照签名规则添加相应的请求头和签名请求头;以下展示Feign请求自动添加签名时序图:
图3 FeignInterceptor调用时序图
虽然这种调用逻辑比较简单,但需要注册图中FeignClient需要单独配置拦截器,及拦截器不能注册成全局拦截器,只需在需要的FeignClient上配置;RestTemplate 与Feign远程调用过程类型,但也需要注意不能使用全局对象,需要使用单独的签名RestTemplate对象;
案例1: AOP方式使用
1.1 接口暴漏
1、在Filter 中 添加放行URL,这里不添加了
2、在Controller 方法中添加注解@OpenApi
@OpenApi
@PostMapping(value = "/open/saveUser")
public HttpResponse<Boolean> saveUser(@RequestBody @Validated UserInfo user) {
return userService.saveUser(user);
}
3、引入Maven依赖(示例):
<dependency>
<groupId>com.gxf</groupId>
<artifactId>common-openapi</artifactId>
</dependency>
4、添加配置:
# 开放接口 appKey
spring.open-api.keys[0].app-key=8YA26xxxx
spring.open-api.keys[0].app-secret=xxxxx
1.2 Feign远程调用
1、添加配置:
spring.open-api.feign.appKey: xxxx
spring.open-api.feign.appSecret: xxxx
2、启用FeignClient
@Configuration
// 启用该注解则全局Feign调用都会自动添加请求头,(不推荐)
@EnableOpenApiFeignInterceptor
// 注意 如果已经启动则不需要 再次添加该注解
@EnableFeignClients(basePackages = "com.openapi")
public class FeignClientConfig {
}
3、如果启用EnableOpenApiFeignInterceptor就不需要如下代码了:
// 自定义配置,注意不能注册到Spring容器中
public class MyFeignClientConfiguration implements FeignClientConfigurer {
/**
* Feign 远程调用签名验证器
*
* @return 结果
*/
@Bean
public FeignSignRequestInterceptor feignSignRequestInterceptor() {
return new FeignSignRequestInterceptor();
}
}
// 使用自定义Feign配置
@FeignClient(name="MyOpenApi", url = "${spring.open-api.url}", configuration = MyFeignClientConfiguration.class)
public interface MyOpenApi {
}