Apple 文档 Authentication and Credentials 的翻译
Handling an Authentication Challenge
Respond appropriately when a server demands authentication for a URL request.
当服务器要求认证时,需要适当的进行处理。
Overview
When your app makes a request with a NSURLSession Task,the server may respond with one or more demands for credentials before continuing. The session task attempts to handle this for you. IF it can’t ,it calls your session’s delegate to handle the challenges.
当我们的 app 用 NSURLSession Task 发起一次请求之前,服务器可能返回需要认证的response,session task 会自动对其进行处理,假如处理不了,会回调NSURLSession 的 delegate ,让 delegate 进行处理。
Implement the delegate methods described in this article to answer challengs issues by a server that your app connects to. If you don’t implement a delegate, your request may be denied by the server, and you receive a response with HTTP status code 401(Forbidden) instead of the data you expect.
为了响应服务器对认证的需求,你必须在 app 中实现本文中提到的代理方法,否则请求将会被服务器拒绝,从而收到 401 错误。
Determine the Appropriate Delegate Method
在代理方法进行合适的处理。
Implement one or both delegate authentication methods,depending on the nature of the challenge(s) you receive.
根据你的需求,实现其中以下一个或多个方法。
Implement the
URLSession:didReceiveChallenge:completionHandler:
method ofNSURLSessionDelegate
to handle session-wide challenges. These ars challengs like Transport Layer Security(TLS) validation. Once you’ve successfully handled this kind of challenge, your action remains in effect for all tasks created from that NSURLSession.处理 session 范围的认证需求,例如 TLS 认证,你需要实现
NSURLSessionDelegate
代理中的URLSession:didReceiveChallenge:completionHandler:
方法。一旦你实现了这个方法成功处理了这类认证需求,所有NSURLSession
生成的task
都会应用这个方法进行处理。Implement the
URLSession:task:didReceiveChallenge:completionHandler:
method ofNSURLSessionTaskDelegate
to handle task-specific challenges. These are challenges like demands for username/password authentication. Each task created from a given session may issue its own challenges.为了处理指定
task
的认证需求,你需要实现NSURLSessionTaskDelegate
中的URLSession:task:didReceiveChallenge:completionHandler:
方法,这有点类似于关于 用户名/密码 的认证,每个task
可能会有自己的认证需求。
AS a simple example, consider what happens when you request an http URL protected by HTTP Basic authentication, as defined in RFC 7617. Because this is a task-specific challenge, you handle this by implementing URLSession:task:didReceiveChallenge:completionHandler:
举个简单的例子,当你发起一个需要进行 HTTP Basic authentication 的请求时,因为这属于一个 task
层面的认证需求,你需要实现URLSession:task:didReceiveChallenge:completionHandler:
方法。
Note
If you connect via https, you also receive a server trust challenge. See performing Manual Server Trust Authentication for information on handling this type of session-wide challenge.
如果你通过 https 进行网络连接,你也会收到认证回调,查看 Performing Manual Server Trust Authentication 文档对这一 session 级别的认证 进行详细了解
Determine the Type of Authentication Challenge
决定认证挑战的类型
When you receive an authentication challenge, use your delegate method to determine the type of challenge. The delegate method receives a NSURLAuthentication Challenge
instance that describes the challenge being issued. This instance contains a protectionSpace
property whose authenticationMethod
property indicates the kind of challenge being issued (such as a request for a username and password, or a client certificate). You use this value to determine whether you can handle the challenge.
当你收到一个认证需求时,根据回调方法,可以获取到认证的类型。代理方法会返回一个 NSURLAuthentication Challenge
对象,描述了这个认证需求。这个对象包含了一个 protectionSpace
属性,这个属性中又包含有一个authenticationMethod
属性,表明了这个需求的类型(比如用户名/密码,或者客户端证书)。你可以根据这个类型来判断是否回应这个认证需求。
You respond to the challenge by directly invoking the completion handler passed in to the challenge, passing an NSURLSessionAuthChallengeDisposition
indicating your response to the challenge. You use the disposition argument to provide a credential, cancel the request, or allow the default handling to proceed, whichever is appropriate.
通过调用方法回调的 completion block,可以对这次认证需求做出响应。在 completion block 中,传入不同的NSURLSessionAuthChallengeDisposition
参数,代表了不同含义,例如提供一个证书、取消这次请求或者进行默认操作,这取决于你。
Listing1 tests the authentication method to see if it is the expected type, HTTP Basic. If the authenticationMethod property indicates some other kind of challenge, it calls the completion handler with the NSURLSessionAuthChallengePerformDefaultHandling
disposition. Telling the task to use its default handling may satisfy the challenge; otherwise, the task will move on to the next challenge in the response and call this delegate again. This process continues until the task reaches the HTTP Basic challenge that you expect to handle.
下面的例1中,判断了这次认证需求的类型是否是 HTTP Basic,假如是其他的类型,使用默认的NSURLSessionAuthChallengePerformDefaultHandling
进行处理,task 会遍历所有的认证需求,不断的回调该方法,直到类型是 HTTP Basic 的时候才会进行下一步处理
Listing 1
Checking the authentication method of an authentication challenge
1 | let authMethod = challenge.protectionSpace.authenticationMethod |
Create a Credential Instance
To successfully answer the challenge, you need to submit a credential appropriate to type of challenge you have received. For HTTP Basic and HTTP Digest challenges, you provide a username and password. Listing 2 shows a helper method that attempts to create a NSURLCredential
instance from user-interface fields, if they are filled in.
为了成功的响应认证需求,你需要提交一个适合这次需求类型的证书。对于 HTTP Basic 和 HTTP Digest 认证需求来说,你需要提供一个 用户名和 密码。例2 展示了一个从用户界面的数据生成一个证书的例子。
*Listing 2
Creating a URLCredential from user interface values
*
1 | func credentialsFromUI() -> URLCredential? { |
In this example, the returned NSURLCredential has NSURLCredentialPersistenceForSession persistence, so it’s only stored by the NSURLSession instance that created the task. You would need to supply new NSURLCredential instances for tasks created by other session instances, and on future runs of the app.
在这个例子中,这个证书的生命周期是NSURLCredentialPersistenceForSession
,这代表了该证书只会作用于生成这个 task 的 session 中,对于其他 session 发起的 task ,你需要生成一个新的证书来应对。
Call the Completion Handler
Once you’ve tried to create a credential instance, you must call the completion handler to answer the challenge.
生成证书后,必须回调 completion block ,来响应这次 认证需求
If you can’t create a credential, or if the user explicitly canceled, call the completion handler and pass the
NSURLSessionAuthChallengeCancelAuthenticationChallenge
disposition.
假如无法生成证书,或者用户明确的手动取消了,你可以在 block 中传入NSURLSessionAuthChallengeCancelAuthenticationChallenge
参数。If you can create a credential instance, use the
NSURLSessionAuthChallengeUseCredential
disposition to pass it to the completion handler.
假如可以生成证书,completion block 中传入NSURLSessionAuthChallengeUseCredential
参数
Listing 3 shows both these options.
例三处理了这两种情况
Listing 3
Invoking the authentication challenge completion Handler
1 | guard let credential = credentialOrNil else { |
If you supply a credential that is accepted by the server, the task begins uploading or downloading data.
假如你传入的证书被服务器接受了,请求会继续进行。
Important
You can pass the completion handler to other methods or temporarily store it in a property, for situations like waiting for the user to complete a username/password dialog. But eventually you must call the completion handler to complete the challenge and allow the task to proceed, even if you’re choosing to cancel, as seen in the failure case of Listing 3.
在必要的时候,你可以将 completion block 传到其他方法中,甚至可以存储起来,比如等待用户填写用户名和密码的时候;但是为了让请求继续进行,你必须回调这个 completion block ,即使你选择取消这次请求的时候,你也需要像上面 例3 中那样进行处理。
Handle Failues Gracefully
If the credential is refused, the system calls your delegate method again. When this happens, the callback provides your rejected credential as the proposedCredential
property of the NSURLAuthenticationChallenge
parameter. The challenge instance also includes a previousFailureCount
property, which indicates how many times the credential has been rejected. You can use these properties to determine what to do next. For example, if the previousFailureCount
is greater than zero, you could use the user string of the proposedCredential
to populate a user/password reentry UI.
假如证书被服务器拒绝,系统会再次回调代理方法,并且将上次的证书作为 challenge 的 proposedCredential 参数返回。challenge 还有一个 previousFailureCount 的属性,代表了已经失败了多少次,你可以使用这些参数来决定下一步怎么做,例如,假如失败次数超过一次,你可以在 UI 上展示一个失败重新输入的提示。
Topics
Creating URL Credentials
Performing Manual Server Trust Authentication
Performing Manual Server Trust Authentication
Evaluate the server’s security credentials in your app.
在你的 app 中认证系统的安全证书。
Overview
When you use a secure connection (such as https) with a URL request, your NSURLSessionDelegate
receives an authentication challenge with an authentication type of NSURLAuthenticationMethodServerTrust
. Unlike other challenges where the server is asking your app to authenticate itself, this is an opportunity for you to authenticate the server’s credentials.
当你使用安全连接(例如 https)进行请求时,你设置的 NSURLSessionDelegate
会被回调,并传回一个认证需求类型为NSURLAuthenticationMethodServerTrest
的 chanllenge。其他的认证需求,服务器会要求你的 app 认证自身,这个认证需求,可以让你对服务器进行认证的。
Tip
See Handling an Authentication Challenge for an introduction to authentication challenges.
Determine When Manual Server Trust Evaluation Is Appropriate
何时对 Server Trust 进行手动处理
In most cases, you should let the URL Loading System’s default handling evaluate the server trust. You get this behavior when you either don’t have a delegate or don’t handle authentication challenges. However, performing your own evaluation may be useful for scenarios like the following:
大多数情况下,你不需要实现代理方法,系统会进行默认的处理,但是在下面这些场景下,最好进行自定义的认证处理:
You want to accept server credentials that would otherwise be rejected by the system. For example, your app makes a secure connection to a development server that uses a self-signed certificate, which would ordinarily not match anything in the system’s trust store.
有时候你必须接受系统证书,否则连接会失败。例如,你连接的服务器,使用的证书是自签的,通常情况下,这个服务器证书是没有预存到系统的新人列表中的,必须选择信任,否则连接会失败。You want to reject credentials that would otherwise be accepted by the system. For example, you want to “pin” your app to a set of specific keys or certificates under your control, rather than accept any valid credential.
你希望你的 app 只访问某些拥有特定的证书的服务器,其他的请求一概拒绝。
Figure 1 illustrates how an app performs manual credential evaluation by providing a delegate method to handle the authentication challenge. This bypasses the default handling. Instead, the delegate directly compares the server certificate or its public key against a copy of the certificate or key (or a hash of either of these) stored in the app bundle itself. If the delegate decides the server credential is valid, it accepts the server trust and allows the connection to continue.
图1 表示一个 app 进行认证需求的流程,系统通过回调代理方法,将 Server Trust 的公钥和证书交给代理,代理方法中,将公钥或者证书,跟存储在 app boundle 中的公钥和证书(或者 hash 表或者其他)进行对比,假如是一致的,那么代理方法通过回调 block ,同意接受服务器证书,然后继续请求。
Note
NSURLSession
enforcesApp Transport Security (ATS)
, if it is enabled for the domain you are connecting to. This applies security requirements for the certificates, TLS version, and cipher used by the connection. You cannot loosen server trust requirements for an ATS-protected domain, but you can tighten them, using the manual evaluation technique shown in this article. SeeNSAppTransportSecurity
inInformation Property List Key Reference
for further details.NSURLSession 会使用 ATS 中的设置,这表明对于一个连接请求,它的 证书、TLS 版本和加密算法都是有要求的,虽然你无法避过 server trust 的认证需求,但是你可以单独的处理这些其他要求,阅读文档 Information Property List Key Reference 中的NSAppTransportSecurity 章节,获取更详细的信息。
Handle Server Trust Authentication Challenges
怎样对 server trust 认证需求进行具体处理。
To perform manual server trust authentication, implement the NSURLSessionDelegate method URLSession:didReceiveChallenge:completionHandler:
. When this method is called, the first things your implementation needs to do are to check that:
为了手动处理 server trust 认证需求,你需要实现协议NSURLSessionDelegate
中的URLSession:didReceiveChallenge:completionHandler:
方法。当这个方法被回调的时候,你需要做以下检查:
- The challenge type is server trust, and not some other kind of challenge.
这个认证需求的类型是 server trust,而不是其他类型 - The challenge’s host name matches the host that you want to perform manual credential evaluation for.
这个认证需求发起的 host name ,与你想要请求的网站一致。
Listing 1 shows how to test these conditions, given the challenge
parameter passed to the URLSession:didReceiveChallenge:completionHandler:
callback. It gets the challenge’s protectionSpace
and uses it to perform the two checks listed above. First, it gets the authenticationMethod
from the protection space and checks that the type of authentication is NSURLAuthenticationMethodServerTrust
. Then it makes sure the protection space’s host
matches the expected name example.com
. If either of these conditions are not met, it calls the completionHandler
with the NSURLSessionAuthChallengePerformDefaultHandling
disposition to allow the system to handle the challenge.
下面的代码 例1 ,展示了怎样判断上面那些条件,并进行进一步的处理。首先获取到 challenge
的 protectionSpace
属性,并且获取到该属性中的authenticationMethod
属性,判断其类型是否是NSURLAuthenticationMethodServerTrust
,然后获取其host
属性,判断是否跟example.com
匹配,假如这两个判断有一个不匹配,那么调用 completionHandler block 时,disposition 参数会传入NSURLSessionAuthChallengePerformDefaultHandling
,从而使系统处理这次认证需求。
Listing 1
Testing the challenge type and host name of a server trust authentication challenge.
判断 challenge 的 认证类型 和 host
1 | let protectionSpace = challenge.protectionSpace |
Evaluate the Credential in the Challenge
如何对 challenge 中的证书进行认证
To access the server’s credential, get the serverTrust
property (an instance of the SecTrustRef
class) from the protection space
. Listing 2 shows how to access the server trust and accept or reject it. The listing starts by attempting to get the serverTrust
property from the protection space, and falls back to default handling if the property is nil
. Next, it passes the server trust to a private helper method checkValidity(of:)
that compares the certificate or public key in the server trust to known-good values stored in the app bundle.
通过获取protectionSpace
中的serverTrust
属性,来获取到一个SectrustRef
实例,例2 展示了怎样获取一个 server trust,并且信任还是拒绝它,首先获取到这个 serverTrust,假如为nil
的话,回调 completionHandler,传入参数为默认。然后将 server trust 传入到一个私有方法 checkValidity(of:)
中,将其公钥或证书 与存储在 bundle 中的公钥或证书进行对比
1 | guard let serverTrust = protectionSpace.serverTrust else { |
Once the code determines the validity of the server trust, it takes one of two actions:
根据 server trust 的正确与否,将会进行合适的处理:
If the server trust’s credential is valid, create a new
NSURLCredential
instance from the server trust. Then call thecompletionHandler
with theNSURLSessionAuthChallengeUseCredential
disposition, passing in the newly-created credential. This tells the system to accept the server’s credentials.
假如 server trust 的证书通过了认证,那么根据 server trust 生成一个NSURLCredential
对象,并在 completionHandler 中,disposition 参数传入NSURLSessionAuthChallengeUseCredential
,credential 参数传入新创建的证书对象,来告诉系统,你已经信任了这个系统证书。If the challenge’s credential is invalid, call the completionHandler with the
NSURLSessionAuthChallengeCancelAuthenticationChallenge
disposition. This tells the system to reject the server’s credentials.
假如 server trust 的证书没有通过验证,completionHandler 的 disposition 参数传入NSURLSessionAuthChallengeCancelAuthenticationChallenge
,证书传入 nil ,代表你拒绝了服务器的证书验证,即请求失败。
Tip
See
Certificate, Key, and Trust Services
to learn more about how to evaluate a SecTrustRef instance or access certificates or public keys from it.
为了更详细的了解怎样对一个SecTrustRef
进行验证,你可以阅读Certificate, Key, and Trust Services
文档
Create a Long-Term Server Authentication Strategy
从长远开发的角度来看,优雅的处理服务器认证需求
If you determine that you need to evaluate server trust manually in some or all cases, plan for what your app will do if you need to change your server credentials. Keep the following guidelines in mind:
在某些情况下,假如你决定手动的对认证需求进行处理,考虑一下这种情况,假如服务器的证书需要更换怎么办?你可以参考以下的方法:
Compare the server’s credentials against a public key, instead of storing a single certificate in your app bundle. This will allow you to reissue a certificate for the same key and update the server, rather than needing to update the app.
在进行证书比对时,将服务器证书与公钥进行比对,而不是在 app bundle 中存储一个证书,这样当服务器更换证书时,你仍然可以使用这个公钥,而不是更新你的 app。Compare the issuing certificate authority’s (CA’s) keys, rather than using the leaf key. This way, you can deploy certificates containing new keys signed by the same CA.
比对证书时,使用根证书的 key 进行比对,而不是使用节点 key,这样你可以支持验证根证书下发的新的证书。Use a set of keys or CAs, so you can rotate server credentials more gracefully.
采用一个证书或者公钥的集合,这样使你更优雅的处理证书的验证。