Utworzono obiekt aplikacji klienckiej. Teraz użyjesz go do uzyskania tokenu w celu wywołania API internetowego. W ASP.NET lub ASP.NET Core wywoływanie internetowego interfejsu API odbywa się w kontrolerze:
Microsoft.Identity.Web dodaje metody rozszerzenia, które zapewniają wygodne usługi do wywoływania programu Microsoft Graph lub podrzędnego internetowego interfejsu API. Te metody zostały szczegółowo wyjaśnione w aplikacji webowej, która wywołuje interfejsy API: Wywołanie API. Dzięki tym metodom pomocniczym nie trzeba ręcznie uzyskiwać tokenu.
Jeśli jednak chcesz ręcznie uzyskać token, poniższy kod przedstawia przykład użycia pliku Microsoft.Identity.Web do tego w kontrolerze macierzystym. Wywołuje program Microsoft Graph przy użyciu interfejsu API REST (zamiast zestawu Microsoft Graph SDK). Zazwyczaj nie musisz pobierać tokenu, musisz utworzyć nagłówek autoryzacji, który dodasz do swojego żądania. Aby uzyskać nagłówek autoryzacji, wstrzyknij usługę IAuthorizationHeaderProvider
przez wstrzykiwanie zależności w konstruktorze kontrolera (lub w konstruktorze strony, jeśli używasz Blazor), a następnie używaj jej w akcjach kontrolera. Ten interfejs zawiera metody, które tworzą ciąg zawierający protokół (Bearer, Pop, ...) i token. Aby uzyskać nagłówek autoryzacyjny w celu wywołania API w imieniu użytkownika, użyj (CreateAuthorizationHeaderForUserAsync
). Aby uzyskać nagłówek autoryzacji w celu wywołania podrzędnego interfejsu API w imieniu samej aplikacji, w scenariuszu demona użyj (CreateAuthorizationHeaderForAppAsync
).
Metody kontrolera są chronione przez [Authorize]
atrybut, który gwarantuje, że tylko uwierzytelnieni użytkownicy mogą używać aplikacji internetowej.
[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 udostępnia IAuthorizationHeaderProvider
przez wstrzykiwanie zależności.
Oto uproszczony kod działania HomeController
, który pobiera token do wywołania 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);
}
Aby lepiej zrozumieć kod wymagany w tym scenariuszu, sprawdź krok 2 (2-1-Web app Calls Microsoft Graph) w samouczku ms-identity-aspnetcore-webapp-tutorial.
Atrybut AuthorizeForScopes
na górze akcji kontrolera (lub strony Razor, jeśli używasz szablonu Razor) jest udostępniany przez Microsoft.Identity.Web. Gwarantuje to, że użytkownik zostanie poproszony o zgodę w razie potrzeby i przyrostowo.
Istnieją inne złożone odmiany, takie jak:
- Wywoływanie kilku interfejsów API.
- Przetwarzanie zgody przyrostowej i dostępu warunkowego.
Te zaawansowane kroki zostały omówione w rozdziale 3 samouczka 3-WebApp-multi-APIs .
Kod ASP.NET jest podobny do kodu pokazanego dla ASP.NET Core:
- Akcja kontrolera, której ochronę zapewnia atrybut
[Authorize]
, wyodrębnia identyfikator najemcy oraz identyfikator użytkownika z członka kontrolera ClaimsPrincipal
(ASP.NET używa HttpContext.User
). Dzięki temu tylko uwierzytelnieni użytkownicy mogą korzystać z aplikacji.
Microsoft.Identity.Web dodaje metody rozszerzenia do kontrolera, które zapewniają wygodne usługi do wywoływania Microsoft Graph lub podrzędnego interfejsu API, uzyskiwania nagłówka autoryzacji, czy też tokenu. Metody używane do bezpośredniego wywoływania interfejsu API zostały szczegółowo wyjaśnione w artykule Aplikacja internetowa, która wywołuje internetowe interfejsy API: Wywoływanie interfejsu API. Korzystając z tych metod pomocniczych, nie trzeba ręcznie uzyskiwać tokenu.
Jeśli jednak chcesz ręcznie uzyskać token lub skompilować nagłówek autoryzacji, poniższy kod pokazuje, jak używać microsoft.Identity.Web, aby to zrobić w kontrolerze. Wywołuje interfejs API (Microsoft Graph) przy użyciu interfejsu API REST zamiast pakietu Microsoft Graph SDK.
Aby uzyskać nagłówek autoryzacji, należy pobrać usługę IAuthorizationHeaderProvider
z kontrolera, używając metody rozszerzenia GetAuthorizationHeaderProvider
. Aby otrzymać nagłówek autoryzacyjny do wywołania interfejsu API w imieniu użytkownika, użyj CreateAuthorizationHeaderForUserAsync
. Aby uzyskać nagłówek autoryzacji w celu wywołania podrzędnego interfejsu API w imieniu samej aplikacji, w scenariuszu demona użyj polecenia CreateAuthorizationHeaderForAppAsync
.
Poniższy fragment przedstawia działanie HomeController
, które pobiera nagłówek autoryzacji do wywołania Microsoft Graph jako API REST.
[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);
}
}
Poniższy fragment kodu przedstawia akcję HomeController
, która pobiera token dostępu w celu wywołania Microsoft Graph jako 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);
}
}
W przykładzie języka Java kod wywołujący interfejs API znajduje się w getUsersFromGraph
metodzie w pliku AuthPageController.java#L62.
Metoda próbuje wywołać getAuthResultBySilentFlow
. Jeśli użytkownik musi wyrazić zgodę na więcej uprawnień, kod przetwarza MsalInteractionRequiredException
obiekt w celu zażądania zgody od użytkownika.
@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
W przykładzie Node.js kod, który uzyskuje token, jest w acquireToken
metodzie AuthProvider
klasy .
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);
}
};
}
Ten token dostępu jest następnie używany do obsługi żądań do punktu końcowego /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);
}
}
);
W przykładzie języka Python kod wywołujący interfejs API znajduje się w app.py.
Kod próbuje uzyskać token z pamięci podręcznej na tokeny. Jeśli nie może uzyskać tokenu, przekierowuje użytkownika do trasy logowania. W przeciwnym razie może przejść do wywołania interfejsu 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)