开放Api认证
使用说明
所有访问哈哈云开放接口的请求,都要携带认证Header
Wsse认证Header
参数 | 类型 | 必需 | 描述 |
---|---|---|---|
Authorization | string | 是 | 取值为 WSSE realm="SDP",profile="UsernameToken",type="Appkey" |
X-WSSE | string | 是 | 取值为UsernameToken Username="app_key的取值",PasswordDigest="passwordDigest的取值",Nonce="Nonce的取值",Created="Created取值" |
取值说明
参数 | 类型 | 必需 | 描述 |
---|---|---|---|
Username | string | 是 | AppKey,租户唯一id |
PasswordDigest | string | 是 | 取值为Base64(Sha256(Nonce+Created+Appsecret)生成),使用Nonce,Created,AppSecret拼接后的字符串进行SHA256加密即可,拼接时不要使用任何字符 |
Nonce | string | 是 | 客户发送请求时生成的随机数,长度为1~128位,可包含数字和大小写字母 |
Created | string | 是 | 随机数生成时间,采用标准UTC格式 |
注
: AppKey&AppSecret,请联系运营人员获取
示例
Authorization = WSSE realm="SDP",profile="UsernameToken",type="Appkey"
X-WSSE = UsernameToken Username="3736309225585818",PasswordDigest="MmI4MDM2OWRjMTdhMTA1MTFmYWU3MGFmMmM0YTRjYjdjNjNlYWNmMWQ2ZGQ1ZTFiYjljODVjNTYwMWFmZTZkMg==",Nonce="6b35e09847ba4a15963ac85e63baec76",Created="2021-11-05T04:18:11Z"
代码参考
- JAVA代码示例:
/*
* Copyright © 2021 HaHa Cloud Information Technology Co.,Ltd. All rights reserved.
*/
package com.hahacloud.openapi;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 李丰
* @version [版本号, 2021/11/4]
*/
public class WsseHttpHeader {
/**
* WSSE头部
*/
public static final String WSSE_HEAD = "WSSE ";
public static final String WSSE_HEADER = "x-wsse";
public static final String AUTH_HEADER = "Authorization";
private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private static final String DEFAULT_REALM = "SDP";
private static final String DEFAULT_PROFILE = "UsernameToken";
private static final String DEFAULT_TYPE = "Appkey";
private static String userNameExpression = "Username=\"([A-Za-z0-9+/=]+)\"";
private static String nonceExpression = "Nonce=\"([A-Za-z0-9+/=]+)\"";
private static String passwordDigestExpression = "PasswordDigest=\"([A-Za-z0-9+/=]+)\"";
private static String parseCreatedExpression = "Created=\"(\\d{4}-\\d{1,2}-\\d{1,2}T\\d{2}:\\d{2}:\\d{2}Z)\"";
private static String reamExpression = "realm=\"([A-Za-z0-9+/=]+)\"";
private static String profileExpression = "profile=\"([A-Za-z0-9+/=]+)\"";
private static String typeExpression = "type=\"([A-Za-z0-9+/=]+)\"";
private static Pattern headerWssePattern;
private static Pattern headerTokenPattern;
/**
* realm="SDP"
*/
private String realm;
/**
* profile头部值="UsernameToken"
*/
private String profile;
/**
* type=Appkey
*/
private String type;
/**
* Username
*/
private String userName;
/**
* PasswordDigest
*/
private String passwordDigest;
/**
* nonce随机数
*/
private String nonce;
/**
* 时间戳=yyyy-MM-dd'T'HH:mm:ss'Z'
*/
private String created;
/**
* Getters & Setters.
*/
public String getUserName() {
return userName;
}
public String getPasswordDigest() {
return passwordDigest;
}
public String getNonce() {
return nonce;
}
public String getCreated() {
return created;
}
public String getProfile() {
return profile;
}
public String getRealm() {
return realm;
}
public String getType() {
return type;
}
public void setRealm(String realm) {
this.realm = realm;
}
public void setProfile(String profile) {
this.profile = profile;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setNonce(String nonce) {
this.nonce = nonce;
}
public void setCreated(String created) {
this.created = created;
}
public static void setUserNameExpression(String userNameExpression) {
WsseHttpHeader.userNameExpression = userNameExpression;
}
public void setType(String type) {
this.type = type;
}
public void setPasswordDigest(String passwordDigest) {
this.passwordDigest = passwordDigest;
}
public static WsseHttpHeader parseHttpHeader(String httpTokenHeader, String httpWsseHeader) throws Exception {
return parseHttpHeader(httpTokenHeader, httpWsseHeader, null, null, null);
}
public static WsseHttpHeader parseHttpHeader(String httpTokenHeader, String httpWsseHeader, String defaultRealm,
String defaultProfile, String defaultType) throws Exception {
WsseHttpHeader wsseHeader = new WsseHttpHeader();
if (StringUtils.isNotEmpty(httpTokenHeader)) {
httpTokenHeader = httpTokenHeader.replaceAll(", ", ",");
Matcher matcher = getHeaderTokenPattern().matcher(httpTokenHeader);
if (matcher.matches()) {
wsseHeader.setRealm(matcher.group(1));
wsseHeader.setProfile(matcher.group(2));
wsseHeader.setType(matcher.group(3));
}
}
if (StringUtils.isEmpty(defaultRealm)) {
defaultRealm = DEFAULT_REALM;
}
if (StringUtils.isEmpty(defaultProfile)) {
defaultProfile = DEFAULT_PROFILE;
}
if (StringUtils.isEmpty(defaultType)) {
defaultType = DEFAULT_TYPE;
}
if (!defaultRealm.equals(wsseHeader.getRealm()) || !defaultProfile.equals(wsseHeader.getProfile())
|| !defaultType.equals(wsseHeader.getType())) {
throw new Exception("Invalid Autorization : " + httpTokenHeader);
}
if (!StringUtils.isEmpty(httpWsseHeader)) {
if (!httpWsseHeader.startsWith(wsseHeader.getProfile())) {
throw new Exception("Invalid WSSE : " + httpWsseHeader);
}
httpWsseHeader = httpWsseHeader.replaceAll(", ", ",");
Matcher matcher = getHeaderPattern(wsseHeader.getProfile()).matcher(httpWsseHeader);
if (matcher.matches()) {
wsseHeader.setUserName(matcher.group(1));
wsseHeader.setPasswordDigest(matcher.group(2));
wsseHeader.setNonce(matcher.group(3));
wsseHeader.setCreated(matcher.group(4));
if (Base64.getDecoder().decode(wsseHeader.getPasswordDigest()) == null) {
throw new Exception("Invalid Password Digest : " + wsseHeader.getPasswordDigest());
}
}
}
return wsseHeader;
}
public boolean isAuthenticated(String password) {
return createPasswordDigest(password).equals(getPasswordDigest());
}
public String createPasswordDigest(String password) {
String sha256hex = DigestUtils.sha256Hex(getNonce() + getCreated() + password);
return Base64.getEncoder().encodeToString(sha256hex.getBytes(StandardCharsets.UTF_8));
}
public static WsseHttpHeader createDefaultWsse(String appKey, String secret) {
WsseHttpHeader wsseHttpHeader = new WsseHttpHeader();
wsseHttpHeader.setRealm(DEFAULT_REALM);
wsseHttpHeader.setProfile(DEFAULT_PROFILE);
wsseHttpHeader.setType(DEFAULT_TYPE);
wsseHttpHeader.setUserName(appKey);
wsseHttpHeader.setNonce(UUID.randomUUID().toString().replaceAll("-", ""));
wsseHttpHeader.setCreated(LocalDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern(DATE_FORMAT)));
wsseHttpHeader.setPasswordDigest(wsseHttpHeader.createPasswordDigest(secret));
return wsseHttpHeader;
}
public String getWsseHeader() {
return String.format(getProfile() + " Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"",
getUserName(), getPasswordDigest(), getNonce(), getCreated());
}
public String getTokenHeader() {
return WSSE_HEAD + "realm=\"" + realm + "\",profile=\"" + profile + "\",type=\"" + type + "\"";
}
/**
*
*/
WsseHttpHeader() {
super();
}
/**
* @return Returns the passwordDigestExpression.
*/
public static String getPasswordDigestExpression() {
return passwordDigestExpression;
}
/**
* @return Returns the userNameExpression.
*/
public static String getUserNameExpression() {
return userNameExpression;
}
/**
* @return Returns the createdExpression.
*/
public static String getCreatedExpression() {
return parseCreatedExpression;
}
/**
* @return Returns the nonceExpression.
*/
public static String getNonceExpression() {
return nonceExpression;
}
/**
* @return Returns the headerPattern.
*/
public static Pattern getHeaderPattern(String profile) {
if (headerWssePattern == null) {
StringBuffer sb = new StringBuffer(profile + " ");
sb.append(userNameExpression);
sb.append(",");
sb.append(passwordDigestExpression);
sb.append(",");
sb.append(nonceExpression);
sb.append(",");
sb.append(parseCreatedExpression);
headerWssePattern = Pattern.compile(sb.toString());
}
return headerWssePattern;
}
public static Pattern getHeaderTokenPattern() {
if (headerTokenPattern == null) {
StringBuffer sb = new StringBuffer(WSSE_HEAD);
sb.append(reamExpression);
sb.append(",");
sb.append(profileExpression);
sb.append(",");
sb.append(typeExpression);
headerTokenPattern = Pattern.compile(sb.toString());
}
return headerTokenPattern;
}
}
完整示例可参考: 链接
- GO代码示例:
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"time"
)
const (
BASE_AUTH = `WSSE realm="SDP",profile="UsernameToken",type="Appkey"`
BASE_WSSE_FMT = `UsernameToken Username="%s",PasswordDigest="%s",Nonce="%s",Created="%s"`
APP_KEY = `!!!请填写你的appkey,若没有请与哈哈云平台联系!!!`
APP_SECRET = `!!!请填写你的appsecret,若没有请与哈哈云平台联系!!!`
)
func wsseHeader(key, secret string) string {
hasher := sha256.New()
nonce, _ := randomHex(32)
ts := time.Now().UTC().Format("2006-01-02T15:04:05Z")
hasher.Write([]byte(nonce + ts + secret))
digest := base64.StdEncoding.EncodeToString(hasher.Sum(nil))
return fmt.Sprintf(BASE_WSSE_FMT, key, digest, nonce, ts)
}
func randomHex(n int) (string, error) {
bytes := make([]byte, n)
if _, err := rand.Read(bytes); err != nil {
panic(err)
}
return hex.EncodeToString(bytes), nil
}
func main() {
fmt.Println("key:", APP_KEY, " secret:", APP_SECRET)
fmt.Printf("x-wsse: %s\n", wsseHeader(APP_KEY, APP_SECRET))
fmt.Printf("Authorization: %s\n", BASE_AUTH)
}