技术 思绪 摘录 旅行
阿里云对象存储OSS(Object Storage Service)是阿里云提供的海量、安全、低成本、高可靠的云存储服务。其数据设计持久性不低于99.9999999999%(12个9),服务可用性(或业务连续性)不低于99.995%。OSS具有与平台无关的RESTful API接口,您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。如果你的应用场景仅仅是网站上传个图片、视频这些,那么阿里云OSS和七牛云的作用是一样的。

    本文主要介绍如何配置阿里云的OSS、RAM访问控制,然后使用子账户直接颁发临时凭证,供前端javascript或者其他后端来操作我们的阿里云资源。

    首先说下目前应用场景:我需要将一个网站的所有静态资源(图片、视频等)上传的阿里云存储,访问的时候减少服务器带宽压力,希望上传过程直接通过前端js来操作,不再通过后端服务器,那么我们需要为前端提供上传凭证,就需要后端程序提供一个api来供其他服务或者前端获取凭证,阿里云对权限这一块,做的很完善,主要是给子账户配置权限,在哪配,怎么配置,做的越完善肯定越复杂。

    当然你也可以直接使用七牛云,更加简单 七牛云对象存储JS SDK使用录


一、首先我们要明白阿里云的RAM访问控制是如何运作的。

    image.png

有几个概念需要明白:

    子账户(用户):你的阿里云账号登录控制台,是最大最高权限的账号,那么里面有很多数据子系统,你可以创建其他子账户来给你的各种应用使用,总不能直接给总账户吧,和OA权限一样的,各司其职。

    用户组:你可以理解成企业的部门,分配权限的时候,直接给部门分权限,那么这里面的所有人都有部门权限了。

    RAM角色:这个就是权限的具体体现,权限就是一个功能一个功能的操作控制,代表了能不能操作,那么RAM角色就是把权限进行打包,然后关联到人或者用户组;比如你的OSS操作和ECS操作是分别给两个APP使用的权限,那么就可以创建两个RAM角色,将他们分别分给各司其职的子账户即可。

    STS:这是阿里云用来给子账户颁发临时密钥的系统,有个AssumeRole接口,我们后端API给前端提供的信息就是通过这个接口来的。


二、如何配置RAM?

    1、登录阿里云【控制台】,鼠标移动到头像上,就会看到【访问控制】,进入到控制访问主界面

image.pngimage.png

    2、点开【用户】,点击【创建用户】,创建一个OssAccount的子账户,然后访问我需要的接口访问,不需要登录控制台,所以选择【编程访问】,点击确定

image.png

    看到用户信息的界面已经展示出来了,如下图。把这两个AccessKey记录一下,等哈用

image.png

3、我们来直接给这个账户添加权限,添加【AliyunOSSFullAccess】、【AliyunSTSAssumeRoleAccess】这两个权限,保存即可,子账户就建立好了。

image.png

    4、点击【RAM角色管理】打开界面,准备创建角色信息,点击【创建RAM】角色,名为Ram

image.pngimage.png

image.png

    5、还是一样的,授权【AliyunOSSFullAccess】、【AliyunSTSAssumeRoleAccess】,这里点击角色ram名称,进入详情,看到ARN信息,记录下等哈用。

image.png

三、怎么配置OSS?

    1、创建OSS,进入主控制台,点击【Bucket列表】,再点击【创建Bucket】

image.png

    2、我取名为carsonyangblog,选择【华南1(深圳)】,读写权限选择【公共读取】,确定即可。

        公共读是因为我们要对外访问,可能我的图片需要给别的域名下使用。

image.png

    3、还需要配置授权策略和跨域,因为我们要让前端直接上传到我们的bucket中。

image.png

    4、点击【Bucket授权策略】,再点击【新增授权】,选择【子账户】,授权选择【完全控制】,即可。

image.png

    5、点击【跨域设置】,再点击【创建规则】,按下图设置。

image.png


到这里我们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>

image.png

CarsonIT 微信扫码关注公众号 策略、创意、技术

留下您的脚步

 

最近评论

查看更多>>

热点推荐

友情打赏

请打开您的微信,扫一扫