版权声明:本文为Fighter168原创文章,未经允许不得转载。 https://blog.csdn.net/fighterandknight/article/details/80517646
前言
目前来说,很多公司都使用者阿里云的资源,对象存储(OSS)就是其中一个我们经常使用的一个资源,在这个使用的过程中,如果我们想要做到一些安全管理(Security Token Service,简称 STS),以及资源的访问控制(Resource Access Management,简称 RAM),这里面也就是官方描述的RAM和STS。
如果你们平台对接很多个合作方,每个合作方又不想从阿里云中重新购买一个新的阿里云的oss,又想实现对每个合作方的资源的一个访问控制,以及权限管理,那么通过这种结合RAM和STS的方式,就非常容易解决这个问题。
阿里云的官方文档并未给出根据子账户的信息如何获取sts的token的例子,获取sts的token的流程有涉及到加签以及urlencode的处理,以及HMAC-SHA1算法的处理,所以这里面有一些坑,由于网上也没有找到详细的例子,官方也没给出例子,直是简单说了这个流程,所以自己倒腾了半天,才整理出来,并拿到生产上使用,下面结合代码说一下这个整体的实现流程。
实现逻辑
下面这个是官方给出的实现说明
根据子账户获取STS的TOKEN
生成sts的toke的工具类
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
/**
* @ClassName: GenTempOssUtil
* @Description: 生成临时的oss信息
* @author wenhuixiang
* @date 2018年5月30日 下午4:35:10
*
*/
public class GenTempOssUtil {
private static final String ENCODING = "UTF-8";
private static final String STANDARD_DATE_FORMAT_UTC = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
private final static Logger log = LoggerFactory.getLogger(HttpClientUtil.class);
public static void main(String[] args)throws Exception {
String accessId="你的accessId信息";
String secretKey="你的secretKey信息";
String arn="你的arn信息";
genOssInfo(accessId, secretKey, arn);
}
/**
* @Title: genOssInfo
* @Description: 生产临时的 oss 信息
* @param @param accessId 子账户的accessId
* @param @param secretKey 子账户的secretKey
* @param @param arn 对应子账户的权限信息等
*/
public static String genOssInfo(String accessId,String secretKey,String arn){
StringBuilder stringToSign=new StringBuilder();
StringBuilder canonicalizedQueryString=new StringBuilder();
StringBuilder reqBuilder=new StringBuilder();
String resp=null;
String uuid=UUID.randomUUID().toString();
SimpleDateFormat sdf = new SimpleDateFormat(STANDARD_DATE_FORMAT_UTC);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String timestamp =sdf.format(new Date());
stringToSign.append("GET&%2F&");
try {
canonicalizedQueryString.append("AccessKeyId="+URLEncoder.encode(accessId,ENCODING))
.append("&Action="+URLEncoder.encode("AssumeRole",ENCODING))
.append("&Format="+URLEncoder.encode("JSON",ENCODING))
.append("&GET=")
.append("&RoleArn="+URLEncoder.encode(arn,ENCODING))
.append("&RoleSessionName="+URLEncoder.encode("client",ENCODING))
.append("&SignatureMethod="+URLEncoder.encode("HMAC-SHA1",ENCODING))
.append("&SignatureNonce="+URLEncoder.encode(uuid,ENCODING))
.append("&SignatureVersion="+URLEncoder.encode("1.0",ENCODING))
.append("&Timestamp="+URLEncoder.encode(timestamp,ENCODING))
.append("&Version="+URLEncoder.encode("2015-04-01",ENCODING));
stringToSign.append(URLEncoder.encode(canonicalizedQueryString.toString(),ENCODING));
log.info("加签字符串:"+stringToSign.toString());
String genHMAC = genHMAC(stringToSign.toString(),secretKey+"&");
reqBuilder.append("Format="+URLEncoder.encode("JSON",ENCODING))
.append("&Version="+URLEncoder.encode("2015-04-01",ENCODING))
.append("&AccessKeyId="+URLEncoder.encode(accessId,ENCODING))
.append("&Signature="+URLEncoder.encode(genHMAC,ENCODING))
.append("&SignatureMethod="+URLEncoder.encode("HMAC-SHA1",ENCODING))
.append("&SignatureVersion="+URLEncoder.encode("1.0",ENCODING))
.append("&SignatureNonce="+URLEncoder.encode(uuid,ENCODING))
.append("&Timestamp="+URLEncoder.encode(timestamp,ENCODING))
.append("&Action="+URLEncoder.encode("AssumeRole",ENCODING))
.append("&GET=")
.append("&RoleArn="+URLEncoder.encode(arn,ENCODING))
.append("&RoleSessionName="+URLEncoder.encode("client",ENCODING));
} catch (UnsupportedEncodingException e1) {
resp="{\"msg\":\"获取oss信息参数编码转换异常\"}";
log.error("获取oss信息参数编码转换异常",e1);
}
try {
String reqUrl="https://sts.aliyuncs.com/?"+reqBuilder.toString();
log.info("请求 获取临时的 oss 信息 地址:"+reqUrl);
resp=HttpClientUtil.getRequest(reqUrl);
JSONObject obj=JSON.parseObject(resp);
obj.put("msg", "success");
obj.put("desc", "success");
resp=JSON.toJSONString(obj);
log.info("阿里云返回的 oss 结果信息 :"+resp);
} catch (Exception e) {
JSONObject obj=new JSONObject();
obj.put("msg", "failed");
obj.put("desc", "获取临时的oss信息异常");
resp=JSON.toJSONString(obj);
log.error("获取临时的oss信息异常",e);
}
return resp;
}
/**
* 使用 HMAC-SHA1 签名方法对data进行签名
*
* @param data 被签名的字符串
* @param key 密钥
* @return 加密后的字符串
*/
public static String genHMAC(String data, String key) {
byte[] result = null;
try {
//根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
SecretKeySpec signinKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
//生成一个指定 Mac 算法 的 Mac 对象
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
//用给定密钥初始化 Mac 对象
mac.init(signinKey);
//完成 Mac 操作
byte[] rawHmac = mac.doFinal(data.getBytes());
result = Base64.encodeBase64(rawHmac);
} catch (Exception e) {
log.error("HMAC-SHA1 加密失败",e);
}
if (null != result) {
return new String(result);
} else {
return null;
}
}
}
https请求的工具类
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @ClassName: GenTempOssUtil
* @Description: 类HttpClientUtil.java的实现描述:httpclient工具类
* @author wenhuixiang
* @date 2018年5月30日 下午4:35:10
*
*/
public class HttpClientUtil {
public static final String RETURN_CODE_FAIL = "request_fail";
private final static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
private static final int timeout = 30000;
/**
* 每个主机最大连接数
*/
private static final int maxConnectionsPerHost = 20;
/**
* 最大总共连接数
*/
private static final int maxTotalConnection = 100;
private static final String encoding = "UTF-8";
private HttpClientUtil() {
}
/**
* post请求
*
* @param url 请求地址
* @param params 请求参数
* @return 返回结果
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public static String postRequest(String url, Map<String, String> params) {
return postRequest(url, null, params);
}
/**
* post请求
*
* @param url 请求地址
* @param header 请求头
* @param params 请求参数
* @return 返回结果
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public static String postRequest(String url, Map<String, String> header, Map<String, String> params) {
String result = "";
PostMethod postMethod = null;
try {
//创建连接
HttpClient httpClient = new HttpClient(HttpClientUtilHolder.httpConnectionManager);
// 设置编码
httpClient.getParams().setContentCharset(encoding);
httpClient.getParams().setHttpElementCharset(encoding);
httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, encoding);
postMethod = new PostMethod(url);
//设置头信息
if (MapUtils.isNotEmpty(header)) {
for (Map.Entry<String, String> entry : header.entrySet()) {
postMethod.setRequestHeader(entry.getKey(), entry.getValue());
}
}
//设置参数
List<NameValuePair> list = new ArrayList<NameValuePair>();
NameValuePair nameValuePair = null;
Iterator iter = params.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String key = (String) entry.getKey();
String val = (String) entry.getValue();
nameValuePair = new NameValuePair(key, val);
list.add(nameValuePair);
}
NameValuePair[] data = list.toArray(new NameValuePair[list.size()]);
postMethod.setRequestBody(data);
//请求
logger.info("=================httpclient发起请求");
httpClient.executeMethod(postMethod);
logger.info("=================httpclient请求结束");
result = postMethod.getResponseBodyAsString();
logger.info("httpclient请求结果:{}", result);
} catch (Exception e) {
logger.error("=================httpclient请求异常:", e);
result = RETURN_CODE_FAIL;
} finally {
if (postMethod != null) {
//释放连接
postMethod.releaseConnection();
}
}
return result;
}
/**
* post请求-没有参数名,只有参数值
*
* @param url 请求地址
* @param body 请求参数
* @return 返回结果
* @throws Exception
*/
public static String postRequestOfNoParamName(String url, String body) {
String result = "";
PostMethod postMethod = null;
try {
//创建连接
HttpClient httpClient = new HttpClient(HttpClientUtilHolder.httpConnectionManager);
// 设置编码
httpClient.getParams().setContentCharset(encoding);
httpClient.getParams().setHttpElementCharset(encoding);
httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, encoding);
postMethod = new PostMethod(url);
postMethod.setRequestEntity(new StringRequestEntity(body, "", encoding));
//请求
httpClient.executeMethod(postMethod);
result = postMethod.getResponseBodyAsString();
} catch (Exception e) {
logger.error("=================httpclient请求异常异常:", e);
result = RETURN_CODE_FAIL;
} finally {
if (postMethod != null) {
//释放连接
postMethod.releaseConnection();
}
}
return result;
}
public static String getRequest(String url) throws Exception {
String result = "";
GetMethod getMethod = null;
try {
//创建连接
HttpClient httpClient = new HttpClient(HttpClientUtilHolder.httpConnectionManager);
// 设置编码
httpClient.getParams().setContentCharset(encoding);
httpClient.getParams().setHttpElementCharset(encoding);
httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, encoding);
getMethod = new GetMethod(url);
//请求
int statusCode = httpClient.executeMethod(getMethod);
if (statusCode == HttpStatus.SC_OK) {
result = getMethod.getResponseBodyAsString();
} else {
result = RETURN_CODE_FAIL;
}
} catch (Exception e) {
logger.error("=================httpclient请求异常异常:", e);
// result = RETURN_CODE_FAIL;
result = null;
throw new Exception(e.getMessage(), e);
} finally {
if (getMethod != null) {
//释放连接
getMethod.releaseConnection();
}
}
return result;
}
/**
* 单例创建httpConnectionManager.
*/
private static class HttpClientUtilHolder {
private static HttpConnectionManager httpConnectionManager = initConnectionManager();
private static HttpConnectionManager initConnectionManager() {
logger.info("initConnectionManager");
httpConnectionManager = new MultiThreadedHttpConnectionManager();
HttpConnectionManagerParams params = httpConnectionManager.getParams();
params.setConnectionTimeout(timeout);
params.setSoTimeout(timeout);
params.setDefaultMaxConnectionsPerHost(maxConnectionsPerHost);
params.setMaxTotalConnections(maxTotalConnection);
return httpConnectionManager;
}
}
}
结果示例
使用获取到的token信息上传文件
public static void main(String[] args) {
String accessKeyId = "你获取到的accessKeyId";
String accessKeySecret = "你获取到的secretKey";
String securityToken = "你获取到的token";
// 以杭州为例
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
OSSClient client = new OSSClient(endpoint, accessKeyId, accessKeySecret, securityToken);
ObjectMetadata meta = new ObjectMetadata();
// 指定上传的内容类型。
meta.setContentType("text/plain");
UploadFileRequest fileReq=new UploadFileRequest("<yourBucketName>","<yourObjectName>");
// 指定上传并发线程数,默认为1。
fileReq.setTaskNum(5);
// 指定上传的分片大小,从100KB到5GB,单位是Byte,默认为100K。
fileReq.setPartSize(1 * 1024 * 1024);
// 指定上传的本地文件,必选参数。
fileReq.setUploadFile("C:\\Users\\Administrator\\Desktop\\123.png");
//Object的元数据。
fileReq.setObjectMetadata(meta);
try {
client.uploadFile(fileReq);
} catch (Throwable e) {
e.printStackTrace();
}finally {
if(client!=null) client.shutdown();
}
}