WKWebViewで認証されたページにアクセスしたいときは、確認証方法に合わせた処理が必要です。この記事ではHTTPのBasic認証に対応する方法を解説します。
全体的な流れ
WKWebViewでBasic認証に対応するには、次のような流れを実装します。
- 認証エラーになったら、認証方式を判定する。
- アカウント情報を持っていたら、アカウント情報をヘッダに追加してアクセスする。
- アカウント情報を持っていなかったら、認証画面を表示する。
- アカウント情報が入力されたら、アカウント情報をヘッダに追加してアクセスする。
認証が必要なページにアクセスするとどうなる?
認証に対応するために必要な処理を何も実装せずに、認証が必要なページにアクセスすると、エラーページが表示されます。SafariやChromeのような認証ダイアログは自動的には表示されません。
これらの処理はアプリ側で実装する必要があります。
まずは、次の2つを実装します。
- 認証エラーになったことを判定する。
- 認証方式がBasic認証かどうかを判定する。
認証エラーになったことの判定
認証エラーになり、認証が必要になると、WKNavigationDelegate
の次のメソッドが呼ばれます。
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
WKNavigationDelegate
プロトコルに適合したクラスで実装し、WKWebView
クラスのnavigationDelegate
プロトコルにセットします。
必要な処理を実行して、最後にcompletionHandler
を実行します。
認証方式の判定
認証方式を判定するには、URLAuthenticationChallenge
のprotectionSpace
プロパティを取得して、URLProtectionSpace
のauthenticationMethod
プロパティをチェックします。
次のコードは、Basic認証かどうかを判定しています。
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
var isBasicAuthentication = false
let method = challenge.protectionSpace.authenticationMethod
if method.compare(NSURLAuthenticationMethodHTTPBasic) == .orderedSame {
isBasicAuthentication = true
}
completionHandler(.performDefaultHandling, nil)
}
Basic認証の場合は、authenticationMethod
の値はNSURLAuthenticationMethodHTTPBasic
になっています。
認証画面の表示
Basic認証だったら、認証画面を表示し、それ以外だったらデフォルトの処理を行うように実装します。
認証画面でログイン、または、キャンセルボタンが押されたら、webView(_:didReceive:completionHandler:)
で取得したcompletionHandler
を実行します。
次のコードは、UIAlertController
でログイン画面を作っているサンプルコードです。とりあえず、表示するというコードなので、ボタンの処理は特に行っていません。
func showBasicAuthAlert(completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let alert = UIAlertController(title: "Authentication",
message: "Enter your account",
preferredStyle: .alert)
// ユーザー名のテキストフィールドを作成する
alert.addTextField { (textField) in
textField.placeholder = "User Name"
textField.keyboardType = .asciiCapable
}
// パスワードのテキストフィールドを作成する
alert.addTextField { (textField) in
textField.placeholder = "Password"
textField.keyboardType = .asciiCapable
textField.isSecureTextEntry = true
}
// ログインボタンを作成する
alert.addAction(UIAlertAction(title: "Login", style: .default, handler: { (action) in
completionHandler(.performDefaultHandling, nil)
}))
// キャンセルボタンを作成する
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) in
completionHandler(.performDefaultHandling, nil)
}))
// アラートを表示する
self.present(alert, animated: true, completion: nil)
}
キャンセルボタンの処理
キャンセルボタンが押されたときの処理を実装します。とはいっても、既に実装済みのコードです。
completionHandler
の引数に.performDefaultHandling
を指定して実行します。cancelAuthenticationChallenge
も定義されていて、そちらを使った方が良いと最初は思ったのですが、試してみると、cancelAuthenticationChallenage
は処理の中断でした。
SafariやChromeの動作を見てみると、Basic認証でエラーになったときは、エラーページを表示するようになっています。しかし、cancelAuthenticationChallenge
を指定してしまうと、エラーページの表示処理も行わずに中止してしまいました。
.performDefaultHandling
を指定すると、エラーページの表示に遷移するので、WKWebView
と組み合わせるときは、.performDefaultHandling
の方が良いと思います。
但し、エラーになったら、WKWebView
自体を非表示にして、別の画面に遷移するような仕様のときは、cancelAuthenticationChallenge
の方が良いでしょう。
ログイン処理
ログイン処理は、次の処理を実装します。
- 入力されたユーザー名とパスワードを取得する
- 取得したユーザー名とパスワードから認証情報を作成する
- 作成した認証情報を使って認証を行う
コードにすると次のようになります。
// ログインボタンを作成する
alert.addAction(UIAlertAction(title: "Login", style: .default, handler: { (action) in
var userName = ""
var password = ""
// 入力された文字列を取得する
if let textFields = alert.textFields {
if let str = textFields[0].text {
userName = str
}
if let str = textFields[1].text {
password = str
}
}
// このセッション専用の認証情報を作成する
let credential = URLCredential(user: userName, password: password, persistence: .forSession)
// 作成した認証情報で認証を行う
completionHandler(.useCredential, credential)
}))
認証情報は、URLCredential
のインスタンスです。イニシャライザでユーザー名とパスワードを指定できます。最後のpersistence
は認証情報をどのように保持するかを指定します。
次の値が定義されています。
- none : 保持しない
- forSession : セッション中だけ保持する
- permanent : キーチェインに保存する
- synchronizable : キーチェインに保存し、Apple IDに紐付けて同期可能にする(iCloud経由で同期可能という意味だと思います)
認証情報を作成したら、useCredential
を指定して completionHandler
を実行します。
認証に成功すると、ページにアクセスできます。