2012-01-25 25 views
19

मैं अपने UIWebView में HTTP मूल प्रमाणीकरण का समर्थन करना चाहता हूं।UIWebView में प्रमाणीकरण कैसे ठीक से करें?

पल में, मैं

webView:shouldStartLoadWithRequest:navigationType: में अनुरोध रद्द कर रहा हूँ तो के लिए जांच करें और आवश्यकतानुसार क्रेडेंशियल प्रदान करने अपने ही NSURLConnectionDelegate में उन्हें संभाल। मैं फिर वेब दृश्य में एचटीएमएल प्रस्तुत करने के लिए loadData:MIMEType:textEncodingName:baseURL: का उपयोग करता हूं। यह किसी भी यूआरएल के लिए ठीक काम करता है जो प्रतिनिधि को पास किया जाता है।

मेरी समस्या यह है कि प्रतिनिधि को कभी भी एम्बेडेड तत्वों, जैसे छवियों, जावास्क्रिप्ट या सीएसएस फ़ाइलों के लिए बुलाया नहीं जाता है। तो अगर मेरे पास एक HTML पृष्ठ है जो मूलभूत प्रमाणीकरण से संरक्षित एक छवि का संदर्भ देता है, तो उस छवि को ठीक से लोड नहीं किया जा सकता है। इसके अतिरिक्त, webView:didFinishLoad: कभी नहीं कहा जाता है, क्योंकि वेब दृश्य पूरी तरह से पृष्ठ को लोड नहीं कर सका।

मैंने ऐप स्टोर पर उपलब्ध तीसरे पक्ष के ब्राउज़र टेरा के साथ उस मामले की जांच की है, और यह पूरी तरह से उस स्थिति से निपट सकता है। मुझे लगता है कि अपना खुद का NSURLProtocol प्रदान करके इसे हल करना संभव होगा, लेकिन यह बहुत जटिल लगता है। मैं क्या खो रहा हूँ?

+0

अरे NeoNacho, क्या आपने यह पता लगाया कि इस मुद्दे को कैसे हल किया जाए? मुझे एक ही समस्या है - मैं एचटीएमएल पेज लोड कर सकता हूं, लेकिन सभी सीएसएस/जावास्क्रिप्ट को कभी भी लोड/संसाधित नहीं किया जाता है। यदि आपके पास कोई संकेत है, तो साझा करें :) – Stretch

उत्तर

25

उन सभी डोमेन के लिए साझा क्रेडेंशियल स्टोरेज का उपयोग करने का प्रयास करें जिन्हें आपको प्रमाणीकृत करने की आवश्यकता है।

यहाँ UIWebView के लिए नमूना काम कर रहा है यह विंडोज आईआईएस होने के खिलाफ परीक्षण किया गया था केवल BasicAuthentication सक्षम

यह आपकी साइट साख को जोड़ने का तरीका है:

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

आपका 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 

वेबव्यू प्रमाणीकरण के लिए अंतिम समाधान कस्टम प्रोटोकॉल कार्यान्वयन पर आधारित था। सभी प्रोटोकॉल एक स्टैक के रूप में पंजीकृत हैं, इसलिए यदि आप HTTP प्रोटोकॉल को फिर से परिभाषित करते हैं तो यह वेब व्यू से आने वाले सभी अनुरोधों को रोक देगा, इसलिए आपको इनकमिंग अनुरोध के साथ मिश्रित विशेषताओं को जांचना होगा और इसे नए अनुरोध में दोबारा डालना होगा और इसे अपने कनेक्शन के माध्यम से दोबारा भेजना होगा। चूंकि आप ढेर में हैं, इसलिए आपका अनुरोध आपसे तत्काल आता है और आपको इसे अनदेखा करना होगा। तो यह वास्तविक HTTP प्रोटोकॉल कार्यान्वयन के लिए प्रोटोकॉल स्टैक डाउन हो जाता है, क्योंकि आपका अनुरोध प्रमाणीकृत नहीं है, आपको प्रमाणीकरण अनुरोध प्राप्त होगा। और प्रमाणीकरण के बाद आपको सर्वर से वास्तविक प्रतिक्रिया मिलेगी, इसलिए आप प्रतिक्रिया को दोबारा जवाब देते हैं और वेब व्यू से प्राप्त मूल अनुरोध का जवाब देते हैं और यही वह है।

डॉन; नए अनुरोध या प्रतिक्रिया निकायों को बनाने का प्रयास न करें, आपको बस उन्हें फिर से भेजना होगा। अंतिम कोड कोड की 30-40 लाइनों की लगभग 30-40 लाइन होगी और यह काफी सरल है, लेकिन बहुत सारे डिबगिंग और टेटिंग की आवश्यकता है।

Unfortunatlly मैं यहाँ के बाद से मैं पहले से ही अलग परियोजना के लिए आवंटित किया हूँ कोड प्रदान नहीं कर सकते, मैं सिर्फ इतना कहना है कि मेरी पोस्ट गलत तरीका है चाहता था, यह stucks जब उपयोगकर्ता पासवर्ड बदल जाता है।

+0

क्या यह स्वचालित रूप से होता है? एक बार मेजबान के खिलाफ प्रमाणीकृत हो जाने पर, ऐसा लगता है कि बाद के अनुरोधों पर पुनः प्रमाणीकरण करना आवश्यक नहीं है। –

+0

मै मैक्स के समान व्यवहार देख रहा हूं। एक बार प्रमाणीकृत हो जाने के बाद, मुझे फिर से प्रमाणीकृत करने के लिए चुनौती नहीं दी जा रही है। मुझे लगता है कि मेरे आईओएस सिम्युलेटर सत्र में कुछ संग्रहीत किया जा रहा है? मैंने सिम्युलेटर को रीसेट करने का प्रयास किया, लेकिन अभी भी चुनौती नहीं दी जा रही है। अजीब। – gstroup

+0

"मॉडरेटर ने मेरे लिए महत्वपूर्ण पोस्ट मारे, मैं उनके लिए महत्वपूर्ण पदों को मारता हूं। इस पोस्ट ने मुझे 4 सप्ताह का शोध दिया। तो एक अच्छा दिन है"। मुझे आपको यह बताने में खेद है कि आपका मूल उत्तर पोस्ट इतिहास में संग्रहीत है और अब इसे पुनर्प्राप्त किया जा रहा है। ध्यान देने के लिए धन्यवाद। – Shoe

2

TKAURLProtocolPro के लिए [http://kadao.dir.bg/cocoa.htm] SVWebViewController के लिए [https://github.com/samvermette/SVWebViewController]

8

गुप्त बुनियादी प्रमाणीकरण का उपयोग कोको है करने के लिए http NSURL और संबंधित वर्गों को जानना।

  • NSURL
  • NSURLRequest/NSMutableURLRequest
  • NSURLConnection
  • NSURLCredential
  • NSURLCredentialStorage
  • NSURLProtectionSpace
  • UIWebView/WebView/NIWebController आदि

असली जादू NSURLConnection से आता है। DevDocs के शब्दों में, "एक NSURLConnection ऑब्जेक्ट एक यूआरएल अनुरोध लोड करने के लिए समर्थन प्रदान करता है।" यदि आप इसे प्रदर्शित किए बिना पृष्ठभूमि में कुछ यूआरएल लोड करना चाहते हैं तो आप NSURLConnection का उपयोग करेंगे। NSURLConnection की वास्तविक शक्ति विधि

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

NSURLConnectionDelegate प्रोटोकॉल सफल कनेक्शन, घातक त्रुटियों, और प्रमाणीकरण चुनौतियों का जवाब देने के लिए तरीके है में है। यदि आप HTTP मूल प्रमाणीकरण द्वारा संरक्षित डेटा तक पहुंचने का प्रयास कर रहे हैं तो यह है कि कोको इसे कैसे करता है। इस बिंदु पर एक उदाहरण कुछ स्पष्टता लाएगा।

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

यह एक यूआरएल बनाता है। यूआरएल से एक URLRequest बनाया गया है। URLRequest को फिर वेब दृश्य में लोड किया जाता है। 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; 
} 

जब भी प्रमाणीकरण चुनौती एक क्रेडेंशियल क्रेडेंशियल संग्रहण में जोड़ा जाता है नहीं है। आप क्रेडेंशियल स्टोरेज का उपयोग करने के लिए कनेक्शन भी बताते हैं।

+0

बस एक प्रश्न: मैं देखता हूं कि जब आप didReceiveAuthenticationChallenge ईवेंट प्राप्त करते हैं तो आप साझा क्रेडेंशियल स्टोरेज में प्रमाण-पत्र जोड़ रहे हैं। लेकिन क्या यह हर बार आपको इस घटना को प्राप्त करने के लिए क्रेडेंशियल रीसेट करने का एक अच्छा अभ्यास है? बस पूछते हुए, मैं यह पता लगाने की कोशिश कर रहा हूं कि साझा क्रेडेंशियल स्टोरेज कैसे काम करता है। – NLemay

+0

यह शायद सबसे अच्छा समाधान नहीं है, लेकिन यह कार्यात्मक था और मैं इसे साफ करने के लिए कभी वापस नहीं गया। यदि पासवर्ड बदलने वाला नहीं है तो आप इसे प्रतिनिधि कॉल के बाहर रख सकते हैं और केवल एक बार ऐसा कर सकते हैं, यदि क्रेडेंशियल बदलने जा रहा है तो चुनौती प्राप्त होने पर वर्तमान उपयोगकर्ता नाम पासवर्ड देखना अच्छा होगा। –

4

मैंने UIWebView के लिए NSMutableURLRequest का उपयोग करके मूल लेख क्रेडेंशियल्स सेट करके इसे अभी लागू किया है। यह sharedCredentialStorage को लागू करते समय किए गए राउंड ट्रिप से भी बचाता है (बेशक इसमें ट्रेडऑफ शामिल हैं)।

समाधान:

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

आप NSData + Base64 श्रेणी जो Matt Gallagher's page से NSData के लिए base64EncodedString लागू करता है प्राप्त कर सकते हैं (यह ब्लॉग पोस्ट के नीचे स्थित था जब मैं इसे डाउनलोड किया)

+0

बस सोचा कि मैं जोड़ूंगा कि अब आपको एनएसडीटा + बेस 64 श्रेणी की आवश्यकता नहीं है क्योंकि कार्यक्षमता अब मानक एनएसडीटा कार्यान्वयन में शामिल है (हालांकि थोड़ा अलग विधि हस्ताक्षर) –

+0

दरअसल - बेस 64 एन्कोडिंग के लिए ऐप्पल दस्तावेज़ देखें [यहां] (https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/#//apple_ref/occ/instm/NSData/base64EncodedStringWithOptions :) – NSTJ

+5

अंत में आप एक नया यूआरएल अनुरोध परिभाषित करते हैं '[NSURLRequest basicAuthHTTPURLRequestForUrl: url]; 'पिछली परिभाषाएं उस अनुरोध को कैसे प्रभावित करती हैं? – matteok

संबंधित मुद्दे