本文主要介绍如何配置阿里云的OSS、RAM访问控制,然后使用子账户直接颁发临时凭证,供前端javascript或者其他后端来操作我们的阿里云资源。
首先说下目前应用场景:我需要将一个网站的所有静态资源(图片、视频等)上传的阿里云存储,访问的时候减少服务器带宽压力,希望上传过程直接通过前端js来操作,不再通过后端服务器,那么我们需要为前端提供上传凭证,就需要后端程序提供一个api来供其他服务或者前端获取凭证,阿里云对权限这一块,做的很完善,主要是给子账户配置权限,在哪配,怎么配置,做的越完善肯定越复杂。
当然你也可以直接使用七牛云,更加简单 七牛云对象存储JS SDK使用录
一、首先我们要明白阿里云的RAM访问控制是如何运作的。
有几个概念需要明白:
子账户(用户):你的阿里云账号登录控制台,是最大最高权限的账号,那么里面有很多数据子系统,你可以创建其他子账户来给你的各种应用使用,总不能直接给总账户吧,和OA权限一样的,各司其职。
用户组:你可以理解成企业的部门,分配权限的时候,直接给部门分权限,那么这里面的所有人都有部门权限了。
RAM角色:这个就是权限的具体体现,权限就是一个功能一个功能的操作控制,代表了能不能操作,那么RAM角色就是把权限进行打包,然后关联到人或者用户组;比如你的OSS操作和ECS操作是分别给两个APP使用的权限,那么就可以创建两个RAM角色,将他们分别分给各司其职的子账户即可。
STS:这是阿里云用来给子账户颁发临时密钥的系统,有个AssumeRole接口,我们后端API给前端提供的信息就是通过这个接口来的。
二、如何配置RAM?
1、登录阿里云【控制台】,鼠标移动到头像上,就会看到【访问控制】,进入到控制访问主界面
2、点开【用户】,点击【创建用户】,创建一个OssAccount的子账户,然后访问我需要的接口访问,不需要登录控制台,所以选择【编程访问】,点击确定
看到用户信息的界面已经展示出来了,如下图。把这两个AccessKey记录一下,等哈用
3、我们来直接给这个账户添加权限,添加【AliyunOSSFullAccess】、【AliyunSTSAssumeRoleAccess】这两个权限,保存即可,子账户就建立好了。
4、点击【RAM角色管理】打开界面,准备创建角色信息,点击【创建RAM】角色,名为Ram
5、还是一样的,授权【AliyunOSSFullAccess】、【AliyunSTSAssumeRoleAccess】,这里点击角色ram名称,进入详情,看到ARN信息,记录下等哈用。
三、怎么配置OSS?
1、创建OSS,进入主控制台,点击【Bucket列表】,再点击【创建Bucket】
2、我取名为carsonyangblog,选择【华南1(深圳)】,读写权限选择【公共读取】,确定即可。
公共读是因为我们要对外访问,可能我的图片需要给别的域名下使用。
3、还需要配置授权策略和跨域,因为我们要让前端直接上传到我们的bucket中。
4、点击【Bucket授权策略】,再点击【新增授权】,选择【子账户】,授权选择【完全控制】,即可。
5、点击【跨域设置】,再点击【创建规则】,按下图设置。
到这里我们Oss配置完了。
四、开始写代码
我的前端界面是:http://127.0.0.1:5500/ossupload.html
我的后端API地址是:http://localhost:5000/weatherforecast
1、后端代码开始:.NET Core API配置如下:
添加引用:Aliyun.Acs.Core、Aliyun.Acs.Sts
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", /* * STSRegionId: https://help.aliyun.com/document_detail/66053.html?spm=a2c4g.11186623.6.793.46f17069mFpq2G * OSSRegion:https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.14.19d0433bBFviGo#concept-zt4-cvy-5db */ "OSS": { /*获取临时凭证的接口地址 STSRegionId: https://help.aliyun.com/document_detail/66053.html?spm=a2c4g.11186623.6.793.46f17069mFpq2G */ "STSRegionId": "cn-shenzhen", "STSEndpoint": "sts.cn-shenzhen.aliyuncs.com", /* 刚刚创建的子账户OssAccount和两个accesskey */ "RoleSessionName": "OssAccount@1287***********31.onaliyun.com", "AccessKeyId": "LTAI4**********rwXmiywo", "AccessKeySecret": "wmhdQ******************MGBpgQ", /* 刚刚创建的ARM角色的ARN信息 */ "RoleArn": "acs:ram::1***********1:role/ram", /* 刚刚创建的Oss名称 */ "Bucket": "carsonyangblog", /* Oss访问地址,深圳的Bucket,就选择深圳的地址 OSSRegion:https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.14.19d0433bBFviGo#concept-zt4-cvy-5db */ "OSSRegion": "oss-cn-shenzhen", /* 公共访问的地址,结合前端的文件名,就可以直接访问上传的文件了。Bucket名+OssRegin地址+aliyuncs.com+文件名 */ "DoMain": "http://carsonyangblog.oss-cn-shenzhen.aliyuncs.com/" } }
API代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AliOssDemoApi.Model; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Aliyun.Acs.Core; using Aliyun.Acs.Core.Profile; using Aliyun.Acs.Core.Http; using Aliyun.Acs.Sts.Model.V20150401; using Microsoft.AspNetCore.Cors; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; namespace AliOssDemoApi.Controllers { [EnableCors("CorsPolicy")] [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly IOptions<OssConfig> _options; private readonly ILogger<WeatherForecastController> _logger; public static IMemoryCache _memoryCache = new MemoryCache(new MemoryCacheOptions()); public WeatherForecastController(IOptions<OssConfig> options, ILogger<WeatherForecastController> logger) { _options = options; _logger = logger; } [HttpGet] public OssTokenResult Get() { string cachekey = "OssToken"; OssTokenResult data = _memoryCache.Get<OssTokenResult>(cachekey); if (data != null) { return data; } string REGIONID = _options.Value.STSRegionId; string ENDPOINT = _options.Value.STSEndpoint; //构建一个阿里云client,用于发起请求 //构建阿里云client时需要设置AccessKey ID和AccessKey Secret DefaultProfile.AddEndpoint(REGIONID, REGIONID, "Sts", ENDPOINT); IClientProfile profile = DefaultProfile.GetProfile(REGIONID, _options.Value.AccessKeyId, _options.Value.AccessKeySecret); DefaultAcsClient client = new DefaultAcsClient(profile); //构建AssumeRole请求 AssumeRoleRequest request = new AssumeRoleRequest(); request.AcceptFormat = FormatType.JSON; //指定角色ARN request.RoleArn = _options.Value.RoleArn; request.RoleSessionName = _options.Value.RoleSessionName; //设置Token有效期,可选参数,默认3600秒 request.DurationSeconds = 3600; //设置Token的附加权限策略;在获取Token时,通过额外设置一个权限策略进一步减小Token的权限 //request.Policy="<policy-content>" try { AssumeRoleResponse response = client.GetAcsResponse(request); Console.WriteLine("AccessKeyId: " + response.Credentials.AccessKeyId); Console.WriteLine("AccessKeySecret: " + response.Credentials.AccessKeySecret); Console.WriteLine("SecurityToken: " + response.Credentials.SecurityToken); //Token过期时间;服务器返回UTC时间,这里转换成北京时间显示 Console.WriteLine("Expiration: " + DateTime.Parse(response.Credentials.Expiration).ToLocalTime()); data = new OssTokenResult(); data.AccessKeyId = response.Credentials.AccessKeyId; data.AccessKeySecret = response.Credentials.AccessKeySecret; data.StsToken = response.Credentials.SecurityToken; data.Region = _options.Value.OSSRegion; data.Bucket = _options.Value.Bucket; data.DoMain = _options.Value.DoMain; _memoryCache.Set(cachekey, data, new MemoryCacheEntryOptions().SetAbsoluteExpiration(DateTime.Parse(response.Credentials.Expiration).ToLocalTime())); return data; } catch (Exception ex) { _logger.LogError(ex, "获取凭证出错"); _memoryCache.Remove(cachekey); return new OssTokenResult(); } } } }
OSsConfig.cs结构
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace AliOssDemoApi.Model { public class OssConfig { public string STSRegionId { get; set; } public string STSEndpoint { get; set; } public string AccessKeyId { get; set; } public string AccessKeySecret { get; set; } public string RoleArn { get; set; } public string RoleSessionName { get; set; } public string Bucket { get; set; } public string OSSRegion { get; set; } public string DoMain { get; set; } } }
OssTokenResult.cs结构
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace AliOssDemoApi.Model { public class OssTokenResult { public string AccessKeyId { get; set; } public string AccessKeySecret { get; set; } public string StsToken { get; set; } public string Region { get; set; } public string Bucket { get; set; } public string DoMain { get; set; } } }
API跨域设置
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure<OssConfig>(Configuration.GetSection("OSS")); services.AddCors(options => options.AddPolicy("CorsPolicy", builder => { //不限制 //builder.AllowAnyMethod() // .SetIsOriginAllowed(_ => true) // .AllowAnyHeader() // .AllowCredentials(); //限制 builder.AllowAnyMethod() .WithOrigins(new[] { "http://127.0.0.1:5500" }) .AllowAnyHeader() .AllowCredentials(); })); services.AddControllers(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseCors("CorsPolicy"); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } }
2、前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="jquery.js"></script> <script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-6.8.0.min.js"></script> </head> <body> <body> <input type="file" id="file" /> <img src="" id="imgsrc"> <script type="text/javascript"> document.getElementById('file').addEventListener('change', function (e) { let file = e.target.files[0]; let storeAs = 'upload-file123.png';//自定义文件名 let domain = '';//后台返回的文件访问域名 两个合起来就是文件访问全路径 console.log(file.name + ' => ' + storeAs); // OSS.urlib是SDK内部封装的发送请求的逻辑,开发者可以使用任何发送请求的库向sts-server发送请求。 OSS.urllib.request("http://localhost:5000/weatherforecast", { method: 'GET' }, (err, response) => { if (err) { return alert(err); } try { result = JSON.parse(response); } catch (e) { return alert('parse sts response info error: ' + e.message); } domain = result.doMain; let client = new OSS({ accessKeyId: result.accessKeyId, accessKeySecret: result.accessKeySecret, stsToken: result.stsToken, region: result.region, bucket: result.bucket }); // storeAs可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。 // file可以自定义为File对象、Blob数据以及OSS Buffer。 client.multipartUpload(storeAs, file).then(function (result) { console.log(result); $("#imgsrc").attr("src", domain + storeAs) }).catch(function (err) { console.log(err); }); }); }); </script> </body> </body> </html>
留下您的脚步
最近评论