我有大量的 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),这很奇怪。欢迎所有帮助
答案 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>
结果: