上述用postman工具模拟客户端请求接口,
实现的需求: 必须在head里有正确的签名, 才可以访问系统中的接口,否则,进行拦截
实现方案:先给用户生成一个签名,提供给用户,用户对签名进行加密后,请求头auth_signature携带加密后的签名访问接口,拦截器对加密后的签名进行解密,判断签名是否正确,是否过期,签名是否已进行认证等业务逻辑,都验证通过后方可访问系统中的接口
步骤:
1、先生成 appKey和secret提供给用户:
String appKey = IdUtil.nanoId(12); String secret = IdUtil.fastSimpleUUID();
2、用户对签名进行加密后,访问请求头里,如上图postman示例:
public static void main(String[] args) { String appKey = "DRUEWDRUEWDRUEWDRUEW"; String secret = "5fa055e7f2f2d45fa055e7f2f2d4"; long timestamp = System.currentTimeMillis(); 模拟签名加密: String lisi = SecureUtil.aes(SecureUtil.md5(new DateTime().toString("yyyyMMdd")).getBytes()).encryptHex(appKey+"&"+secret+"&"+timestamp); System.out.println(lisi); 模拟签名解密: String s = SecureUtil.aes(SecureUtil.md5(new DateTime().toString("yyyyMMdd")).getBytes()).decryptStr(lisi); System.out.println(s); }
3:自定义个拦截器,进行解密,判断解密后的是否符合要求:
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Resource
private OpenApiAuthInterceptor openApiAuthInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//开放api拦截器
registry.addInterceptor(openApiAuthInterceptor).addPathPatterns("/**/openApi/**");
}
@Slf4j
@Component
public class OpenApiAuthInterceptor implements HandlerInterceptor {
@Resource
private OpenapiAuthManageBiz openapiAuthManageBiz;
@Resource
private OpenApiRequestRecordMapper openApiRequestRecordMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String contextPath = request.getServletPath();
//先获取请求头中的签名
String signature = request.getHeader("auth_signature");
if(StrUtil.isEmpty(signature)){
//签名为空的情况
setResponse(response,401,"请求接口失败,签名不可为空");
return false;
}
//对签名进行解密
try {
String s = SecureUtil.aes(SecureUtil.md5(new DateTime().toString("yyyyMMdd")).getBytes()).decryptStr(signature);
String[] split = s.split("&");
if(split.length < 3){
setResponse(response,401,"请求接口失败,签名有误");
return false;
}
//分别获取 appKey,secret和请求接口时间戳
String appKey = split[0];
String secret = split[1];
Long timestamp = Convert.toLong(split[2]);
//校验签名的时效性
if((System.currentTimeMillis()-2*60*1000)>timestamp){
setResponse(response,401,"请求接口失败,签名已过期");
return false;
}
//校验 appKey, secret 以及访问接口权限
OpenapiAuthManage openapiAuthManage = new OpenapiAuthManage();
String checkResult = openapiAuthManageBiz.checkAuth(appKey, secret, contextPath, openapiAuthManage);
if(StrUtil.isNotEmpty(checkResult)){
setResponse(response,401,checkResult);
return false;
}
//接口调用记录
saveRequestRecord(request, contextPath, openapiAuthManage);
} catch (Exception e) {
log.error("请求开放接口签名异常,{}",e.getMessage());
setResponse(response,401,"请求接口失败,签名有误");
return false;
}
return true;
}}
public String checkAuth(String appKey, String secret, String apiPath, OpenapiAuthManage openapiAuthManage) {
//根据appKey获取授权
OpenapiAuthManage authByAppKey = getAuthByAppKey(appKey);
BeanUtil.copyProperties(authByAppKey,openapiAuthManage);
if (authByAppKey == null || !authByAppKey.getStatus().equals(1)) {
return "请求接口失败,appKey不存在或已被停用";
}
//校验secret
if (!authByAppKey.getSecret().equals(secret)) {
return "请求接口失败,无效的secret";
}
//校验接口权限
if (authByAppKey.getApiRange().equals(0)) {
Boolean aBoolean = checkApiPermission(authByAppKey, apiPath);
if (!aBoolean) {
return "请求接口失败,请求了权限之外的接口或停用的接口";
}
}
return "";
}
public OpenapiAuthManage getAuthByAppKey(String appKey){
Example example = new Example(OpenapiAuthManage.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("appKey",appKey);
List<OpenapiAuthManage> list = mapper.selectByExample(example);
if (CollUtil.isNotEmpty(list)){
return list.get(0);
}
return null;
}
public Boolean checkApiPermission(OpenapiAuthManage authByAppKey,String apiPath){
String applyApis = authByAppKey.getApplyApis();
List<Map> list = JsonUtils.jsonToList(applyApis, Map.class);
if(CollUtil.isNotEmpty(list)){
for (Map map : list) {
if(apiPath.equals(map.get("path"))){
//校验接口是否被停用
//根据api路径获取api
OpenApi openApi = openApiBiz.queryApiByPath(apiPath);
if(openApi.getStatus().equals(1)){
return true;
}
}
}
}
return false;
}