《历史代码分析》1、接口安全校验-拦截器的利用

打印 上一主题 下一主题

主题 844|帖子 844|积分 2542

1、接口安全校验-拦截器的利用


本系列《历史代码分析》为工作中遇到具有代表性的代码,已做脱敏处置惩罚。今天我们讲一下接口安全检验,利用到Spring中的拦截器。
请先看下面代码:
  1. package tech.xueyao.filter.interceptor;
  2. import tech.xueyao.contant.properties.SystemProperties;
  3. import tech.xueyao.exception.NoPermissionException;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import org.apache.commons.codec.digest.DigestUtils;
  7. import org.apache.commons.lang.StringUtils;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.stereotype.Component;
  12. import org.springframework.web.servlet.HandlerInterceptor;
  13. import org.springframework.web.servlet.ModelAndView;
  14. /**
  15. * @Note:
  16. * @auther: Simon.Xue
  17. * @create: 2019/11/8 12:07
  18. */
  19. @Component
  20. public class CheckSignInterceptor implements HandlerInterceptor {
  21.   private static Logger logger = LoggerFactory.getLogger(CheckSignInterceptor.class);
  22.   @Autowired
  23.   private SystemProperties systemProperties;
  24.   @Override
  25.   public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
  26.       Exception arg3) throws Exception {
  27.   }
  28.   @Override
  29.   public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
  30.       ModelAndView arg3) throws Exception {
  31.   }
  32.   @Override
  33.   public boolean preHandle(HttpServletRequest req, HttpServletResponse response, Object object)
  34.       throws Exception {
  35.     String token = req.getHeader("token");
  36.     String current_timestamp = req.getHeader("current-timestamp");
  37.     String nonce_str = req.getHeader("nonce-str");
  38.     String sign = req.getHeader("sign");
  39.     logger.debug("token = {}, current_timestamp = {}, nonce_str = {}, sign = {}"
  40.         , token, current_timestamp, nonce_str, sign);
  41.     if (StringUtils.isEmpty(current_timestamp)
  42.         || current_timestamp.length() != 13
  43.         || StringUtils.isEmpty(nonce_str)
  44.         || nonce_str.length() < 16
  45.         || StringUtils.isEmpty(sign)) {
  46.       //返回调用方,没有接口权限
  47.       logger.error("返回调用方信息:没有接口权限");
  48.       throw new NoPermissionException("没有接口访问权限");
  49.     }
  50.     // 验证sign签名
  51.     String apiSecurityKey = systemProperties.getSecurity().getMd5ValidKey();
  52.     token = StringUtils.isEmpty(token) ? "" : token;
  53.     String waitSignStr =
  54.         token + current_timestamp.substring(0, 10) + nonce_str.substring(0, 16) + apiSecurityKey;
  55.     String mySign = DigestUtils.md5Hex(waitSignStr);
  56.     if (!mySign.equalsIgnoreCase(sign)) {
  57.       logger.error("调用接口时,验证签名:不一致");
  58.       throw new NoPermissionException("签名不一致");
  59.     }
  60.     return true;
  61.   }
  62. }
复制代码
以下是对这段 Java 代码的分析:
1. 代码概述

这段代码界说了一个名为CheckSignInterceptor​的拦截器,它实现了 Spring 的HandlerInterceptor​接口。该拦截器的重要功能是在处置惩罚 HTTP 请求之前,对请求头中的签名信息进行验证,以确保请求具有正当的访问权限。
这段代码是界说一个拦截器,实现了HandlerInterceptor​接口,重要是在处置惩罚HTTP请求之前,对请求头的中签名信息进行校验,只有检验通过,才气访问接口。注意,肯定有不需要检验的接口,如登录功能,所以需要设置忽略拦截路径,本文不先容。
2. 类界说和注解
  1. @Component
  2. public class CheckSignInterceptor implements HandlerInterceptor {
复制代码

  • ​@Component​:这是 Spring 的注解,说明该类为一个组件,步伐启动时,Spring会自己扫描管理它。
  • ​implements HandlerInterceptor​,需要实现接口中的三个方法。
3. HandlerInterceptor​方法实现

​afterCompletion​方法,该方法在接口请求完成后调用。
​postHandle​方法,该方法在接口请求后、视图渲染之前调用。
​preHandle​方法,该方法在接口请求前调用,因为我们需要判定是否有权限访问接口,所以我们要在之前做校验判定。
  1. @Override
  2. public boolean preHandle(HttpServletRequest req, HttpServletResponse response, Object object)
  3.     throws Exception {
  4.   String token = req.getHeader("token");
  5.   String current_timestamp = req.getHeader("current-timestamp");
  6.   String nonce_str = req.getHeader("nonce-str");
  7.   String sign = req.getHeader("sign");
  8.   logger.debug("token = {}, current_timestamp = {}, nonce_str = {}, sign = {}"
  9.       , token, current_timestamp, nonce_str, sign);
  10.   if (StringUtils.isEmpty(current_timestamp)
  11.       || current_timestamp.length() != 13
  12.       || StringUtils.isEmpty(nonce_str)
  13.       || nonce_str.length() < 16
  14.       || StringUtils.isEmpty(sign)) {
  15.     //返回调用方,没有接口权限
  16.     logger.error("返回调用方信息:没有接口权限");
  17.     throw new NoPermissionException("没有接口访问权限");
  18.   }
  19.   // 验证sign签名
  20.   String apiSecurityKey = systemProperties.getSecurity().getMd5ValidKey();
  21.   token = StringUtils.isEmpty(token) ? "" : token;
  22.   String waitSignStr =
  23.       token + current_timestamp.substring(0, 10) + nonce_str.substring(0, 16) + apiSecurityKey;
  24.   String mySign = DigestUtils.md5Hex(waitSignStr);
  25.   if (!mySign.equalsIgnoreCase(sign)) {
  26.     logger.error("调用接口时,验证签名:不一致");
  27.     throw new NoPermissionException("签名不一致");
  28.   }
  29.   return true;
  30. }
复制代码
上面代码步骤

  • 从请求头中获取token​、current-timestamp​、nonce-str​和sign​信息。
  • 检查请求头信息,假如current-timestamp​为空或长度不为 13,nonce-str​为空或长度小于 16,sign​为空,则抛出NoPermissionException​异常,代表没有接口权限。
  • 从设置类SystemProperties​中获取加密密钥getMd5ValidKey,​此密钥为固定的常量。
  • 根据事先指定签名的规则,拼接字符串waitSignStr​,并利用 MD5 算法天生最终的签名mySign​。
  • 用天生的签名mySign​和请求头中的传来的签名sign​比较,假如不一致,则抛出NoPermissionException​异常,代表没有接口权限。
  • 假如全部校验都通过,则返回true​,代表有接口权限,继承执行业务代码。
6. 总结

在不同的公司中,我都看到相似的业务逻辑,都会对接口做一些签名校验,本篇文章中,每次都要盘算签名,这种方法并不很好,建议把天生的签名信息放在Redis缓存中,这是如今比较流行的做法。



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

来自云龙湖轮廓分明的月亮

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表