我目前正在开发一个项目,允许用户使用您的Microsoft工作/学校帐户验证他们的登录。目前,我有一个用户调用API登录的页面,如下所示:
// Graph API endpoint to show user profile
var graphApiEndpoint = "https://graph.microsoft.com/v1.0/me";
// Graph API scope used to obtain the access token to read user profile
var graphAPIScopes = ["https://graph.microsoft.com/user.read"];
// Initialize application
var userAgentApplication = new Msal.UserAgentApplication(msalconfig.clientID, null, loginCallback, {
redirectUri: msalconfig.redirectUri
});
//Previous version of msal uses redirect url via a property
if (userAgentApplication.redirectUri) {
userAgentApplication.redirectUri = msalconfig.redirectUri;
}
window.onload = function () {
// If page is refreshed, continue to display user info
if (!userAgentApplication.isCallback(window.location.hash) && window.parent === window && !window.opener) {
var user = userAgentApplication.getUser();
if (user) {
callGraphApi();
}
}
}
/**
* Call the Microsoft Graph API and display the results on the page. Sign the user in if necessary
*/
function callGraphApi() {
var user = userAgentApplication.getUser();
if (!user) {
// If user is not signed in, then prompt user to sign in via loginRedirect.
// This will redirect user to the Azure Active Directory v2 Endpoint
userAgentApplication.loginRedirect(graphAPIScopes);
// The call to loginRedirect above frontloads the consent to query Graph API during the sign-in.
// If you want to use dynamic consent, just remove the graphAPIScopes from loginRedirect call.
// As such, user will be prompted to give consent when requested access to a resource that
// he/she hasn't consented before. In the case of this application -
// the first time the Graph API call to obtain user's profile is executed.
} else {
// If user is already signed in, display the user info
window.location = "calc.html"
}
}
/**
* Callback method from sign-in: if no errors, call callGraphApi() to show results.
* @param {string} errorDesc - If error occur, the error message
* @param {object} token - The token received from login
* @param {object} error - The error string
* @param {string} tokenType - The token type: For loginRedirect, tokenType = "id_token". For acquireTokenRedirect, tokenType:"access_token".
*/
function loginCallback(errorDesc, token, error, tokenType) {
if (errorDesc) {
showError(msal.authority, error, errorDesc);
} else {
callGraphApi();
}
}
/**
* Show an error message in the page
* @param {string} endpoint - the endpoint used for the error message
* @param {string} error - Error string
* @param {string} errorDesc - Error description
*/
function showError(endpoint, error, errorDesc) {
var formattedError = JSON.stringify(error, null, 4);
if (formattedError.length < 3) {
formattedError = error;
}
document.getElementById("errorMessage").innerHTML = "An error has occurred:<br/>Endpoint: " + endpoint + "<br/>Error: " + formattedError + "<br/>" + errorDesc;
console.error(error);
}
/**
* Call a Web API using an access token.
* @param {any} endpoint - Web API endpoint
* @param {any} token - Access token
* @param {object} responseElement - HTML element used to display the results
* @param {object} showTokenElement = HTML element used to display the RAW access token
*/
function callWebApiWithToken(endpoint, token, responseElement, showTokenElement) {
var headers = new Headers();
var bearer = "Bearer " + token;
headers.append("Authorization", bearer);
var options = {
method: "GET",
headers: headers
};
fetch(endpoint, options)
.then(function (response) {
var contentType = response.headers.get("content-type");
if (response.status === 200 && contentType && contentType.indexOf("application/json") !== -1) {
response.json()
.then(function (data) {
// Display response in the page
console.log(data);
responseElement.innerHTML = JSON.stringify(data, null, 4);
if (showTokenElement) {
showTokenElement.parentElement.classList.remove("hidden");
showTokenElement.innerHTML = token;
}
})
.catch(function (error) {
showError(endpoint, error);
});
} else {
response.json()
.then(function (data) {
// Display response as error in the page
showError(endpoint, data);
})
.catch(function (error) {
showError(endpoint, error);
});
}
})
.catch(function (error) {
showError(endpoint, error);
});
}
/**
* Sign-out the user
*/
function signOut() {
userAgentApplication.logout();
}
<!DOCTYPE html>
<html>
<head>
<!-- bootstrap reference used for styling the page -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<title>Home</title>
</head>
<body style="margin: 40px">
<button id="callGraphButton" type="button" class="btn btn-primary" onclick="callGraphApi()">Call Microsoft Graph API</button>
<div id="errorMessage" class="text-danger"></div>
<div class="hidden">
<h3>Graph API Call Response</h3>
<pre class="well" id="graphResponse"></pre>
</div>
<div class="hidden">
<h3>Access Token</h3>
<pre class="well" id="accessToken"></pre>
</div>
<div class="hidden">
<h3>ID Token Claims</h3>
<pre class="well" id="userInfo"></pre>
</div>
<button id="signOutButton" type="button" class="btn btn-primary hidden" onclick="signOut()">Sign out</button>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/0.1.3/js/msal.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.4/bluebird.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script>
<script type="text/javascript" src="msalconfig.js"></script>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
该页面还连接到msalconfig文件,但出于安全原因,我不会包含所述文件,因为它包含一些令牌。
如果用户成功登录,他/她将被路由到calc.html,这是一个我设计用于测试目的的简单caluclator应用程序。但是,目前,任何人都可以访问calc.html文件,如果他们转到URL。我想要一种方法只允许登录用户访问该页面,因为最终,该项目将允许我所在机构的各位教授和其他人在十年内改变他们课程的计划。没有PHP,有没有办法做到这一点?如果是这样,这些方式是什么样的?如果可能的话,我宁愿保持基于Web和客户端的东西,但我愿意接受任何事情。
这是一个相当开放的问题,但我会抨击它。
首先,你声明你不愿意分享msaclconfig.js
- 如果你不想将它分享给Stack Overflow,我完全理解。但是,您正在将该文件共享给正在加载登录页面的任何人。那可能完全是任何人。
对于您所说的用例 - 多人编辑和查看同一组数据 - 您将需要一个服务器端组件。但是,您不一定需要自己创建或托管该组件。由于您已经使用Microsoft的服务进行身份验证,也许您也可以使用他们的服务进行数据存储?例如,Graph API突出显示它可以访问Excel,并且可能用作存储后端。
如果您使用Microsoft的服务进行身份验证和数据存储,则加载应用程序页面的非授权用户可能不是问题。他们可以看到页面本身,但是他们无法查看或编辑计划 - 前提是当前正确设置了查看和编辑支持数据存储的权限。当然,这是一种安全权衡。看到该应用程序可以帮助潜在的攻击者,但如果服务器足够安全,这是可以接受的。
类似的东西适用于您可能使用的任何存储后端。在身份验证时,会生成某种身份验证令牌。这应该是这样的,只有服务器能够生成有效的令牌,并且服务器可以稍后检查它从客户端收到的令牌是否有效。在每次请求数据存储时,都会检查该令牌,并且只能使用有效令牌访问数据。查看静态应用程序页面可能不是问题,因为实际的敏感内容仅传递给经过身份验证和授权的用户。