参考官方文章地址:如何使用免登访问流程_阿里云集成转售解决方案-阿里云帮助中心
参考代码地址:如何使用安全访问服务Python及Java示例代码_阿里云集成转售解决方案-阿里云帮助中心
1. RAM的含义
阿里云的访问控制RAM(Resource Access Management)提供了强大的细粒度权限管理功能,适用于企业中多个部门或角色需要访问ECS资源的情况。为了保障敏感信息和关键业务流程的安全,您可以根据各部门或角色的具体职责分配不同的访问权限。通过实施权限分离策略,不仅能显著提升管理效率,还能有效降低信息泄露的风险。本文介绍如何通过控制RAM用户的权限,以实现对云服务器ECS资源的访问控制。
2. 场景示例
假设您公司是使用ECS来托管应用程序和服务。其中,IT架构规划由管理人员主导,他们对所有ECS资源拥有控制权,包括但不限于创建资源、调整资源分配及安全策略配置等关键职责。开发人员负责项目的持续迭代和功能创新,并承担将项目部署到ECS上的任务。运维人员则承担起保障系统正常运转的责任,通过创建快照、创建镜像、执行相关脚本等方式维护现有服务。
针对这三类人员的需求,我们将设计如下权限方案:
管理人员:可以拥有创建、删除ECS实例及修改安全组规则等所有ECS操作权限。
开发人员:能够查看所有ECS实例的信息,但不能修改任何设置,同时可以登录ECS实例进行操作。
运维人员:具备创建部分资源的权限,但不具备删除资源的权限,如创建快照和镜像、执行脚本等任务。
针对以上三类不同的人群便可设置不同的RAM账号,对应设置不同的权限,以实现资源及操作权限的隔离。
3. 为什么要RAM免密登录?
RAM免密登录主要有以下三点好处:安全性,避免长期保存密码;自动化,适合脚本或应用自动执行任务;权限控制,可以精细化管理权限;减少人为错误,比如密码泄露或输错。
4. 免密登录的方式
RAM免密登录的原理
其核心原理是通过非交互式身份验证替代传统密码,常用技术包括:
AccessKey(长期凭证)
用户生成一对
AccessKey ID
和Secret Access Key
,直接嵌入代码或配置文件中,用于API请求签名验证。风险:长期有效,一旦泄露可能被滥用。
STS(临时安全令牌)
通过STS服务申请临时凭证(有效期几分钟至几小时),动态生成Token,到期自动失效。
适用场景:移动端或第三方临时访问。
角色扮演(Role Assume)
将权限赋予某个角色(如ECS实例角色),实例启动时自动获取临时凭证,无需硬编码密钥。
原理:通过云服务的元数据接口(如阿里云的
100.100.100.200
)动态获取临时Token。
联合身份(Federation)
集成企业AD或SSO服务,用户通过现有身份系统登录后,映射为云平台的临时角色。
本文主要介绍角色扮演获取STS(临时安全令牌)实现免密登录。
5. 免密登录流程
6. 示例代码
package com.hundsun.openplat.api.alivno.aliSDK;
import java.io.IOException;
import java.net.URISyntaxException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
/**
*
* 免密登陆STS
*
* 获取安全令牌
*/
public class StsService {
private static String getRoleArn(String accountId, String roleName) {
return String.format("acs:ram::%s:role/%s", accountId, roleName);
}
private static final String SIGN_IN_DOMAIN = "https://signin.aliyun.com/federation";
/**
* 使用安全令牌获取登录令牌
* https://help.aliyun.com/document_detail/91913.html
*
* @param accesskeyId
* @param accessKeySecret
* @param securityToken
* @return
* @throws IOException
* @throws URISyntaxException
*/
private static String getSignInToken(String accesskeyId, String accessKeySecret, String securityToken)
throws IOException, URISyntaxException {
URIBuilder builder = new URIBuilder(SIGN_IN_DOMAIN);
builder.setParameter("Action", "GetSigninToken")
.setParameter("AccessKeyId", accesskeyId)
.setParameter("AccessKeySecret", accessKeySecret)
.setParameter("SecurityToken", securityToken)
.setParameter("TicketType", "mini");
HttpGet request = new HttpGet(builder.build());
CloseableHttpClient httpclient = HttpClients.createDefault();
try (CloseableHttpResponse response = httpclient.execute(request)) {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String context = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSON.parseObject(context);
return jsonObject.getString("SigninToken");
} else {
System.out.println(response.getStatusLine());
}
}
return null;
}
private static String getHdmLoginUrl(String pageUrl, String signInToken) throws URISyntaxException {
URIBuilder builder = new URIBuilder(SIGN_IN_DOMAIN);
builder.setParameter("Action", "Login");
// 登录失效跳转的地址,一般配置为自建WEB配置302跳转的URL
builder.setParameter("LoginUrl", "https://uicuat.hundsun.cn/iuccasserver/login?service=https%3A%2F%2Fwww.sit.hs.net%3A443%2Fcom.hundsun.openplat.front%2Fcloud%2Fopen%2Fuser%2FtoLogin.html");
// 实际访问 DAS 的页面,比如全局大盘,实时大盘,某个实例详情等
builder.setParameter("Destination", pageUrl);
builder.setParameter("SigninToken", signInToken);
HttpGet request = new HttpGet(builder.build());
return request.getURI().toString();
}
/**
* 通过AssumeRole接口获取用户临时身份
* 参考 https://help.aliyun.com/document_detail/28763.html
*
* @param accountId
* @param accessKeyId
* @param accessKeySecret
* @param ramRole
* @return
* @throws ClientException
*/
private static AssumeRoleResponse.Credentials assumeRole(String accountId, String accessKeyId,
String accessKeySecret, String ramRole)
throws ClientException {
String defaultRegion = "cn-hangzhou";
IClientProfile profile = DefaultProfile.getProfile(defaultRegion, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
AssumeRoleRequest request = new AssumeRoleRequest();
// 设置RAMArn, accountId为资源Owner的UID,即主账号
request.setRegionId(defaultRegion);
request.setRoleArn(getRoleArn(accountId, ramRole));
// 用户自定义参数。此参数用来区分不同的令牌,可用于用户级别的访问审计。格式:^[a-zA-Z0-9\.@\-_]+$
request.setRoleSessionName("session-name");
// 指定的过期时间,单位为秒。过期时间范围:900 ~ 3600,默认值为 3600
request.setDurationSeconds(3600L);
AssumeRoleResponse response = client.getAcsResponse(request);
return response.getCredentials();
}
public static void main(String[] args) throws IOException, URISyntaxException {
try {
/*
Step 0 准备子账号和权限授权
*/
String accountId = "";
// 用来访问 DAS 产品的Role,可以按照需要添加AliyunHDMReadOnlyAccess(只读),AliyunHDMFullAccess 权限
// 默认使用 “aliyunid-ag-ram-role-admin”
String ramRole = "";
// 某个子账号AK,SK, 要求需要有 AliyunSTSAssumeRoleAccess 权限
String accessKeyId = "";
String accessKeySecret = "";
/*
Step 1 通过AssumeRole接口获取临时AK, SK, SecurityToken
*/
AssumeRoleResponse.Credentials credentials = assumeRole(accountId, accessKeyId, accessKeySecret, ramRole);
System.out.println("Expiration: " + credentials.getExpiration());
System.out.println("Access Key Id: " + credentials.getAccessKeyId());
System.out.println("Access Key Secret: " + credentials.getAccessKeySecret());
System.out.println("Security Token: " + credentials.getSecurityToken());
/*
Step 2 获取SigninToken
*/
String signInToken = getSignInToken(credentials.getAccessKeyId(),
credentials.getAccessKeySecret(),
credentials.getSecurityToken());
System.out.println("Your SigninToken is: " + signInToken);
/*
Step 3 构造免登录链接,比如 DAS 的监控大盘
*/
//String pageUrl = getHdmLoginUrl("https://hdm.console.aliyun.com/?hideTopbar=true#/customDashboard?", signInToken);
String pageUrl = getHdmLoginUrl("https://ecs-buy4service.aliyun.com/wizard/#/prepay/", signInToken);
System.out.println("Your PageUrl is : " + pageUrl);
} catch (ClientException e) {
System.out.println("Failed:");
System.out.println("Error code: " + e.getErrCode());
System.out.println("Error message: " + e.getErrMsg());
System.out.println("RequestId: " + e.getRequestId());
}
}
}