前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >《历史代码分析》1、接口安全校验-拦截器的使用

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

作者头像
小码农薛尧
发布2025-03-03 15:00:59
发布2025-03-03 15:00:59
4200
代码可运行
举报
文章被收录于专栏:小码农薛尧小码农薛尧
运行总次数:0
代码可运行
本系列《历史代码分析》为工作中遇到具有代表性的代码,已做脱敏处理。今天我们讲一下接口安全检验,使用到Spring中的拦截器。

请先看下面代码:

代码语言:javascript
代码运行次数:0
复制
package tech.xueyao.filter.interceptor;

import tech.xueyao.contant.properties.SystemProperties;
import tech.xueyao.exception.NoPermissionException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;


/**
 * @Note:
 * @auther: Simon.Xue
 * @create: 2019/11/8 12:07
 */
@Component
publicclass CheckSignInterceptor implements HandlerInterceptor {


privatestatic Logger logger = LoggerFactory.getLogger(CheckSignInterceptor.class);

@Autowired
private SystemProperties systemProperties;

@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
      Exception arg3) throws Exception {

  }

@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
      ModelAndView arg3) throws Exception {

  }

@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse response, Object object)
      throws Exception {

    String token = req.getHeader("token");
    String current_timestamp = req.getHeader("current-timestamp");
    String nonce_str = req.getHeader("nonce-str");
    String sign = req.getHeader("sign");
    logger.debug("token = {}, current_timestamp = {}, nonce_str = {}, sign = {}"
        , token, current_timestamp, nonce_str, sign);

    if (StringUtils.isEmpty(current_timestamp)
        || current_timestamp.length() != 13
        || StringUtils.isEmpty(nonce_str)
        || nonce_str.length() < 16
        || StringUtils.isEmpty(sign)) {
      //返回调用方,没有接口权限
      logger.error("返回调用方信息:没有接口权限");
      thrownew NoPermissionException("没有接口访问权限");
    }

    // 验证sign签名
    String apiSecurityKey = systemProperties.getSecurity().getMd5ValidKey();
    token = StringUtils.isEmpty(token) ? "" : token;
    String waitSignStr =
        token + current_timestamp.substring(0, 10) + nonce_str.substring(0, 16) + apiSecurityKey;
    String mySign = DigestUtils.md5Hex(waitSignStr);
    if (!mySign.equalsIgnoreCase(sign)) {
      logger.error("调用接口时,验证签名:不一致");
      thrownew NoPermissionException("签名不一致");
    }

    returntrue;
  }
}

以下是对这段 Java 代码的分析:

1. 代码概述

这段代码定义了一个名为CheckSignInterceptor的拦截器,它实现了 Spring 的HandlerInterceptor接口。该拦截器的主要功能是在处理 HTTP 请求之前,对请求头中的签名信息进行验证,以确保请求具有合法的访问权限。

这段代码是定义一个拦截器,实现了HandlerInterceptor接口,主要是在处理HTTP请求之前,对请求头的中签名信息进行校验,只有检验通过,才能访问接口。注意,肯定有不需要检验的接口,如登录功能,所以需要配置忽略拦截路径,本文不介绍。

2. 类定义和注解

代码语言:javascript
代码运行次数:0
复制
@Component
public class CheckSignInterceptor implements HandlerInterceptor {
  • @Component:这是 Spring 的注解,说明该类为一个组件,程序启动时,Spring会自己扫描管理它。
  • implements HandlerInterceptor,需要实现接口中的三个方法。

3. HandlerInterceptor方法实现

afterCompletion方法,该方法在接口请求完成后调用。

postHandle方法,该方法在接口请求后、视图渲染之前调用。

preHandle方法,该方法在接口请求前调用,因为我们需要判断是否有权限访问接口,所以我们要在之前做校验判断。

代码语言:javascript
代码运行次数:0
复制
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse response, Object object)
    throws Exception {

  String token = req.getHeader("token");
  String current_timestamp = req.getHeader("current-timestamp");
  String nonce_str = req.getHeader("nonce-str");
  String sign = req.getHeader("sign");
  logger.debug("token = {}, current_timestamp = {}, nonce_str = {}, sign = {}"
      , token, current_timestamp, nonce_str, sign);

if (StringUtils.isEmpty(current_timestamp)
      || current_timestamp.length() != 13
      || StringUtils.isEmpty(nonce_str)
      || nonce_str.length() < 16
      || StringUtils.isEmpty(sign)) {
    //返回调用方,没有接口权限
    logger.error("返回调用方信息:没有接口权限");
    thrownew NoPermissionException("没有接口访问权限");
  }

// 验证sign签名
  String apiSecurityKey = systemProperties.getSecurity().getMd5ValidKey();
  token = StringUtils.isEmpty(token) ? "" : token;
  String waitSignStr =
      token + current_timestamp.substring(0, 10) + nonce_str.substring(0, 16) + apiSecurityKey;
  String mySign = DigestUtils.md5Hex(waitSignStr);
if (!mySign.equalsIgnoreCase(sign)) {
    logger.error("调用接口时,验证签名:不一致");
    thrownew NoPermissionException("签名不一致");
  }

returntrue;
}

上面代码步骤

  1. 从请求头中获取tokencurrent-timestampnonce-strsign信息。
  2. 检查请求头信息,如果current-timestamp为空或长度不为 13,nonce-str为空或长度小于 16,sign为空,则抛出NoPermissionException异常,代表没有接口权限。
  3. 从配置类SystemProperties中获取加密密钥getMd5ValidKey,此密钥为固定的常量。
  4. 根据事先指定签名的规则,拼接字符串waitSignStr,并使用 MD5 算法生成最终的签名mySign
  5. 用生成的签名mySign和请求头中的传来的签名sign比较,如果不一致,则抛出NoPermissionException异常,代表没有接口权限。
  6. 如果所有校验都通过,则返回true,代表有接口权限,继续执行业务代码。

6. 总结

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小码农薛尧 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 代码概述
  • 2. 类定义和注解
  • 3. HandlerInterceptor方法实现
  • 6. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档