api接口大多都支持访问信息的验证,其中参数的排序,加密都是经常用到的。有时候还需要将验证信息放到header中。
将api调用者的参数的key及头信息(时间戳、随机串,调用者标识)按照ascii码升序排列后 用系统中的salt加密 ,得到的签名与调用者传入的签名进行比较。已实现验证合法性的目的。
下面就给大家介绍下我在项目中是如何使用的。大家多提意见。

  1. 创建切片类

    package com.xxx.openapis.annotationsimport java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Inherited
    public @interface OpenAPI {
    }
    
  2. 创建返回结果类

    package com.xxx.openapis.webimport java.io.Serializable;public class AjaxResult implements Serializable {private final int status;private final Object data;private AjaxResult(int status, Object data) {this.status = status;this.data = data;}public static AjaxResult error(int errorCode, String errorMessage) {return new AjaxResult(errorCode, errorMessage);}public static AjaxResult success() {return new AjaxResult(0, null);}public static AjaxResult success(Object data) {return new AjaxResult(0, data);}public int getStatus() {return status;}public Object getData() {return data;}
    }
    
  3. 创建一个实现org.springframework.core.Ordered 的抽象切片类

    package com.xxx.openapis.aspects;import com.xxx.openapis.web.AjaxResult;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.springframework.core.Ordered;import javax.inject.Inject;
    import javax.servlet.http.HttpServletRequest;@Aspect
    public abstract class AbstractAPIAspect implements Ordered {static final String X_API_TIMESTAMP = "x_api_timestamp";//时间戳static final String X_API_NONCE = "x_api_nonce"; //随机串static final String X_API_PARTNERCODE = "x_api_partnercode";//调用者标识static final String X_API_SIGNATURE = "x_api_signature";//签名static final long NONCE_TTL = 20 * 60 * 1000L;//随机串时间范围@InjectHttpServletRequest request;@Around("@within(com.xxx.openapis.annotations.OpenAPI) && execution(public com.xxx.web.AjaxResult *(..))")public Object aound(ProceedingJoinPoint jp) {getLogger().debug("检查API请求");AjaxResult error = check();if (error != null) {getLogger().error("检查API请求,发现问题:{}", error.getData());return error;}try {getLogger().debug("检查API请求,没有发现问题继续");return jp.proceed();} catch (Throwable throwable) {getLogger().error("执行出错", throwable);return AjaxResult.error(500, "执行出错," + throwable.getMessage());}}protected abstract Logger getLogger();protected abstract AjaxResult check();}
    
  4. 创建时间戳检查类继承AbstractAspect抽象类

    package com.xxx.openapis.aspects;import com.xxx.openapis.web.AjaxResult;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;import javax.inject.Named;import static org.springframework.util.StringUtils.hasText;@Aspect
    @Named
    @Slf4j
    public class TimestampChecker extends AbstractAPIAspect {static final int ORDER = 1;//执行顺序@Overrideprotected Logger getLogger() {return log;}@Overrideprotected AjaxResult check() {String header = request.getHeader(X_API_TIMESTAMP);if (!hasText(header)) return AjaxResult.error(401, "未提供请求时间戳");long timestamp;try {timestamp = Long.valueOf(header);} catch (NumberFormatException e) {return AjaxResult.error(401, "请求时间戳应该是系统时间的毫秒数");}if (Math.abs(timestamp - System.currentTimeMillis()) > NONCE_TTL)return AjaxResult.error(403, "时间超过允许的范围,可能是“重播”攻击");return null;}@Overridepublic int getOrder() {return ORDER;}
    }
    
  5. 创建一个随机数检查类继承AbstractAspect抽象类

    package com.xxx.openapis.aspects;import com.xxx.openapis.web.AjaxResult;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.springframework.data.redis.core.StringRedisTemplate;import javax.inject.Inject;
    import javax.inject.Named;
    import java.util.concurrent.TimeUnit;import static org.springframework.util.StringUtils.hasText;@Aspect
    @Named
    @Slf4j
    public class NonceChecker extends AbstractAPIAspect {static final int ORDER = TimestampChecker.ORDER + 1;private static final String KEY_PREFIX = "DISTRIBUTOR.API.NONCE.";@Injectprivate StringRedisTemplate redisTemplate;@Overrideprotected Logger getLogger() {return log;}@Overrideprotected AjaxResult check() {String nonce = request.getHeader(X_API_NONCE);if (!hasText(nonce)) return AjaxResult.error(401, "未提供请求随机数");if (nonceInRedis(nonce)) return AjaxResult.error(403, "已接收相同的请求,怀疑是“重播”攻击。");return null;}private boolean nonceInRedis(String nonce) {if (redisTemplate == null) {getLogger().debug("未启用Redis,忽略此项检查");return false;}if (hasText(redisTemplate.opsForValue().get(KEY_PREFIX + nonce))) return true;redisTemplate.opsForValue().set(KEY_PREFIX + nonce, nonce, NONCE_TTL, TimeUnit.MILLISECONDS);return false;}@Overridepublic int getOrder() {return ORDER;}
    }
    
  6. 创建用户验证类继承AbstractAPIAspect抽象类

    package com.xxx.openapis.aspects;import com.xxx.openapis.models.User;
    import com.xxx.openapis.web.AjaxResult;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;import javax.inject.Inject;
    import javax.inject.Named;import static org.springframework.util.StringUtils.hasText;@Aspect
    @Named
    @Slf4j
    public class UserChecker extends AbstractAPIAspect {static final int ORDER = NonceChecker.ORDER + 1;@Injectprivate UserService userService;@Overrideprotected Logger getLogger() {return log;}@Overrideprotected AjaxResult check() {String userCode = request.getHeader(X_API_PARTNERCODE);if (!hasText(userCode)) {return AjaxResult.error(401, "未提用户商编号");}User user = userService.getUser(userCode);if (user == null) return AjaxResult.error(403, "用户编号错误,不存在此用户");if (user.isLocked()) return AjaxResult.error(403, "用户编号错误,此用户已冻结");request.setAttribute("user", user);return null;}@Overridepublic int getOrder() {return ORDER;}
    }
    
  7. 创建签名验证类继承AbstractAPIAspect抽象类

    package com.xxx.openapis.aspects;import com.xxx.openapis.models.User;
    import com.xxx.openapis.web.AjaxResult;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.springframework.util.StringUtils;import javax.inject.Named;
    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;
    import java.util.Arrays;
    import java.util.Map;
    import java.util.stream.Collectors;import static org.springframework.util.StringUtils.hasText;@Aspect
    @Named
    @Slf4j
    public class SignatureChecker extends AbstractAPIAspect {private static final int ORDER = VendorChecker.ORDER + 1;@Overrideprotected Logger getLogger() {return log;}@Overrideprotected AjaxResult check() {String callerSign = request.getHeader(X_API_SIGNATURE);if (!hasText(callerSign)) return AjaxResult.error(401, "未提供请求签名");User user= (User) request.getAttribute("user");String salt = user.getSalt();String sign = sign(request.getHeader(X_API_TIMESTAMP), request.getHeader(X_API_NONCE), request.getHeader(X_API_PARTNERCODE), request.getParameterMap(), salt);getLogger().debug("收到的签名{}", callerSign);getLogger().debug("计算签名{}", sign);if (!callerSign.equalsIgnoreCase(sign)) return AjaxResult.error(403, "签名错误");return null;}private String sign(final String timestamp, final String nonce, final String partnerCode, final Map<String, String[]> requestParameters, String salt) {//请求参数进行排序,按照ASCII码升序排列String toSign = timestamp+ nonce+ partnerCode+ requestParameters.entrySet().stream().map(entry -> {String key = entry.getKey();String value = Arrays.stream(entry.getValue()).map(this::encode).filter(StringUtils::hasText).sorted().collect(Collectors.joining(","));if (hasText(value)) return key + '=' + value;else return null;}).filter(StringUtils::hasText).sorted().collect(Collectors.joining("&"))+ salt;return DigestUtils.md5Hex(toSign);}private String encode(String value) {try {return URLEncoder.encode(value, "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();return null;}}@Overridepublic int getOrder() {return ORDER;}
    }
    
  8. 创建controller类

    package com.xxx.openapis.conctollers;import com.xxx.openapis.annotations.OpenAPI;import com.xxx.openapis.web.AjaxResult;import static com.xinnet.market.web.AjaxResult.error;
    import static com.xinnet.market.web.AjaxResult.success;@RestController
    @RequestMapping("/api")
    @OpenAPI  //此处重点 加上它相当于告诉系统先走上边的几个检查符合条件后才进控制层
    public class DistributorAPI {@PostMapping("/test")public AjaxResult updatePayStatus(@RequestParam("test") String test) String test) {     try {//调用后端业务逻辑return success();} catch (OrderNotMacherException e) {return error(509, e.getMessage());}}
    }
    

request参数升序排序 md5加密 防重播 header信息 java API接口调用 切片机制实现相关推荐

  1. 按照姓名升序排序的代码_好程序员Java培训分享Java集合的两种排序方法

    好程序员Java培训分享Java集合的两种排序方法,Java集合的工具类Collections中提供了两种排序的方法,分别是: 1.Collections.sort(List list) 2.Coll ...

  2. java md5加密64位_基于Java语言的MD5加密Base64转换方法

    1 importjava.io.IOException;2 importjava.math.BigInteger;3 importjava.security.MessageDigest;4 impor ...

  3. python中sign函数图片_python接口自动化-实现sign签名(MD5加密)

    一,前序 今天在学习sign签名的的时候,了解了下常用的sign加密算法,突然心血来潮,想用python试着将签名生成的通用步骤用代码实现出来,虽然中间经历了一点小波折,请教了开发同事后,按照他给的思 ...

  4. java+filter加密_Javaweb之Filter案例练习-自动登录问题和MD5加密

    自动登录问题和MD5加密 前面已经完成了Filter的自动登录,但是有问题,我们在web.xml中Filter的url-mapping中配置的规则是/*, 也就是这个网站的所有请求都拦截.这肯定不合适 ...

  5. md5加密数据表中的密码php,JSP_使用MD5加密数据库中的用户密码(一),我们知道,现在网络上一般的 - phpStudy...

    使用MD5加密数据库中的用户密码(一) 我们知道,现在网络上一般的网站,稍微完善一点的,往往都需要用户先注册,提供诸如电子邮件.账号.密码等信息以后,成为网站栏目的注册用户,才可以享受网站一些特殊栏目 ...

  6. 对输入进行MD5加密

    使用MD5算法计算用户名字符串的散列值,将计算结果转换成长度32位的十六进制字符串. 然后将字符串中的所有奇数位重新组合,生成新的字符串.这个字符串就是注册码 点击事件: btn_one.setOnC ...

  7. 使用MD5加密数据库中的用户密码介绍

    使用MD5加密数据库中的用户密码介绍 ●前言 我们知道,现在网络上一般的网站,稍微完善一点的,往往都需要用户先注册,提供诸如电子邮件.账号.密码等信息以后,成为网站栏目的注册用户,才可以享受网站一些特 ...

  8. Java工具集-MD5加密工具

    简单工具类 写作初衷:由于日常开发经常需要用到很多工具类,经常根据需求自己写也比较麻烦 网上好了一些工具类例如commom.lang3或者hutool或者Jodd这样的开源工具,但是 发现他们之中虽然 ...

  9. 关于HTML表单用户信息进行MD5加密

    合格的开发者首先要保证用户信息的安全性.通常涉及的就是用户密码.口令等隐秘性信息,可能有部分人会将安全工作交给后台,但在信息从前端传至后台的过程中早已可以被截获.接下来将关于前端提交表单时的MD5加密 ...

最新文章

  1. 硬件重要还是软件重要?一个人能同时学会吗?
  2. Warning: Using a password on the command line interface can be insecure.解决办法
  3. ISP、IAP、ICP的区别!
  4. java解析动态AIS原始数据
  5. WSDL文件生成WEB service server端C#程序
  6. IDEA 中的.iml文件和.idea文件夹 ( 隐藏方式 )
  7. 漫画:跑上百万次代码验证三门问题
  8. STM32学习第二课:STM32c语言基础2
  9. pytorch 学习1
  10. scala map,foreach,flatMap等方法对比
  11. Java poi操作导入导出excel
  12. java树遍历算法_Java递归算法实现目录树的遍历
  13. linux目录分配独立磁盘空间,Linux 分配/home的磁盘空间给根目录
  14. AI智能电话机器人源码搭建揭秘!语音机器人电销机器人源码系统那些事
  15. GJB用于试验的计算机软件,GJB9001C-2017版标准培训课件.ppt
  16. 智慧工厂3D物联网可视化建模管理系统
  17. 线性混合效应模型学习
  18. 兜兜转转还是回到程序员圈子
  19. 那些不回你微信的人都在看什么?
  20. PHPAdmin删除外键约束

热门文章

  1. 第10章第22节:使用Silk互动生成流光艺术图片 [PowerPoint精美幻灯片实战教程]
  2. AppStore(应用商店):从封闭走向开放
  3. HTML5汉堡包西餐美食网站模板
  4. python千锋好还是黑马好_千锋讲师有话说: Python前景好不好?
  5. Oracle学习(五) --- 视图、序列、同义词、索引
  6. 机器学习支持向量机SVM笔记
  7. python2.7.13交叉编译移植到arm
  8. Vue+element ui 上传视频
  9. springBoot实验填报系统
  10. Android系统累次停止运行,安卓手机反应迟钝的原因及解决方法