您已建立用戶端應用程式物件。 現在,您使用該物件取得權杖來呼叫 Web API。 在 ASP.NET 或 ASP.NET Core 中,呼叫 Web API 是在控制器中完成:
Microsoft.Identity.Web 會新增擴充方法,以提供用來呼叫 Microsoft Graph 或下游 Web API 的便利服務。 這些方法在 呼叫 Web API 的 Web 應用程式:呼叫 API 中有詳細的說明。 使用這些協助程式方法時,您不需要手動取得權杖。
但是,如果您想要以手動方式取得權杖,下列程式碼會示範使用 Microsoft.Identity.Web 在首頁控制器中執行此動作的範例。 它會使用 REST API 呼叫 Microsoft Graph (而非 Microsoft Graph SDK)。 通常,您不需要取得權杖,而是需要建置授權標頭以新增至您的要求。 若要取得授權標頭,您可以透過控制器的建構函式 (如果使用 Blazor,則為頁面建構函式) 中的相依性插入來插入 IAuthorizationHeaderProvider
服務,並在控制器動作中使用。 這個介面有方法可產生包含通訊協定 (Bearer、Pop 等) 和權杖的字串。 若要取得代表使用者呼叫 API 的授權標頭,請使用 (CreateAuthorizationHeaderForUserAsync
)。 若要取得授權標頭來代表應用程式本身呼叫下游 API,請在精靈案例中使用 (CreateAuthorizationHeaderForAppAsync
)。
控制器方法會受到[Authorize]
屬性的保護,確保僅有已驗證的使用者可以使用 Web 應用程式。
[Authorize]
public class HomeController : Controller
{
readonly IAuthorizationHeaderProvider authorizationHeaderProvider;
public HomeController(IAuthorizationHeaderProvider authorizationHeaderProvider)
{
this.authorizationHeaderProvider = authorizationHeaderProvider;
}
// Code for the controller actions (see code below)
}
ASP.NET Core 透過相依性插入提供 IAuthorizationHeaderProvider
。
以下是HomeController
動作的簡易程式碼,其會取得要呼叫 Microsoft Graph 的權杖:
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public async Task<IActionResult> Profile()
{
// Acquire the access token.
string[] scopes = new string[]{"user.read"};
string accessToken = await authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(scopes);
// Use the access token to call a protected web API.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", accessToken);
string json = await client.GetStringAsync(url);
}
如要更加了解此情節要求的程式碼,請參閱階段 2 (2-1-Web 應用程式呼叫 Microsoft Graph) 的 ms-identity-aspnetcore-webapp-tutorial 教學課程步驟。
控制器動作頂端的AuthorizeForScopes
屬性 (或如果您使用 Razor 範本,即Razor 頁面上的屬性) 是由 Microsoft.Identity.Web 提供。 其可確保使用者會在需要且遞增的情況下要求同意。
還有其他複雜的變化,例如:
這些進階步驟在 3-WebApp-multi-APIs 教學課程的第 3 章中有涵蓋。
ASP.NET 的程式碼類似於 ASP.NET Core 所顯示的程式碼:
- 由
[Authorize]
屬性保護的控制器動作,會擷取控制器的 ClaimsPrincipal
成員的租用戶識別碼和使用者識別碼 (ASP.NET 使用 HttpContext.User
)。 這可確保只有已驗證的使用者可以使用應用程式。
Microsoft.Identity.Web 會將擴充方法新增至控制器,以提供方便的服務來呼叫 Microsoft Graph 或下游 Web API,或取得授權標頭,甚至是權杖。 呼叫 Web API 的 Web 應用程式:呼叫 API 中詳細介紹了用於直接呼叫 API 的方法。 使用這些協助程式方法時,您不需要手動取得權杖。
不過,如果您確實想要手動取得權杖或建置授權標頭,下列程式碼會示範如何使用 Microsoft.Identity.Web 在控制器中執行此動作。 其會使用 REST API 而不是 Microsoft Graph SDK 來呼叫 API (Microsoft Graph)。
若要取得授權標頭,您可以使用擴充方法 GetAuthorizationHeaderProvider
從控制器取得 IAuthorizationHeaderProvider
服務。 若要取得代表使用者呼叫 API 的授權標頭,請使用 CreateAuthorizationHeaderForUserAsync
。 若要取得授權標頭來代表應用程式本身呼叫下游 API,請在精靈案例中使用 CreateAuthorizationHeaderForAppAsync
。
下列程式碼片段顯示 HomeController
的動作,此動作會取得授權標頭,以將 Microsoft Graph 呼叫為 REST API:
[Authorize]
public class HomeController : Controller
{
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public async Task<IActionResult> Profile()
{
// Get an authorization header.
IAuthorizationHeaderProvider authorizationHeaderProvider = this.GetAuthorizationHeaderProvider();
string[] scopes = new string[]{"user.read"};
string authorizationHeader = await authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(scopes);
// Use the access token to call a protected web API.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", authorizationHeader);
string json = await client.GetStringAsync(url);
}
}
下列程式碼片段顯示 HomeController
的動作,此動作會取得存取權杖,以將 Microsoft Graph 呼叫為 REST API:
[Authorize]
public class HomeController : Controller
{
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public async Task<IActionResult> Profile()
{
// Get an authorization header.
ITokenAcquirer tokenAcquirer = TokenAcquirerFactory.GetDefaultInstance().GetTokenAcquirer();
string[] scopes = new string[]{"user.read"};
string token = await tokenAcquirer.GetTokenForUserAsync(scopes);
// Use the access token to call a protected web API.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
string json = await client.GetStringAsync(url);
}
}
在 JAVA 範例中,呼叫 API 的程式碼位於 AuthPageController.java#L62 的 getUsersFromGraph
方法中。
方法嘗試呼叫getAuthResultBySilentFlow
。 如果使用者需要同意更多範圍,則程式碼會處理MsalInteractionRequiredException
物件以挑戰使用者。
@RequestMapping("/msal4jsample/graph/me")
public ModelAndView getUserFromGraph(HttpServletRequest httpRequest, HttpServletResponse response)
throws Throwable {
IAuthenticationResult result;
ModelAndView mav;
try {
result = authHelper.getAuthResultBySilentFlow(httpRequest, response);
} catch (ExecutionException e) {
if (e.getCause() instanceof MsalInteractionRequiredException) {
// If the silent call returns MsalInteractionRequired, redirect to authorization endpoint
// so user can consent to new scopes.
String state = UUID.randomUUID().toString();
String nonce = UUID.randomUUID().toString();
SessionManagementHelper.storeStateAndNonceInSession(httpRequest.getSession(), state, nonce);
String authorizationCodeUrl = authHelper.getAuthorizationCodeUrl(
httpRequest.getParameter("claims"),
"User.Read",
authHelper.getRedirectUriGraph(),
state,
nonce);
return new ModelAndView("redirect:" + authorizationCodeUrl);
} else {
mav = new ModelAndView("error");
mav.addObject("error", e);
return mav;
}
}
if (result == null) {
mav = new ModelAndView("error");
mav.addObject("error", new Exception("AuthenticationResult not found in session."));
} else {
mav = new ModelAndView("auth_page");
setAccountInfo(mav, httpRequest);
try {
mav.addObject("userInfo", getUserInfoFromGraph(result.accessToken()));
return mav;
} catch (Exception e) {
mav = new ModelAndView("error");
mav.addObject("error", e);
}
}
return mav;
}
// Code omitted here
在Node.js 範例中,取得權杖的程式碼位於 AuthProvider
類別的 acquireToken
方法中。
acquireToken(options = {}) {
return async (req, res, next) => {
try {
const msalInstance = this.getMsalInstance(this.msalConfig);
/**
* If a token cache exists in the session, deserialize it and set it as the
* cache for the new MSAL CCA instance. For more, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/caching.md
*/
if (req.session.tokenCache) {
msalInstance.getTokenCache().deserialize(req.session.tokenCache);
}
const tokenResponse = await msalInstance.acquireTokenSilent({
account: req.session.account,
scopes: options.scopes || [],
});
/**
* On successful token acquisition, write the updated token
* cache back to the session. For more, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/caching.md
*/
req.session.tokenCache = msalInstance.getTokenCache().serialize();
req.session.accessToken = tokenResponse.accessToken;
req.session.idToken = tokenResponse.idToken;
req.session.account = tokenResponse.account;
res.redirect(options.successRedirect);
} catch (error) {
if (error instanceof msal.InteractionRequiredAuthError) {
return this.login({
scopes: options.scopes || [],
redirectUri: options.redirectUri,
successRedirect: options.successRedirect || '/',
})(req, res, next);
}
next(error);
}
};
}
接著,此存取權杖會用來處理對 /profile
端點的要求:
router.get('/profile',
isAuthenticated, // check if user is authenticated
async function (req, res, next) {
try {
const graphResponse = await fetch(GRAPH_ME_ENDPOINT, req.session.accessToken);
res.render('profile', { profile: graphResponse });
} catch (error) {
next(error);
}
}
);
在 Python 範例中,呼叫 API 的程式碼位於 app.py 中。
程式碼會嘗試從權杖快取中取得權杖。 如果無法取得權杖,則會將使用者重新導向至登入路由。 否則,可繼續呼叫 API。
@app.route("/call_downstream_api")
def call_downstream_api():
token = auth.get_token_for_user(app_config.SCOPE)
if "error" in token:
return redirect(url_for("login"))
# Use access token to call downstream api
api_result = requests.get(
app_config.ENDPOINT,
headers={'Authorization': 'Bearer ' + token['access_token']},
timeout=30,
).json()
return render_template('display.html', result=api_result)