如何正确在 UIWebView 中进行身份验证? [英] How to do authentication in UIWebView properly?

查看:23
本文介绍了如何正确在 UIWebView 中进行身份验证?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to support HTTP Basic Authentication in my UIWebView.

目前,我正在取消

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

webView:shouldStartLoadWithRequest:navigationType: then handle them in my own NSURLConnectionDelegate to check for and provide credentials if needed. I then use loadData:MIMEType:textEncodingName:baseURL: to present HTML in the web view. That works fine for any URLs that are passed to the delegate.

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

My problem is that the delegate is never called for embedded elements, like images, JavaScript or CSS files. So if I have an HTML page which references an image which is protected with basic authentication, that image cannot be loaded properly. Additionally, webView:didFinishLoad: is never called, because the web view could not fully load the page.

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

I have checked that case with Terra, a third-party browser available on the App Store, and it can fully cope with that situation. I think it would be possible to solve this by providing my own NSURLProtocol, but that seems too complicated. What am I missing?

推荐答案

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

Try to use sharedCredentialStorage for all domains you need to authenticate.

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

Here is working sample for UIWebView it was tested against Windows IIS having only BasicAuthentication enabled

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

This is how to add your site 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];

Swift 4 中的相同代码

same code in 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)

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

Your webView is supposed to work now, if it does not work use next code to debug, especially check log messages of 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

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

The final solution for WebView authentication was based on custom protocol implementation. All protocols registered as a stack, so if you redefine HTTP protocol it would intercept all requests coming from webView, so you have to check attributes assotiated with incoming request and repack it into new request and send it again via your own connection. Since you are in stack, your request immidiatly comes to you again and you have to ignore it. So it goes down protocol stack to real HTTP protocol implementation, since your request is not athenticated you'll get authenticaiton request. And after authenticaiton you'll get a real response from server, so you repack response and reply to original request received from webView and that's it.

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

Don;t try to create new requests or responses bodies, you have to just resend them. The final code would be aproximetly 30-40 lines of code and it is quite simple, but requires a lot of debuging and tetsing.

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

Unfortunatlly I cannot provide code here, since I am assigned to different project already, I just wanted to say that my post is wrong way, it stucks when user changes password.

这篇关于如何正确在 UIWebView 中进行身份验证?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆