华为云Api调用怎么生成Authorization鉴权信息,StringToSign拼接流程

请求示例 

Authorization

为了安全,华为云的 Api 调用都是需要在请求的 Header 中携带 Authorization 鉴权的,这个鉴权15分钟内有效,超过15分钟就不能用了,而且是需要调用方自己手动拼接的。

Authorization的格式为

OBS 用户AK:手动生成的Signature

Signature生成

Signature生成流程:

 参考文档:Header中携带签名_对象存储服务 OBS

Signature生成代码

package com.fdw.algorithm.HWCloud;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class SignDemo {
	
    private static final String SIGN_SEP = "\n";
    private static final String OBS_PREFIX = "x-obs-";
    private static final String DEFAULT_ENCODING = "UTF-8";
    private static final List<String> SUB_RESOURCES = Collections.unmodifiableList(Arrays.asList(
        "CDNNotifyConfiguration", "acl", "append", "attname", "backtosource", "cors", "customdomain", "delete",
   	"deletebucket", "directcoldaccess", "encryption", "inventory", "length", "lifecycle", "location", "logging",
   	"metadata", "mirrorBackToSource", "modify", "name", "notification", "obscompresspolicy",  "orchestration", 
   	"partNumber", "policy", "position", "quota","rename", "replication", "response-cache-control", 
   	"response-content-disposition","response-content-encoding", "response-content-language", "response-content-type", 
   	"response-expires","restore", "storageClass", "storagePolicy", "storageinfo", "tagging", "torrent", "truncate",
   	"uploadId", "uploads", "versionId", "versioning", "versions", "website", "x-image-process",
  	 "x-image-save-bucket", "x-image-save-object", "x-obs-security-token", "object-lock", "retention"));
 
    private String ak;
    private String sk;
    // 对字符串进行UTF8编码
    public String urlEncode(String input) throws UnsupportedEncodingException {
        return URLEncoder.encode(input, DEFAULT_ENCODING)
            .replaceAll("%7E", "~") //for browser
            .replaceAll("%2F", "/")
            .replaceAll("%20", "+");
    }
 
    private String join(List<?> items, String delimiter) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < items.size(); i++) {
            String item = items.get(i).toString();
            sb.append(item);
            if (i < items.size() - 1) {
                sb.append(delimiter);
            }
        }
        return sb.toString();
    }
	
    private boolean isValid(String input) {
        return input != null && !input.equals("");
    }

    // 使用访问密钥SK计算HmacSHA1值
    public String hmacSha1(String input) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
        SecretKeySpec signingKey = new SecretKeySpec(this.sk.getBytes(DEFAULT_ENCODING), "HmacSHA1");
        // 获取Mac实例,并通过getInstance方法指定使用HMAC-SHA1算法
        Mac mac = Mac.getInstance("HmacSHA1");
        // 使用访问密钥SK初始化Mac对象
        mac.init(signingKey);
        return Base64.getEncoder().encodeToString(mac.doFinal(input.getBytes(DEFAULT_ENCODING)));
    }

    // 构造StringToSign
    private String stringToSign(String httpMethod, Map<String, String[]> headers, Map<String, String> queries,
        String bucketName, String objectName) throws Exception{
        String contentMd5 = "";
        String contentType = "";
        String date = "";
		
        TreeMap<String, String> canonicalizedHeaders = new TreeMap<String, String>();
		
        String key;
        List<String> temp = new ArrayList<String>();
        for(Map.Entry<String, String[]> entry : headers.entrySet()) {
            key = entry.getKey();
            if(key == null || entry.getValue() == null || entry.getValue().length == 0) {
                continue;
            }
			
            key = key.trim().toLowerCase(Locale.ENGLISH);
            if(key.equals("content-md5")) {
                contentMd5 = entry.getValue()[0];
                continue;
            }
			
            if(key.equals("content-type")) {
                contentType = entry.getValue()[0];
                continue;
            }
			
            if(key.equals("date")) {
                date = entry.getValue()[0];
                continue;
            }
			
            if(key.startsWith(OBS_PREFIX)) {				
                for(String value : entry.getValue()) {
                    if(value != null) {
                        temp.add(value.trim());
                    }
                }
                canonicalizedHeaders.put(key, this.join(temp, ","));
                temp.clear();
            }
        }
	    // 如果header头域中包含x-obs-date,Date参数置空
        if(canonicalizedHeaders.containsKey("x-obs-date")) {
            date = "";
        }	
		
        // 构造StringToSign,拼接HTTP-Verb、Content-MD5、Content-Type、Date
        StringBuilder stringToSign = new StringBuilder();
        stringToSign.append(httpMethod).append(SIGN_SEP)
            .append(contentMd5).append(SIGN_SEP)
            .append(contentType).append(SIGN_SEP)
            .append(date).append(SIGN_SEP);
			
        // 构造StringToSign,拼接CanonicalizedHeaders
        for(Map.Entry<String, String> entry : canonicalizedHeaders.entrySet()) {
            stringToSign.append(entry.getKey()).append(":").append(entry.getValue()).append(SIGN_SEP);
        }
		
        // 构造StringToSign,拼接CanonicalizedResource
        stringToSign.append("/");
        if(this.isValid(bucketName)) {
            stringToSign.append(bucketName).append("/");
            if(this.isValid(objectName)) {
                stringToSign.append(this.urlEncode(objectName));
            }
        }
		
        TreeMap<String, String> canonicalizedResource = new TreeMap<String, String>();
        for(Map.Entry<String, String> entry : queries.entrySet()) {
            key = entry.getKey();
            if(key == null) {
                continue;
            }

            if(SUB_RESOURCES.contains(key)) {
                canonicalizedResource.put(key, entry.getValue());
            }
        }
		
        if(canonicalizedResource.size() > 0) {
            stringToSign.append("?");
            for(Map.Entry<String, String> entry : canonicalizedResource.entrySet()) {
                stringToSign.append(entry.getKey());
                if(this.isValid(entry.getValue())) {
                    stringToSign.append("=").append(entry.getValue());
                }
                stringToSign.append("&");
            }
            stringToSign.deleteCharAt(stringToSign.length()-1);
        }
		
        // System.out.println(String.format("StringToSign:%s%s", SIGN_SEP, stringToSign.toString()));
		
        return stringToSign.toString();
    }
	
    public String headerSignature(String httpMethod, Map<String, String[]> headers, Map<String, String> queries, String bucketName, String objectName) throws Exception {

        // 构造stringToSign
        String stringToSign = this.stringToSign(httpMethod, headers, queries, bucketName, objectName);
        System.out.println("stringToSign: "+stringToSign);
		
        // 计算签名
        return String.format("OBS %s:%s", this.ak, this.hmacSha1(stringToSign));
    }
	
    public static void main(String[] args) throws Exception {
        SignDemo demo = new SignDemo();

        /* 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
        本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量HUAWEICLOUD_SDK_AK和HUAWEICLOUD_SDK_SK。*/
        //demo.ak = System.getenv("HUAWEICLOUD_SDK_AK");
        //demo.sk = System.getenv("HUAWEICLOUD_SDK_SK");

        //最好用上面的方法赋值,这里只是简化操作
        demo.ak = "ZI6KNMNGWZUWMJV5WMKW";
        demo.sk = "hIAb8jwgPEHTYknEMKql6DRqGZMLxRY66cHzd8D2";
		
        String bucketName = "bucket-test";
        String objectName = "hello.jpg";
        Map<String, String[]> headers = new HashMap<String, String[]>();
        headers.put("date", new String[] {"Wed, 14 Aug 2024 07:20:28 GMT"});
        //headers.put("x-obs-acl", new String[] {"public-read"});
        //headers.put("x-obs-meta-key1", new String[] {"value1"});
        //headers.put("x-obs-meta-key2", new String[] {"value2", "value3"});
        Map<String, String> queries = new HashMap<String, String>();
        //queries.put("acl", null);


        //计算Header中携带的签名	
        System.out.println(demo.headerSignature("GET", headers, queries, null, null));
    }
	
}

注意

代码中的 date 是手动填写的时间,必须为RFC 1123格式的GMT时间(和北京时间差了8个小时),与系统当前时间差不能超过15分钟。

 与请求头中的时间也要保持一致,分秒不差!!!

演示成果

好的,以下是一个简单的Java API接口加密鉴权的示例代码: ```java import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class AuthUtils { private static final String SECRET_KEY = "your_secret_key"; public static String generateAuthToken(String apiPath, long timestamp) { String strToHash = apiPath + timestamp + SECRET_KEY; try { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] hash = md.digest(strToHash.getBytes()); StringBuilder sb = new StringBuilder(); for (byte b : hash) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } } public static boolean verifyAuthToken(String authToken, String apiPath, long timestamp) { String expectedAuthToken = generateAuthToken(apiPath, timestamp); return authToken.equals(expectedAuthToken); } } ``` 这个代码定义了一个 `AuthUtils` 类,其中包含了两个方法: - `generateAuthToken(apiPath: String, timestamp: long): String`:根据 API 路径和当前时间戳生成鉴权令牌。这个方法使用了 SHA-256 哈希算法来加密 API 路径、时间戳和你指定的秘钥。 - `verifyAuthToken(authToken: String, apiPath: String, timestamp: long): boolean`:验证传入的鉴权令牌是否正确。这个方法会重新计算一个鉴权令牌,然后将其与传入的鉴权令牌进行比较。 你可以在调用 API 的时候,将生成鉴权令牌添加到 HTTP 请求头中,例如: ``` Authorization: Bearer <your_auth_token> ``` 然后在服务器端,通过调用 `verifyAuthToken` 方法来验证这个鉴权令牌是否正确。如果验证成功,就说明这个请求是合法的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ThatMonth

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值