Construct Azure Shared Access Signature URL to Download File From Azure Blob Storage
Learn how to construct an Azure Shared Access Signature URL to download a particular file from the Azure Blob Storage container.
Join the DZone community and get the full member experience.
Join For FreeIn any web portal, to access a file stored in an Azure Blob Storage account container, we need to connect to the Azure Blob Storage Account and construct the download link for the specified file using its name and Azure storage account details. Constructed Azure Blob URLs can also be used to upload a file and delete a file from the web portal using Java code.
A shared access signature (SAS) enables you to grant limited access to containers and blobs in your storage account. When you create a SAS, you specify its constraints, including which Azure Storage resources a client is allowed to access, what permissions they have on those resources, and how long the SAS is valid.
Implementation
For example, we can use a Java application to connect to Azure and obtain the download link:
@Resource
private DocumentManagementFacade documentManagementFacade;
@PostMapping(value = "/downloadDocument.ajax")
@ResponseBody
public JSONObject downloadDocument(@RequestParam(name = "documentName", required = true) String documentName, final Model model) throws JSONException {
final JSONObject result = new JSONObject();
String blobUrl = documentManagementFacade.getBlobUrl();
String downloadUrl = addFileNameToBlobUrl(blobUrl,documentName);
result.append("downloadUrl", downloadUrl); // this url brower can directly use for downloading file
return result;
}
/* Method used to add the document name to the blob url
* @param blobUrl - blob url
* @param documentName - document name
* @return String - URL of the file
*/
private String addFileNameToBlobUrl(String blobUrl, String documentName) {
int indexOfQueryString = blobUrl.indexOf("?");
return blobUrl.substring(0, indexOfQueryString) + "/" + documentName + blobUrl.substring(indexOfQueryString);
}
We have implemented the above AJAX call from the front end to enable document downloads in the portal.
Then, we need to write the facade method "getBlobUrl
" as called in the above controller to get the Blob URL. This Blob URL can also be used to upload or delete a file from a specific Azure storage account container.
@Override
public String getBlobUrl() {
BlobUrlData blobUrlData = new BlobUrlData();
blobUrlData.setStorageAccountName(getConfiguration().getString(STORAGE_ACCOUNT_NAME));
blobUrlData.setContainerName(getConfiguration().getString(CONTAINER_NAME));
String sasToken = azureStorageBlobSASIntegrationService.getSasTokenForAzureConnection();
blobUrlData.setAccountSasToken(sasToken);
return blobUrlDataBeanPatternFormatter.format(blobUrlData);
}
In this above facade code, we need to configure the Azure storage account name and container name in an application properties file. Then, we need to call the integration service method to get a secure access token from Azure Blob storage and format the download URL with the provided values.
- Sample Azure Blob storage URL as follows:
https://{storageAccountName}.blob.core.windows.net/{containerName}?{accountSasToken}
- The
storageAccountName
andcontainerName
are retrieved from the application's properties file. - In the code below, we can see how to construct the
accountSasToken
value.
private static final String API_KEY = "APIKey";
private static final String STORAGE_ACCOUNT_NAME = // storage account name from properties file
private static final String SHARED_ACCESS_KEY = // storage account shared access key from properties file which you can get from Azure
private static final String CONTAINER_NAME = // container name from properties file
private static final String UTC_DATE_FORMATTER = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private static final String SIGNED_PERMISSION = "racwd";
private static final String SIGNED_SERVICES = "c";
private static final String SIGNED_PROTOCOL = "https";
private static final String AZURE_API_VERSION = "2022-11-02";
private static final String SECRET_KEY_ALGORITHM = "HmacSHA256";
private static final String SLASH = "/";
@Autowired
private ConfigurationService configurationService;
@Autowired
private BeanPatternFormatter<SasTokenUrlData> sasTokenUrlFormatter;
@Override
public String getSasTokenForAzureConnection() {
String signedPermission = configurationService.getConfiguration().getString(SIGNED_PERMISSION);
String signedServices = configurationService.getConfiguration().getString(SIGNED_SERVICES);
String signedExpiryIntervalMinutes = configurationService.getConfiguration().getString(EXPIRY_INTERVAL);
ZonedDateTime signedStart = ZonedDateTime.now(ZoneOffset.UTC);
ZonedDateTime signedExpiry = signedStart.plusMinutes(signedExpiryIntervalMinutes == null ? SIGNED_EXPIRY_INTERVAL_MINUTES : Long.parseLong(signedExpiryIntervalMinutes));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(UTC_DATE_FORMATTER);
String formattedSignedStart = signedStart.format(formatter);
String formattedSignedExpiry = signedExpiry.format(formatter);
//Set Signature String
String stringToSign = getStringToSign(signedPermission, formattedSignedStart, formattedSignedExpiry, signedServices);
String signature = getHMAC256(stringToSign);
return getSasTokenString(signature, formattedSignedStart, formattedSignedExpiry, signedPermission, signedServices);
}
/**
* Constructs a string to be signed for generating a Shared Access Signature (SAS) token for Azure Blob Storage.
*
* @param signedPermission The permissions associated with the SAS token.
* This could be read (r), write (w), delete (d), list (l), add (a), create (c), update (u) and process (p).
* @param formattedSignedStart The start time for the SAS token, formatted as a UTC string in the format "yyyy-MM-dd'T'HH:mm:ss'Z'".
* @param formattedSignedExpiry The expiry time for the SAS token, formatted as a UTC string in the format "yyyy-MM-dd'T'HH:mm:ss'Z'".
* @param signedServices The services accessible with the SAS token. This could be blob (b), file (f), queue (q), and table (t).
* @return A string that needs to be signed to generate a SAS token.
*/
private String getStringToSign(String signedPermission, String formattedSignedStart, String formattedSignedExpiry, String signedServices) {
String accountName = configurationService.getConfiguration().getString(STORAGE_ACCOUNT_NAME);
String containerName = configurationService.getConfiguration().getString(CONTAINER_NAME);
String canonicalResource = BLOB + accountName + SLASH + containerName;
return signedPermission + "\n" + formattedSignedStart + "\n" + formattedSignedExpiry + "\n" +
canonicalResource + "\n" + SIGNED_PROTOCOL + "\n" +
AZURE_API_VERSION + "\n" + signedServices + "\n";
}
/**
* Generates a HMAC256 signature for a given string using the shared access key.
*
* @param signStr The string to be signed.
* @return The HMAC256 signature of the input string. If an error occurs during the process or the shared access key is empty, an empty string is returned.
*/
private String getHMAC256(String signStr) {
String sharedAccessKey = configurationService.getConfiguration().getString(SHARED_ACCESS_KEY);
if (StringUtils.isEmpty(sharedAccessKey)) {
LOG.error("Shared Access Key is empty");
return StringUtils.EMPTY;
}
try {
SecretKeySpec secretKey = new SecretKeySpec(Base64.decode(sharedAccessKey), SECRET_KEY_ALGORITHM);
Mac sha256HMAC = Mac.getInstance(SECRET_KEY_ALGORITHM);
sha256HMAC.init(secretKey);
return Base64.encodeBytes(sha256HMAC.doFinal(signStr.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
LOG.error("Exception occurred while converting signature {}", e.getMessage());
return StringUtils.EMPTY;
}
}
/**
* Constructs a Shared Access Signature (SAS) token string for Azure Blob Storage.
*
* @param signature The HMAC256 signature of the string to sign.
* @param signedStartTime The start time for the SAS token, formatted as a UTC string in the format "yyyy-MM-dd'T'HH:mm:ss'Z'".
* @param signedExpiryTime The expiry time for the SAS token, formatted as a UTC string in the format "yyyy-MM-dd'T'HH:mm:ss'Z'".
* @param signedPermission The permissions associated with the SAS token.
* This could be read (r), write (w), delete (d), list (l), add (a), create (c), update (u) and process (p).
* @param signedServices The services accessible with the SAS token. This could be blob (b), file (f), queue (q), and table (t).
* @return A SAS token string that can be used to access Azure Blob Storage.
*/
private String getSasTokenString(String signature, String signedStartTime, String signedExpiryTime, String signedPermission, String signedServices) {
SasTokenUrlData sasTokenUrlData = new SasTokenUrlData();
sasTokenUrlData.setSignedPermission(signedPermission);
sasTokenUrlData.setSignedStartTime(signedStartTime);
sasTokenUrlData.setSignedExpiryTime(signedExpiryTime);
sasTokenUrlData.setSignedServices(signedServices);
sasTokenUrlData.setSignedProtocol(SIGNED_PROTOCOL);
sasTokenUrlData.setSignature(URLEncoder.encode(signature, StandardCharsets.UTF_8));
return sasTokenUrlFormatter.format(sasTokenUrlData);
}
Once Azure URL is created we are just opening this URL in a new window from JavaScript.
downloadDocumentCallbackSuccess : function(data) {
let parsedData = JSON.parse(data);
window.open(parsedData.downloadUrl);
},
Published at DZone with permission of Chaitanya Kapure. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments