使用 OAuth 令牌下载 blob 的 Azure 函数

时间:2021-06-11 07:34:13

标签: azure azure-functions azure-storage-blobs

我有大量的 PDF 存储在 blob 容器中(我们称之为 demo_container),在每个 pdf 文件中都有指向其他 pdf 的链接(链接也重定向到 blob)。

我正在寻找一种方法来确保只有当用户具有访问该 blob 的权限(通过 IAM 配置)时链接才有效;这背后的想法是,即使有人偷了一个 pdf,他也无法打开其他的。

这需要使用 OAuth 来获取令牌等,最终认为我需要创建一个充当重定向器的函数(通常,PDF 中的链接会使用所需文件的名称调用我的 API在查询中)。首先,到目前为止我是对的吗?或者有没有更简单的方法来做到这一点?

为了尽可能快地运行(并且因为它不应该是需要太多编程的东西),我使用了 powershell 代码,对我的应用进行了应用注册,并授予它对 Storage_API 的访问权限(使用用户模拟)和 Graph_API,正如 MS 文档中所述。

当我在浏览器上调用我的 API 时,我得到了 MS 身份验证页面并且一切正常,在我的代码中,我尝试确保令牌已正确传递,因此我在函数的上下文中下载了一个 blob 并检查它尺寸。 事实是,只有当我使用有权连接到此 blob 的帐户登录(在 Sto 帐户和容器上配置 IAM)时,它才应该工作,但我不知道为什么无论是谁,脚本的工作方式都是一样的已认证。 我要测试的代码:

using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
# Interact with query parameters or the body of the request.
$name = $Request.Query.Name
$context = (Get-AzStorageAccount -name "hamzasto" -ResourceGroupName "testrg").context
$temp=Get-AzStorageBlobContent -Container "blob1" -Blob "sample.pdf" -Destination ".\" -context $context -force
$body="Size of the file is "+($temp.length)
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
ContentType = 'text/html'
Body = $body
})

我很困惑,因为函数可以使用来自用户的令牌访问 blob(函数中的任何地方都没有使用 SAS),这很奇怪。欢迎所有帮助

enter image description here enter image description here enter image description here enter image description here enter image description here

1 个答案:

答案 0 :(得分:0)

无需使用额外的 Azure 函数,如果您获得具有范围的访问令牌,则可以直接在您的 Web 应用程序上执行此操作:https://<storage account name>.blob.core.windows.net/user_impersonation

我给你写了一个简单的demo,它使用msal.js来登录用户并获取存储blob的访问令牌,只需参考下面的代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    
    <title>Azure AD test</title>
    <script type="text/javascript" src="https://alcdn.msauth.net/lib/1.4.4/js/msal.min.js"></script>

</head>
<body>
    <div >
                    
        <button id="SignIn" onclick="signIn()">Sign in</button><br/>
        <div id="WelcomeMessage"/><br/>
    </div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
    
    var clientAppID = ""
    var tenantID = ""
    var storageAccount = ""
    
    var demoScops = {
         scopes:["https://"+ storageAccount +".blob.core.windows.net/user_impersonation"]
    }

    var msalConfig = {
             auth: {
                 clientId: clientAppID,
                 authority: "https://login.microsoftonline.com/" + tenantID
            },
             cache: {
                 cacheLocation: "localStorage",
                 storeAuthStateInCookie: true
            }
    };
     

    var myMSALObj = new Msal.UserAgentApplication(msalConfig);
    myMSALObj.handleRedirectCallback(authRedirectCallBack);
             
    
    function signIn() {
     
         myMSALObj.loginPopup(demoScops).then(function (loginResponse) {
            console.log(loginResponse);
            initPage();
             
         }).catch(function (error) {
             console.log(error);
         });
     }
     
    function initPage(){
        showWelcomeMessage();
     }
     

     
     function showWelcomeMessage() {
             
         var divWelcome = document.getElementById('WelcomeMessage');
         divWelcome.innerHTML = 'welcome! ' + myMSALObj.account.userName + '<div> blobURL: <input id = "bloburl"/><button onClick="downloadBlob()">download blob</button><div id="errorMessage"/><div>';
         var loginbutton = document.getElementById('SignIn');
         
         loginbutton.innerHTML = 'sign out';
         loginbutton.setAttribute('onclick', 'signOut();');
     }
     
     function downloadBlob(){
         myMSALObj.acquireTokenSilent(demoScops).then(function (tokenResponse) {
            var blobURL = document.getElementById('bloburl').value;
            var accessToken = tokenResponse.accessToken;
            var xhttp = new XMLHttpRequest();
            
            xhttp.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
                document.getElementById('errorMessage').innerHTML = '';
                var downloadUrl = URL.createObjectURL(xhttp.response);
                var a = document.createElement("a");
                document.body.appendChild(a);
                a.style = "display: none";
                a.href = downloadUrl;
                a.download = blobURL.split('/').pop();
                a.click();
            }else{
                console.log(xhttp.response)
                document.getElementById('errorMessage').innerHTML = 'error';
            }
            };
            xhttp.open("GET", blobURL);
            xhttp.responseType = "blob";
            xhttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
            xhttp.setRequestHeader('x-ms-version', '2017-11-09');
            xhttp.send(); 
              
              
         }).catch(function (error) {
              console.log(error);
              })
        }     
     
   
 
     
     function authRedirectCallBack(error, response) {
         if (error) {
             console.log(error);
         }
     }
     
     function requiresInteraction(errorCode) {
         if (!errorCode || !errorCode.length) {
             return false;
         }
         return errorCode === "consent_required" ||
             errorCode === "interaction_required" ||
             errorCode === "login_required";
     }
     

     var ua = window.navigator.userAgent;
     var msie = ua.indexOf('MSIE ');
     var msie11 = ua.indexOf('Trident/');
     var msedge = ua.indexOf('Edge/');
     var isIE = msie > 0 || msie11 > 0;
     var isEdge = msedge > 0;

     var loginType = isIE ? "REDIRECT" : "POPUP";
     
     if (loginType === 'POPUP') {
          if (myMSALObj.getAccount()) {
              initPage()
          }
     }
     else if (loginType === 'REDIRECT') {
         document.getElementById("SignIn").onclick = function () {
              myMSALObj.loginRedirect(requestObj);
         };
         if (myMSALObj.getAccount() && !myMSALObj.isCallback(window.location.hash)) {
              initPage()
          }
     } else {
         console.error('Please set a valid login type');
     }
     
     

      function signOut() {
          window.localStorage.clear();
          myMSALObj.logout();
      }
    
</script>

</html>

结果:

我授予了我的存储帐户容器的第一个测试帐户数据读取者角色: enter image description here

当我使用此帐户访问此容器下的 blob 时,它的工作方式除外: enter image description here

当我使用另一个没有读取权限的测试帐户时,用户无法访问它: enter image description here

顺便说一句,在 html 页面上运行逻辑之前,请确保您已为您的存储帐户启用 CORS: enter image description here

相关问题