How to do authentication in UIWebView properly?

后端 未结 5 1918
离开以前
离开以前 2020-12-02 15:49

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

At the moment, I am canceling requests in

webView:shouldStartLoadWithRequest:navig

相关标签:
5条回答
  • 2020-12-02 16:30

    Make sure to remember that logging out is not so easy with sessions and UIWebView credentials. See answer here: https://stackoverflow.com/a/18143902/2116338.

    0 讨论(0)
  • 2020-12-02 16:45

    For TKAURLProtocolPro [http://kadao.dir.bg/cocoa.htm] For SVWebViewController [https://github.com/samvermette/SVWebViewController]

    0 讨论(0)
  • 2020-12-02 16:48

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

    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];
    

    Edit: 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)
    

    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
    

    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.

    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.

    0 讨论(0)
  • 2020-12-02 16:48

    I've just implemented this by setting basic auth credentials using an NSMutableURLRequest for the UIWebView. This also avoids the round trip incurred when implementing sharedCredentialStorage (of course there are tradeoffs involved).

    Solution:

        NSString *url = @"http://www.my-url-which-requires-basic-auth.io"
        NSString *authStr = [NSString stringWithFormat:@"%@:%@", username, password];
        NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
        NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedString]];
        NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
        [mutableRequest setValue:authValue forHTTPHeaderField:@"Authorization"];
        NSURLRequest *request = [mutableRequest copy];
        NSURLRequest *request = [NSURLRequest basicAuthHTTPURLRequestForUrl:url];
        [self.webView loadRequest:request];
    

    You can grab the NSData+Base64 category which implements the base64EncodedString for NSData from Matt Gallagher's page (it was at the bottom of the blog post when I downloaded it)

    0 讨论(0)
  • 2020-12-02 16:54

    The secret to HTTP basic authentication using cocoa is knowing NSURL and the related classes.

    • NSURL
    • NSURLRequest/NSMutableURLRequest
    • NSURLConnection
    • NSURLCredential
    • NSURLCredentialStorage
    • NSURLProtectionSpace
    • UIWebView/WebView/NIWebController etc.

    The real magic comes from NSURLConnection. In the words of the devDocs, "An NSURLConnection object provides support to perform the loading of a URL request." If you want to load some a URL in the background without displaying it you would use NSURLConnection. The real power of the NSURLConnection is in the method

    + (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id < NSURLConnectionDelegate >)delegate
    

    The NSURLConnectionDelegate protocol has methods for responding to successful connections, fatal errors, and authentication challenges. If you are trying to access data Protected by HTTP basic authentication this is how Cocoa does it. At this point an example should bring some clarity.

    //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];
    

    This creates a URL. From the URL a URLRequest is created. The URLRequest is then loaded in the web view. The Request is also used to make a URLConnection. We don't really use the connection, but we need to receive notifications about authentication so we set the delegate. There are only two methods we need from the delegate.

    - (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;
    }
    

    Whenever there is an authentication challenge a credential is added to the credential storage. You also tell the connection to use the credential storage.

    0 讨论(0)
提交回复
热议问题