首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

ApiAuthValve API 鑒權機制深度解析

     編輯:Java 綜合教學

ApiAuthValve API 鑒權機制深度解析

基於阿里巴巴技術體系的 API 鑒權組件設計與實現,涵蓋動態配置、MD5 簽名驗證、時間戳防重放、Pipeline 攔截器鏈整合等企業級安全實踐。

一、背景與基礎組件

1. Autoconfig 動態配置

Maven 插件在打包階段根據環境變量替換模板變量,實現「一次構建,多環境部署」。

<plugin>
  <groupId>com.alibaba.citrus.tool</groupId>
  <artifactId>autoconfig-maven-plugin</artifactId>
  <version>1.2</version>
  <configuration>
    <includeDescriptorPatterns>
      <includeDescriptorPattern>autoconf/auto-config.xml</includeDescriptorPattern>
    </includeDescriptorPatterns>
  </configuration>
  <executions>
    <execution>
      <phase>package</phase>
      <goals><goal>autoconfig</goal></goals>
    </execution>
  </executions>
</plugin>

核心思想:配置模板 + 環境變量 → 具體配置文件。

2. MD5 摘要算法

  • 單向不可逆,適合密碼存儲、簽名驗證
  • 原理:哈希函數將任意長度輸入映射為固定長度摘要
  • Java 使用 DigestUtils.md5Hex(str) (Apache Commons Codec)

二、鑒權架構設計

1. Spring Bean 定義 (component-beans.xml.vm)

<bean id="apiValve" class="com.alibaba.tboss.common.services.login.ApiAuthValve" init-method="initial">
  <property name="includeStr">
    <list><value><![CDATA[^/tboss/web/api/.*\.json$]]></value></list>
  </property>
  <property name="excludeStr"><list></list></property>
</bean>
  • includeStr:需鑒權的 URL 正則
  • excludeStr:模塊內跳過鑒權的 URL
  • init-method="initial":容器啟動時編譯正則模式

2. Pipeline 整合 (pipeline-web.xml)

<valve class="com.alibaba.tboss.common.services.login.ApiAuthValve" />

位於 checkCsrfToken 之前,確保 API 請求優先通過鑒權。

三、核心實現 ApiAuthValve.java

public class ApiAuthValve extends AbstractValve {
    private static final Logger logger = LoggerFactory.getLogger(ApiAuthValve.class);
    
    @Resource ApiUserBo apiUserBo;
    @Autowired HttpServletRequest request;
    @Autowired HttpServletResponse response;
    
    protected static String[] includeStr;
    protected static List includes = new ArrayList<>();
    protected static String[] excludeStr;
    protected static List excludes = new ArrayList<>();
    
    public void initial() {
        if (includeStr != null) {
            for (String s : includeStr) includes.add(Pattern.compile(s.toLowerCase()));
        }
        if (excludeStr != null) {
            for (String s : excludeStr) excludes.add(Pattern.compile(s.toLowerCase()));
        }
    }
    
    @Override
    public void invoke(PipelineContext pipelineContext) throws Exception {
        String uri = request.getRequestURI();
        try {
            if (isInControl(uri)) {
                String apiName = request.getParameter(API_AUTH_NAME);
                String timestamp = request.getParameter(API_AUTH_TIMESTAMP);
                String signature = request.getParameter(API_AUTH_SIGNATURE);
                apiUserBo.checkAuthorization(apiName, timestamp, signature, uri);
            }
        } catch (Exception e) {
            logger.error(uri + " fail - " + e.getMessage(), e);
            // 統一錯誤響應
            ErrorContext ctx = new DefaultErrorContext();
            ctx.addError(ErrorItem.create("rpc_500", "500", 
                "API auth fail: " + e.getMessage()));
            Object result = new DefaultResultGenerator.GenericWebRPCResult(
                "API auth fail", null, ctx);
            response.getWriter().print(JSON.toJSONString(result));
            return; // 終止後續 Valve
        }
        pipelineContext.invokeNext(); // 繼續鏈
    }
    
    private boolean isInControl(String uri) {
        boolean match = false;
        for (Pattern p : includes) {
            if (p.matcher(uri.toLowerCase()).matches()) { match = true; break; }
        }
        if (match) {
            for (Pattern p : excludes) {
                if (p.matcher(uri.toLowerCase()).matches()) { match = false; break; }
            }
        }
        return match;
    }
}

四、業務驗證邏輯 (ApiUserBo.checkAuthorization)

public void checkAuthorization(String apiName, String timestamp, 
                               String signature, String uri) {
    // 1. 參數非空
    if (StringUtils.isAnyBlank(apiName, timestamp, signature))
        throw new ApiAuthException("apiName, timestamp, signature is missing");
    
    // 2. 時間戳格式 & 有效期 (15分鐘)
    Date gmtRequest;
    try {
        gmtRequest = DateUtils.parseDate(timestamp, API_AUTH_TIMEFORMAT);
    } catch (ParseException e) {
        throw new ApiAuthException("Unsupported timestamp format");
    }
    if (Math.abs(System.currentTimeMillis() - gmtRequest.getTime()) >= 15 * 60 * 1000)
        throw new ApiAuthException("Request has been expired");
    
    // 3. 用戶存在性 & 權限
    ApiUser user = getApiUserByName(apiName);
    if (user == null) throw new ApiAuthException("Api user not exist");
    if (!isAuthorized(user, uri)) throw new ApiAuthException("No permission");
    
    // 4. 簽名驗證
    String realCode = decode(user.getCode()); // 解密存儲的密鑰
    String signBase = String.format("%s%s%s", apiName, timestamp, realCode);
    if (!signature.equals(DigestUtils.md5Hex(signBase)))
        throw new ApiAuthException("Signature mismatch");
}

五、安全機制總結

防禦維度實現手段
身份認證apiName + 密鑰對稱加密存儲
防重放攻擊timestamp 有效期 15 分鐘 + 單次使用檢查(可擴展)
完整性校驗MD5(apiName + timestamp + secret) 簽名
授權控制URL 級別白名單 + 用戶權限矩陣
靈活配置正則 include/exclude + Autoconfig 多環境

六、調用方接入示例

// 客戶端生成簽名
String apiName = "partner_001";
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String secret = "shared_secret_from_platform";
String signBase = apiName + timestamp + secret;
String signature = DigestUtils.md5Hex(signBase);

// HTTP 請求參數
GET /tboss/web/api/query.json?apiName=partner_001×tamp=2024-01-15%2010:30:00&signature=abc123...
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved