Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Dotyczy: Dzierżawcy siły roboczej
Dzierżawcy zewnętrzni (dowiedz się więcej)
Jest to trzeci samouczek z serii samouczków, który przeprowadzi Cię przez proces logowania użytkowników przy użyciu identyfikatora Entra firmy Microsoft.
Przed rozpoczęciem użyj selektora Wybierz typ najemcy znajdującego się w górnej części tej strony, aby wybrać typ najemcy. Microsoft Entra ID oferuje dwie konfiguracje dzierżawy: pracowników i zewnętrzna. Konfiguracja dzierżawy zasobów pracowniczych obejmuje pracowników, aplikacje wewnętrzne i inne zasoby organizacyjne. Zewnętrzny dzierżawca jest przeznaczony dla aplikacji skierowanych do klientów.
W tym samouczku:
- Zaloguj się użytkownik.
- Wyloguj użytkownika.
- Tworzenie interfejsu użytkownika aplikacji
Warunki wstępne
Logowanie użytkownika
Istnieją dwie główne opcje logowania użytkowników przy użyciu biblioteki Microsoft Authentication Library (MSAL) dla systemu iOS: uzyskiwanie tokenów interaktywnie lub dyskretnie.
Aby interaktywnie zalogować się użytkownika, użyj następującego kodu:
func acquireTokenInteractively() { guard let applicationContext = self.applicationContext else { return } guard let webViewParameters = self.webViewParameters else { return } // #1 let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters) parameters.promptType = .selectAccount // #2 applicationContext.acquireToken(with: parameters) { (result, error) in // #3 if let error = error { self.updateLogging(text: "Could not acquire token: \(error)") return } guard let result = result else { self.updateLogging(text: "Could not acquire token: No result returned") return } // #4 self.accessToken = result.accessToken self.updateLogging(text: "Access token is \(self.accessToken)") self.updateCurrentAccount(account: result.account) self.getContentWithToken() } }
Właściwość
promptType
elementuMSALInteractiveTokenParameters
konfiguruje zachowanie uwierzytelniania i monitu o wyrażenie zgody. Obsługiwane są następujące wartości:-
.promptIfNecessary
(wartość domyślna) — użytkownik jest monitowany tylko w razie potrzeby. Doświadczenie logowania jednokrotnego (SSO) jest określane przez obecność plików cookie w interfejsie przeglądarki oraz rodzaj konta. Jeśli wielu użytkowników jest zalogowanych, zostanie wyświetlony proces wyboru konta. Jest to zachowanie domyślne. -
.selectAccount
— jeśli żaden użytkownik nie zostanie określony, w widoku internetowym uwierzytelniania zostanie wyświetlona lista aktualnie zalogowanych kont dla użytkownika do wyboru. -
.login
— wymaga uwierzytelnienia użytkownika w widoku internetowym. Tylko jedno konto może być zalogowane naraz, jeśli określisz tę wartość. -
.consent
— wymaga od użytkownika wyrażenia zgody na bieżący zestaw zakresów żądania.
-
Aby zalogować użytkownika w trybie dyskretnym, użyj następującego kodu:
func acquireTokenSilently(_ account : MSALAccount!) { guard let applicationContext = self.applicationContext else { return } /** Acquire a token for an existing account silently - forScopes: Permissions you want included in the access token received in the result in the completionBlock. Not all scopes are guaranteed to be included in the access token returned. - account: An account object that we retrieved from the application object before that the authentication flow will be locked down to. - completionBlock: The completion block that will be called when the authentication flow completes, or encounters an error. */ let parameters = MSALSilentTokenParameters(scopes: kScopes, account: account) applicationContext.acquireTokenSilent(with: parameters) { (result, error) in if let error = error { let nsError = error as NSError // interactionRequired means we need to ask the user to sign-in. This usually happens // when the user's Refresh Token is expired or if the user has changed their password // among other possible reasons. if (nsError.domain == MSALErrorDomain) { if (nsError.code == MSALError.interactionRequired.rawValue) { DispatchQueue.main.async { self.acquireTokenInteractively() } return } } self.updateLogging(text: "Could not acquire token silently: \(error)") return } guard let result = result else { self.updateLogging(text: "Could not acquire token: No result returned") return } self.accessToken = result.accessToken self.updateLogging(text: "Refreshed Access token is \(self.accessToken)") self.updateSignOutButton(enabled: true) self.getContentWithToken() } }
Metoda
acquireTokenSilently
próbuje w sposób dyskretny uzyskać token dostępu dla istniejącego konta MSAL. UżywaapplicationContext
do żądania tokenu z określonymi zakresami. Jeśli wystąpi błąd, sprawdza, czy jest wymagana interakcja użytkownika, a jeśli tak, inicjuje pozyskiwanie tokenu interakcyjnego. Po powodzeniu aktualizuje token dostępu, rejestruje wynik, włącza przycisk wylogowywanie i pobiera zawartość przy użyciu tokenu.
Obsługa wywołania zwrotnego logowania (tylko system iOS)
Otwórz plik AppDelegate.swift . Aby obsłużyć wywołanie zwrotne po zalogowaniu, dodaj MSALPublicClientApplication.handleMSALResponse
do klasy appDelegate
w następujący sposób:
// Inside AppDelegate...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String)
}
Jeśli używasz środowiska Xcode 11, należy umieścić wywołanie zwrotne biblioteki MSAL w pliku SceneDelegate.swift . Jeśli dla zgodności ze starszymi wersjami iOS obsługiwane są zarówno UISceneDelegate, jak i UIApplicationDelegate, wywołanie zwrotne MSAL musi zostać umieszczone w obu plikach.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let urlContext = URLContexts.first else {
return
}
let url = urlContext.url
let sourceApp = urlContext.options.sourceApplication
MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: sourceApp)
}
Wyloguj użytkownika
Ważny
Wylogowywanie przy użyciu biblioteki MSAL usuwa wszystkie znane informacje o użytkowniku z aplikacji, a także usuwa aktywną sesję na urządzeniu, jeśli jest to dozwolone przez konfigurację urządzenia. Możesz również opcjonalnie wylogować użytkownika z przeglądarki.
Aby dodać możliwość wylogowywania, dodaj następujący kod w klasie ViewController
.
@objc func signOut(_ sender: AnyObject) {
guard let applicationContext = self.applicationContext else { return }
guard let account = self.currentAccount else { return }
do {
/**
Removes all tokens from the cache for this application for the provided account
- account: The account to remove from the cache
*/
let signoutParameters = MSALSignoutParameters(webviewParameters: self.webViewParameters!)
signoutParameters.signoutFromBrowser = false // set this to true if you also want to signout from browser or webview
applicationContext.signout(with: account, signoutParameters: signoutParameters, completionBlock: {(success, error) in
if let error = error {
self.updateLogging(text: "Couldn't sign out account with error: \(error)")
return
}
self.updateLogging(text: "Sign out completed successfully")
self.accessToken = ""
self.updateCurrentAccount(account: nil)
})
}
}
Tworzenie interfejsu użytkownika aplikacji
Teraz utwórz interfejs użytkownika zawierający przycisk wywołujący interfejs API programu Microsoft Graph, inny do wylogowania i widok tekstowy, aby wyświetlić dane wyjściowe, dodając następujący kod do klasy ViewController
:
Interfejs użytkownika systemu iOS
var loggingText: UITextView!
var signOutButton: UIButton!
var callGraphButton: UIButton!
var usernameLabel: UILabel!
func initUI() {
usernameLabel = UILabel()
usernameLabel.translatesAutoresizingMaskIntoConstraints = false
usernameLabel.text = ""
usernameLabel.textColor = .darkGray
usernameLabel.textAlignment = .right
self.view.addSubview(usernameLabel)
usernameLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
usernameLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10.0).isActive = true
usernameLabel.widthAnchor.constraint(equalToConstant: 300.0).isActive = true
usernameLabel.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
// Add call Graph button
callGraphButton = UIButton()
callGraphButton.translatesAutoresizingMaskIntoConstraints = false
callGraphButton.setTitle("Call Microsoft Graph API", for: .normal)
callGraphButton.setTitleColor(.blue, for: .normal)
callGraphButton.addTarget(self, action: #selector(callGraphAPI(_:)), for: .touchUpInside)
self.view.addSubview(callGraphButton)
callGraphButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
callGraphButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 120.0).isActive = true
callGraphButton.widthAnchor.constraint(equalToConstant: 300.0).isActive = true
callGraphButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
// Add sign out button
signOutButton = UIButton()
signOutButton.translatesAutoresizingMaskIntoConstraints = false
signOutButton.setTitle("Sign Out", for: .normal)
signOutButton.setTitleColor(.blue, for: .normal)
signOutButton.setTitleColor(.gray, for: .disabled)
signOutButton.addTarget(self, action: #selector(signOut(_:)), for: .touchUpInside)
self.view.addSubview(signOutButton)
signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
signOutButton.topAnchor.constraint(equalTo: callGraphButton.bottomAnchor, constant: 10.0).isActive = true
signOutButton.widthAnchor.constraint(equalToConstant: 150.0).isActive = true
signOutButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
let deviceModeButton = UIButton()
deviceModeButton.translatesAutoresizingMaskIntoConstraints = false
deviceModeButton.setTitle("Get device info", for: .normal);
deviceModeButton.setTitleColor(.blue, for: .normal);
deviceModeButton.addTarget(self, action: #selector(getDeviceMode(_:)), for: .touchUpInside)
self.view.addSubview(deviceModeButton)
deviceModeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
deviceModeButton.topAnchor.constraint(equalTo: signOutButton.bottomAnchor, constant: 10.0).isActive = true
deviceModeButton.widthAnchor.constraint(equalToConstant: 150.0).isActive = true
deviceModeButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
// Add logging textfield
loggingText = UITextView()
loggingText.isUserInteractionEnabled = false
loggingText.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(loggingText)
loggingText.topAnchor.constraint(equalTo: deviceModeButton.bottomAnchor, constant: 10.0).isActive = true
loggingText.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10.0).isActive = true
loggingText.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -10.0).isActive = true
loggingText.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 10.0).isActive = true
}
func platformViewDidLoadSetup() {
NotificationCenter.default.addObserver(self,
selector: #selector(appCameToForeGround(notification:)),
name: UIApplication.willEnterForegroundNotification,
object: nil)
}
@objc func appCameToForeGround(notification: Notification) {
self.loadCurrentAccount()
}
Interfejs użytkownika systemu macOS
var callGraphButton: NSButton!
var loggingText: NSTextView!
var signOutButton: NSButton!
var usernameLabel: NSTextField!
func initUI() {
usernameLabel = NSTextField()
usernameLabel.translatesAutoresizingMaskIntoConstraints = false
usernameLabel.stringValue = ""
usernameLabel.isEditable = false
usernameLabel.isBezeled = false
self.view.addSubview(usernameLabel)
usernameLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 30.0).isActive = true
usernameLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10.0).isActive = true
// Add call Graph button
callGraphButton = NSButton()
callGraphButton.translatesAutoresizingMaskIntoConstraints = false
callGraphButton.title = "Call Microsoft Graph API"
callGraphButton.target = self
callGraphButton.action = #selector(callGraphAPI(_:))
callGraphButton.bezelStyle = .rounded
self.view.addSubview(callGraphButton)
callGraphButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
callGraphButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
callGraphButton.heightAnchor.constraint(equalToConstant: 34.0).isActive = true
// Add sign out button
signOutButton = NSButton()
signOutButton.translatesAutoresizingMaskIntoConstraints = false
signOutButton.title = "Sign Out"
signOutButton.target = self
signOutButton.action = #selector(signOut(_:))
signOutButton.bezelStyle = .texturedRounded
self.view.addSubview(signOutButton)
signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
signOutButton.topAnchor.constraint(equalTo: callGraphButton.bottomAnchor, constant: 10.0).isActive = true
signOutButton.heightAnchor.constraint(equalToConstant: 34.0).isActive = true
signOutButton.isEnabled = false
// Add logging textfield
loggingText = NSTextView()
loggingText.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(loggingText)
loggingText.topAnchor.constraint(equalTo: signOutButton.bottomAnchor, constant: 10.0).isActive = true
loggingText.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10.0).isActive = true
loggingText.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -10.0).isActive = true
loggingText.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -10.0).isActive = true
loggingText.widthAnchor.constraint(equalToConstant: 500.0).isActive = true
loggingText.heightAnchor.constraint(equalToConstant: 300.0).isActive = true
}
func platformViewDidLoadSetup() {}
Następnie w klasie ViewController
zastąp metodę viewDidLoad()
:
override func viewDidLoad() {
super.viewDidLoad()
initUI()
do {
try self.initMSAL()
} catch let error {
self.updateLogging(text: "Unable to create Application Context \(error)")
}
self.loadCurrentAccount()
self.platformViewDidLoadSetup()
}
Następne kroki
Jest to trzeci samouczek z serii samouczków, który przeprowadzi Cię przez proces logowania użytkowników przy użyciu identyfikatora Entra firmy Microsoft.
Przed rozpoczęciem użyj selektora Wybierz typ najemcy znajdującego się w górnej części tej strony, aby wybrać typ najemcy. Microsoft Entra ID oferuje dwie konfiguracje dzierżawy: pracowników i zewnętrzna. Konfiguracja dzierżawy zasobów pracowniczych obejmuje pracowników, aplikacje wewnętrzne i inne zasoby organizacyjne. Zewnętrzny dzierżawca jest przeznaczony dla aplikacji skierowanych do klientów.
W tym samouczku:
- Zaloguj się użytkownik.
- Wyloguj użytkownika.
Warunki wstępne
Logowanie użytkownika
Istnieją dwie główne opcje logowania użytkowników przy użyciu biblioteki Microsoft Authentication Library (MSAL) dla systemu iOS: uzyskiwanie tokenów interaktywnie lub dyskretnie.
Aby interaktywnie zalogować się użytkownika, użyj następującego kodu:
acquireTokenInteractively() { guard let applicationContext = self.applicationContext else { return } guard let webViewParameters = self.webViewParameters else { return } updateLogging(text: "Acquiring token interactively...") let parameters = MSALInteractiveTokenParameters(scopes: Configuration.kScopes, webviewParameters: webViewParameters) parameters.promptType = .selectAccount applicationContext.acquireToken(with: parameters) { (result, error) in if let error = error { self.updateLogging(text: "Could not acquire token: \(error)") return } guard let result = result else { self.updateLogging(text: "Could not acquire token: No result returned") return } self.accessToken = result.accessToken self.updateLogging(text: "Access token is \(self.accessToken)") self.updateCurrentAccount(account: result.account) } }
Kod najpierw sprawdza, czy kontekst aplikacji i parametry widoku internetowego są dostępne. Następnie aktualizuje logowanie, aby wskazać, że token jest uzyskiwany interaktywnie. Następnie konfiguruje parametry pozyskiwania tokenu interakcyjnego, określając zakresy i parametry widoku internetowego. Ustawia również rodzaj monitu na wybór konta.
Następnie wywołuje metodę
acquireToken
w kontekście aplikacji ze zdefiniowanymi parametrami. W funkcji obsługi zakończenia sprawdza się wszelkie błędy. Jeśli wystąpi błąd, zaktualizuje on rejestrowanie za pomocą komunikatu o błędzie. W przypadku powodzenia pobiera token dostępu z wyniku, aktualizuje rejestrowanie przy użyciu tokenu i aktualizuje bieżące konto.Gdy aplikacja uzyska token dostępu, możesz pobrać oświadczenia skojarzone z bieżącym kontem. W tym celu użyj następującego fragmentu kodu:
let claims = result.account.accountClaims let preferredUsername = claims?["preferred_username"] as? String
Kod odczytuje oświadczenia z konta, korzystając z właściwości
accountClaims
obiekturesult.account
. Następnie pobiera wartość oświadczenia "preferred_username" ze słownika oświadczeń i przypisuje ją do zmiennejpreferredUsername
.Aby zalogować użytkownika w trybie dyskretnym, użyj następującego kodu:
func acquireTokenSilently() { self.loadCurrentAccount { (account) in guard let currentAccount = account else { self.updateLogging(text: "No token found, try to acquire a token interactively first") return } self.acquireTokenSilently(currentAccount) } }
Kod inicjuje proces uzyskiwania tokenów w trybie dyskretnym. Najpierw próbuje załadować bieżące konto. Jeśli zostanie znalezione bieżące konto, przejdzie do uzyskania tokenu w trybie dyskretnym przy użyciu tego konta. Jeśli nie znaleziono bieżącego konta, system zaktualizuje logowanie, aby wskazać, że nie znaleziono tokenu, i sugeruje, aby spróbować zdobyć token w sposób interaktywny najpierw.
W powyższym kodzie wywołujemy dwie funkcje,
loadCurrentAccount
iacquireTokenSilently
. FunkcjaloadCurrentAccount
powinna mieć następujący kod:func loadCurrentAccount(completion: AccountCompletion? = nil) { guard let applicationContext = self.applicationContext else { return } let msalParameters = MSALParameters() msalParameters.completionBlockQueue = DispatchQueue.main // Note that this sample showcases an app that signs in a single account at a time applicationContext.getCurrentAccount(with: msalParameters, completionBlock: { (currentAccount, previousAccount, error) in if let error = error { self.updateLogging(text: "Couldn't query current account with error: \(error)") return } if let currentAccount = currentAccount { self.updateCurrentAccount(account: currentAccount) self.acquireTokenSilently(currentAccount) if let completion = completion { completion(self.currentAccount) } return } // If testing with Microsoft's shared device mode, see the account that has been signed out from another app. More details here: // https://docs.microsoft.com/azure/active-directory/develop/msal-ios-shared-devices if let previousAccount = previousAccount { self.updateLogging(text: "The account with username \(String(describing: previousAccount.username)) has been signed out.") } else { self.updateLogging(text: "") } self.accessToken = "" self.updateCurrentAccount(account: nil) if let completion = completion { completion(nil) } }) }
Kod używa biblioteki MSAL dla systemu iOS do załadowania bieżącego konta. Sprawdza błędy i odpowiednio aktualizuje rejestrowanie. Jeśli zostanie znalezione bieżące konto, aktualizuje je i próbuje uzyskać tokeny w trybie dyskretnym. Jeśli istnieje poprzednie konto, rejestruje wylogowanie. Jeśli nie znaleziono żadnych kont, czyści token dostępu. Na koniec wykonuje blok ukończenia, jeśli zostanie podany.
Funkcja
acquireTokenSilently
powinna zawierać następujący kod:func acquireTokenSilently(_ account : MSALAccount) { guard let applicationContext = self.applicationContext else { return } /** Acquire a token for an existing account silently - forScopes: Permissions you want included in the access token received in the result in the completionBlock. Not all scopes are guaranteed to be included in the access token returned. - account: An account object that we retrieved from the application object before that the authentication flow will be locked down to. - completionBlock: The completion block that will be called when the authentication flow completes, or encounters an error. */ updateLogging(text: "Acquiring token silently...") let parameters = MSALSilentTokenParameters(scopes: Configuration.kScopes, account: account) applicationContext.acquireTokenSilent(with: parameters) { (result, error) in if let error = error { let nsError = error as NSError // interactionRequired means we need to ask the user to sign-in. This usually happens // when the user's Refresh Token is expired or if the user has changed their password // among other possible reasons. if (nsError.domain == MSALErrorDomain) { if (nsError.code == MSALError.interactionRequired.rawValue) { DispatchQueue.main.async { self.acquireTokenInteractively() } return } } self.updateLogging(text: "Could not acquire token silently: \(error)") return } guard let result = result else { self.updateLogging(text: "Could not acquire token: No result returned") return } self.accessToken = result.accessToken self.updateLogging(text: "Refreshed Access token is \(self.accessToken)") self.updateSignOutButton(enabled: true) } }
Ta funkcja używa biblioteki MSAL dla systemu iOS do dyskretnego uzyskiwania tokenu dla istniejącego konta. Po zweryfikowaniu
applicationContext
loguje proces pozyskiwania tokenu. Przy użyciuMSALSilentTokenParameters
definiuje wymagane parametry. Następnie próbuje uzyskać token w trybie dyskretnym. Jeśli wystąpią błędy, sprawdza wymagania dotyczące interakcji z użytkownikiem, inicjując w razie potrzeby interaktywny proces. Po pomyślnym zakończeniu zaktualizuje właściwośćaccessToken
i zapisze odświeżony token, kończąc przez włączenie przycisku wylogowania.
Wyloguj użytkownika
Aby wylogować użytkownika z aplikacji systemu iOS (Swift) przy użyciu biblioteki MSAL dla systemu iOS, użyj następującego kodu:
@IBAction func signOut(_ sender: UIButton) {
guard let applicationContext = self.applicationContext else { return }
guard let account = self.currentAccount else { return }
guard let webViewParameters = self.webViewParameters else { return }
updateLogging(text: "Signing out...")
do {
/**
Removes all tokens from the cache for this application for the provided account
- account: The account to remove from the cache
*/
let signoutParameters = MSALSignoutParameters(webviewParameters: webViewParameters)
// If testing with Microsoft's shared device mode, trigger signout from browser. More details here:
// https://docs.microsoft.com/azure/active-directory/develop/msal-ios-shared-devices
if (self.currentDeviceMode == .shared) {
signoutParameters.signoutFromBrowser = true
} else {
signoutParameters.signoutFromBrowser = false
}
applicationContext.signout(with: account, signoutParameters: signoutParameters, completionBlock: {(success, error) in
if let error = error {
self.updateLogging(text: "Couldn't sign out account with error: \(error)")
return
}
self.updateLogging(text: "Sign out completed successfully")
self.accessToken = ""
self.updateCurrentAccount(account: nil)
})
}
}
Kod weryfikuje istnienie applicationContext
, currentAccount
i webViewParameters
. Następnie rejestruje proces wylogowywania. Kod usuwa wszystkie tokeny z pamięci podręcznej dla podanego konta. W zależności od bieżącego trybu urządzenia określa, czy wylogować się z przeglądarki. Po zakończeniu aktualizuje odpowiednio tekst rejestrowania. Jeśli podczas procesu wylogowania wystąpi błąd, rejestruje komunikat o błędzie. Po pomyślnym wylogowaniu zmienia token dostępu na pusty ciąg znaków i usuwa dane bieżącego konta.