如何在UIWebView中正确进行身份验证?

Neo*_*cho 19 authentication uiwebview ios

我想在我的UIWebView中支持HTTP基本身份验证.

目前,我正在取消请求

webView:shouldStartLoadWithRequest:navigationType:然后在我自己的NSURLConnectionDelegate中处理它们以检查并在需要时提供凭据.然后我用loadData:MIMEType:textEncodingName:baseURL:它在Web视图中显示HTML.这适用于传递给委托的任何URL.

我的问题是委托永远不会被要求嵌入元素,如图像,JavaScript或CSS文件.因此,如果我有一个HTML页面引用受基本身份验证保护的图像,则无法正确加载该图像.此外,webView:didFinishLoad:永远不会调用,因为Web视图无法完全加载页面.

我已经通过App Store上的第三方浏览器Terra检查了这个案例,它可以完全应对这种情况.我认为可以通过提供我自己的NSURLProtocol来解决这个问题,但这似乎太复杂了.我错过了什么?

小智 26

尝试对需要进行身份验证的所有域使用sharedCredentialStorage.

这是针对UIWebView的工作示例,它针对仅启用了BasicAuthentication的Windows IIS进行了测试

这是添加站点凭据的方法:

NSString* login = @"MYDOMAIN\\myname";
NSURLCredential *credential = [NSURLCredential credentialWithUser:login
                                                         password:@"mypassword"
                                                      persistence:NSURLCredentialPersistenceForSession];

NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
                                         initWithHost:@"myhost"
                                                 port:80
                                             protocol:@"http"
                                                realm:@"myhost" // check your web site settigns or log messages of didReceiveAuthenticationChallenge
                                 authenticationMethod:NSURLAuthenticationMethodDefault];

[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential
                                                    forProtectionSpace:protectionSpace];
[protectionSpace release];
Run Code Online (Sandbox Code Playgroud)

编辑:Swift 4中的相同代码

let login = "MYDOMAIN\\myname"
let credential = URLCredential(user:login, password:"mypassword", persistence:.forSession)
let protectionSpace = URLProtectionSpace(host:"myhost", port:80, protocol:"http", realm:"myhost", authenticationMethod:NSURLAuthenticationMethodDefault)
URLCredentialStorage.shared.setDefaultCredential(credential, for:protectionSpace)
Run Code Online (Sandbox Code Playgroud)

您的webView现在可以正常工作,如果它不起作用则使用下一个代码进行调试,尤其是检查didReceiveAuthenticationChallenge的日志消息.

    #import "TheSplitAppDelegate.h"
    #import "RootViewController.h"

    @implementation TheSplitAppDelegate

    @synthesize window = _window;
    @synthesize splitViewController = _splitViewController;
    @synthesize rootViewController = _rootViewController;
    @synthesize detailViewController = _detailViewController;

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // Override point for customization after application launch.
        // Add the split view controller's view to the window and display.
        self.window.rootViewController = self.splitViewController;
        [self.window makeKeyAndVisible];

        NSLog(@"CONNECTION: Add credentials");

        NSString* login = @"MYDOMAIN\\myname";
        NSURLCredential *credential = [NSURLCredential credentialWithUser:login
                                                                 password:@"mypassword"
                                                              persistence:NSURLCredentialPersistenceForSession];

        NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
                                                 initWithHost:@"myhost"
                                                 port:80
                                                 protocol:@"http"
                                                 realm:@"myhost" // check your web site settigns or log messages of didReceiveAuthenticationChallenge
                                                 authenticationMethod:NSURLAuthenticationMethodDefault];


        [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];
        [protectionSpace release];    

        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://myhost/index.html"]
                                                               cachePolicy:NSURLRequestReloadIgnoringCacheData
                                                           timeoutInterval:12
                                        ];

        NSLog(@"CONNECTION: Run request");
        [[NSURLConnection alloc] initWithRequest:request delegate:self];

        return YES;
    }

    - (void)applicationWillResignActive:(UIApplication *)application
    {

    }

    - (void)applicationDidEnterBackground:(UIApplication *)application
    {

    }

    - (void)applicationWillEnterForeground:(UIApplication *)application
    {

    }

    - (void)applicationDidBecomeActive:(UIApplication *)application
    {

    }

    - (void)applicationWillTerminate:(UIApplication *)application
    {

    }

    - (void)dealloc
    {
        [_window release];
        [_splitViewController release];
        [_rootViewController release];
        [_detailViewController release];
        [super dealloc];
    }

    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
    {
        NSLog(@"CONNECTION: got auth challange");
        NSString* message = [NSString stringWithFormat:@"CONNECTION: cred cout = %i", [[[NSURLCredentialStorage sharedCredentialStorage] allCredentials] count]];
        NSLog(message);
        NSLog([connection description]);

        NSLog([NSString stringWithFormat:@"CONNECTION: host = %@", [[challenge protectionSpace] host]]);
        NSLog([NSString stringWithFormat:@"CONNECTION: port = %i", [[challenge protectionSpace] port]]);
        NSLog([NSString stringWithFormat:@"CONNECTION: protocol = %@", [[challenge protectionSpace] protocol]]);
        NSLog([NSString stringWithFormat:@"CONNECTION: realm = %@", [[challenge protectionSpace] realm]]);
        NSLog([NSString stringWithFormat:@"CONNECTION: authenticationMethod = %@", [[challenge protectionSpace] authenticationMethod]]);
    }

    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
        // release the connection, and the data object
        [connection release];

        // inform the user
        NSLog(@"CONNECTION: failed! Error - %@ %@",
              [error localizedDescription],
              [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
    } 

    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
    {
        NSLog(@"CONNECTION: received response via nsurlconnection");
    }

    - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
    {
        NSLog(@"CONNECTION: USE!");
        return YES;
    }


    @end
Run Code Online (Sandbox Code Playgroud)

WebView身份验证的最终解决方案基于自定义协议实现.所有协议都注册为堆栈,因此如果您重新定义HTTP协议,它将拦截来自webView的所有请求,因此您必须检查与传入请求相关的属性并将其重新打包到新请求中并通过您自己的连接再次发送.由于你处于堆叠状态,你的请求会再次出现,你必须忽略它.因此,它会将协议栈降低到真正的HTTP协议实现,因为您的请求未经过身份验证,您将获得认证请求.在验证之后,您将从服务器获得真实的响应,因此您重新打包响应并回复从webView收到的原始请求,就是这样.

不要尝试创建新的请求或响应主体,您必须重新发送它们.最终的代码将是大约30-40行代码,它非常简单,但需要大量的调试和挖掘.

不幸的是我不能在这里提供代码,因为我已经被分配到不同的项目,我只想说我的帖子是错误的方式,当用户更改密码时它会卡住.


And*_*oos 8

使用cocoa进行HTTP基本身份验证的秘诀是知道NSURL和相关的类.

  • NSURL
  • 的NSURLRequest/NSMutableURLRequest
  • NSURLConnection的
  • NSURLCredential
  • NSURLCredentialStorage
  • NSURLProtectionSpace
  • UIWebView/WebView/NIWebController等

真正的魔力来自NSURLConnection.用devDocs的话说,"NSURLConnection对象提供了对URL请求加载的支持." 如果要在后台加载某些URL而不显示它,则可以使用NSURLConnection.NSURLConnection的真正力量在于该方法

+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id < NSURLConnectionDelegate >)delegate
Run Code Online (Sandbox Code Playgroud)

NSURLConnectionDelegate协议具有响应成功连接,致命错误和身份验证挑战的方法.如果您尝试访问受HTTP基本身份验证保护的数据,那么Cocoa就是这样做的.在这一点上,一个例子应该带来一些清晰度.

//basic HTTP authentication
NSURL *url = [NSURL URLWithString: urlString];
NSMutableURLRequest *request;
request = [NSMutableURLRequest requestWithURL:url
                              cachePolicy:NSURLRequestReloadIgnoringCacheData
                          timeoutInterval:12];
[self.webView openRequest:request];
(void)[NSURLConnection connectionWithRequest:request delegate:self];
Run Code Online (Sandbox Code Playgroud)

这会创建一个URL.从URL创建URLRequest.然后在Web视图中加载URLRequest.Request也用于创建URLConnection.我们并不真正使用连接,但我们需要接收有关身份验证的通知,因此我们设置了委托.委托只需要两种方法.

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{  
    NSURLCredential * cred = [NSURLCredential credentialWithUser:@"username"
                                                    password:@"password"
                                                 persistence:NSURLCredentialPersistenceForSession];
[[NSURLCredentialStorage sharedCredentialStorage]setCredential:cred forProtectionSpace:[challenge protectionSpace]];

}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
{
    return YES;
}
Run Code Online (Sandbox Code Playgroud)

只要存在身份验证质询,就会将凭据添加到凭据存储中.您还告诉连接使用凭证存储.