DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Securing Your Software Supply Chain with JFrog and Azure
Register Today

Trending

  • The Role of Automation in Streamlining DevOps Processes
  • Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
  • New ORM Framework for Kotlin
  • Grow Your Skills With Low-Code Automation Tools

Trending

  • The Role of Automation in Streamlining DevOps Processes
  • Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
  • New ORM Framework for Kotlin
  • Grow Your Skills With Low-Code Automation Tools
  1. DZone
  2. Data Engineering
  3. Databases
  4. Windows Azure Storage magic with Shared Access Signatures

Windows Azure Storage magic with Shared Access Signatures

Maarten Balliauw user avatar by
Maarten Balliauw
·
Feb. 18, 14 · Interview
Like (0)
Save
Tweet
Share
7.07K Views

Join the DZone community and get the full member experience.

Join For Free

When building cloud applications on Windows Azure, it’s always a good thing to delegate as much work to specialized services as possible. File downloads would be one good example: these can be streamed directly from Windows Azure blob storage to your client, without having to pass a web application hosted on Windows Azure Cloud Services or Web Sites. Why occupy the web server with copying data from a request stream to a response stream? Let blob storage handle it!

When thinking this through there may be some issues you may think of. Here are a few:

  • How can I keep this blob secure? I don’t want to give everyone access to it!
  • How can I keep the download URL on my web server, track the number of downloads (or enforce other security rules) and still benefit from offloading the download to blob storage?
  • How can the blob be stored in a way that is clear to my application (e.g. a customer ID or something), yet give it a friendly name when downloading?

Let’s answer these!

Meet Shared Access Signatures

Keeping blobs secure is pretty easy on Windows Azure Blob Storage, but it’s also sort of an all-or-nothing story… Either you make all blobs in a container private, or you make them public.

Not to worry though! Using Shared Access Signatures it is possible to grant temporary privileges on a blob, for read and write access. Here’s a code snippet that will grant read access to the blob named helloworld.txt, residing in a private container named files, during the next minute:

CloudStorageAccount account = // your storage account connection here
var client = account.CreateCloudBlobClient();
var container = client.GetContainerReference("files");
var blob = container.GetBlockBlobReference("helloworld.txt");

var builder = new UriBuilder(blob.Uri);
builder.Query = blob.GetSharedAccessSignature(
    new SharedAccessBlobPolicy
    {
        Permissions = SharedAccessBlobPermissions.Read,
        SharedAccessStartTime = new DateTimeOffset(DateTime.UtcNow.AddMinutes(-5)),
        SharedAccessExpiryTime = new DateTimeOffset(DateTime.UtcNow.AddMinutes(1)
    }).TrimStart('?');

var signedBlobUrl = builder.Uri;

Note I’m giving access starting 5 minutes ago, just to make sure any clock skew along the way is ignored within a reasonable time window.

There we go: our blob is secured and by passing along the signedBlobUrl to our user, he or she can start downloading our blob without having access to any other blobs at all.

Meet HTTP redirects

Shared Access Signatures are really cool, but the generated URLs are… “fugly”, they are not pretty or easy to remember. Well, there is this thing called HTTP redirects, right? Here’s an ASP.NET MVC action method that checks if the user is authenticated, queries a repository for the correct filename, generates the signed access signature and redirects us to the actual download.

[Authorize]
[EnsureInvoiceAccessibleForUser]
public ActionResult DownloadInvoice(string invoiceId)
{
    // Fetch invoice
    var invoice = InvoiceService.RetrieveInvoice(invoiceId);
    if (invoice == null)
    {
        return new HttpNotFoundResult();
    }
    
    // We can do other things here: track # downloads, ...

    // Build shared access signature
    CloudStorageAccount account = // your storage account connection here
    var client = account.CreateCloudBlobClient();
    var container = client.GetContainerReference("invoices");
    var blob = container.GetBlockBlobReference(invoice.CustomerId + "-" + invoice.InvoiceId);

    var builder = new UriBuilder(blob.Uri);
    builder.Query = blob.GetSharedAccessSignature(
        new SharedAccessBlobPolicy
        {
            Permissions = SharedAccessBlobPermissions.Read,
            SharedAccessStartTime = new DateTimeOffset(DateTime.UtcNow.AddMinutes(-5)),
            SharedAccessExpiryTime = new DateTimeOffset(DateTime.UtcNow.AddMinutes(1)
        }).TrimStart('?');

    var signedBlobUrl = builder.Uri;

    // Redirect
    return Redirect(signedBlobUrl);
}

This gives us the best of both worlds: our web application can still verify access and run some business logic on it, yet we can offload the file download to blob storage.

Meet Shared Access Signatures content disposition header

Often, storage is a technical thing where we choose technical filenames for the things we store, instead of human-readable or human-friendly file names. In the example above, users will get a very strange filename to be downloaded: the customer id + invoice id, concatenated. No .pdf file extension, nothing else either. Users may get confused by this, or have problems opening the file because teir browser will not recognize this is a PDF.

Last November, a feature was added to blob storage which enables us to let a blob be whatever we want it to be: support for setting some additional headers on a blob through the Shared Access Signature.

The following headers can be specified on-the-fly, through the shared access signature:

  • Cache-Control
  • Content-Disposition
  • Content-Encoding
  • Content-Language
  • Content-Type

Here’s how to generate a meaningful Shared Access Signature in the previous example, where we specify a human-readable filename for the resulting download, as well as a custom content type:

builder.Query = blob.GetSharedAccessSignature(
    new SharedAccessBlobPolicy
    {
        Permissions = SharedAccessBlobPermissions.Read,
        SharedAccessStartTime = new DateTimeOffset(DateTime.UtcNow.AddMinutes(-5)),
        SharedAccessExpiryTime = new DateTimeOffset(DateTime.UtcNow.AddMinutes(1)
    },
    new SharedAccessBlobHeaders
    {
        ContentDisposition = "attachment; filename="
            + customer.DisplayName + "-invoice-" + invoice.InvoiceId + ".pdf",
        ContentType = "application/pdf"
    }).TrimStart('?');

Note: for this feature to work, the service version for the storage account must be set to the latest one, using the DefaultServiceVersion on the blob client. Here’s an example:

CloudStorageAccount account = // your storage account connection here
var client = account.CreateCloudBlobClient();
var serviceProperties = client.GetServiceProperties();
serviceProperties.DefaultServiceVersion = "2013-08-15";
client.SetServiceProperties(serviceProperties);

Combining all these techniques, we can do some analytics and business logic in our web application and offload the boring file and network I/O to blob storage.

Enjoy!

azure Web application application Web Service Database Download Business logic All or nothing (armor) ASP.NET MVC

Published at DZone with permission of Maarten Balliauw, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • The Role of Automation in Streamlining DevOps Processes
  • Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
  • New ORM Framework for Kotlin
  • Grow Your Skills With Low-Code Automation Tools

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: