2009-12-20 18 views
8

Có ai đã tích hợp ứng dụng iPhone với Nhà cung cấp nhận dạng Shibboleth không? Googling đã không đưa ra bất cứ điều gì vì vậy tôi yêu cầu rất kinh nghiệm trực tiếp.Tích hợp ứng dụng iPhone với Shibboleth

Nếu trước đây chưa từng là giai điệu, có khả thi không?

+0

Bạn đang nói về ứng dụng web hoặc ứng dụng gốc? –

+0

ứng dụng gốc; hoặc trang web iPhone có thể xác thực ứng dụng gốc không? – user353829

Trả lời

15

Câu trả lời cho cả hai là "Có".

tôi là một anh chàng Java, vì vậy được yêu cầu cách đây hai tuần tới:

  • Tìm hiểu Objective-C
  • Viết một bản iPhone App
  • Authenticated lập trình với Shibboleth
  • Tải về một hiển thị datafile được bảo vệ bởi Shibboleth

... Có một chút khó khăn. Hợp chất rằng với sự vắng mặt của bất kỳ bài viết diễn đàn để giúp đỡ đã khiến tôi chia sẻ kinh nghiệm của tôi.

Dưới đây là tổng quan, theo sau là một số mã mẫu rất hữu ích. Hãy bình chọn cho câu trả lời của tôi nếu điều này giúp! Nó có giá trị một vài tuần thời gian của tôi :)

Đối với một ứng dụng trên iPhone để tải về nguồn lực Shibbolized, nhu cầu sau đây xảy ra:

  1. Sử dụng URL của API trong Cocoa để gửi yêu cầu HTTP cho tài nguyên được đề cập.
  2. Thực hiện một lớp đại biểu cho yêu cầu đến:
  3. Respond to lại trực tiếp đến IdP (tự động biếu không của Cocoa) SP
  4. Respond to tin tưởng chứng chỉ máy chủ thử thách
  5. Respond to dùng chứng chỉ thách thức
  6. Trả lời lỗi (nếu cần)
  7. Nhận "mẫu liên kết" của IdP cho người dùng được xác thực, biểu mẫu HTML chuyển hướng người dùng trở lại SP với hai tham số
  8. Lập trình HTTP POST hai thông số từ IdP quay lại SP.
  9. Cookie được lưu trữ tự động và chuyển tiếp của Cocoa một lần nữa
  10. Thực hiện yêu cầu ủy quyền URL thứ hai để nhận dữ liệu yêu cầu ban đầu.

Dưới đây là một số tài liệu tham khảo hữu ích từ Apple và Shibboleth:

Và hy vọng tôi có thể bao gồm tất cả các nguồn cho một cuộc biểu tình nhanh chóng.

ApplicationDelegate.h 
---------- 
#import <UIKit/UIKit.h> 
#import "ConsoleViewController.h" 

/* 
The application delegate will hold references to the application's UIWindow and a ConsoleViewController. 
The console does all of the interesting Shibboleth activities. 
*/ 
@interface ApplicationDelegate : NSObject <UIApplicationDelegate> { 

UIWindow *window; 
ConsoleViewController *consoleViewController; 
} 


@end 

ApplicationDelegate.m 
---------- 
#import "ApplicationDelegate.h" 
#import "ConsoleViewController.h" 

/* 
The implementation for the ApplicationDelegate initializes the console view controller and assembles everything. 
The console does all of the interesting Shibboleth activities. 
*/ 
@implementation ApplicationDelegate 


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

// Initialize the console. 
consoleViewController = [[ConsoleViewController alloc] init]; 

window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 
[window setBackgroundColor:[UIColor lightGrayColor]]; 
[window addSubview:[consoleViewController view]]; 

[window makeKeyAndVisible]; 
} 


- (void)dealloc { 
    [window release]; 
[ConsoleViewController release]; 
    [super dealloc]; 
} 


@end 

ConsoleController.h 
---------- 
#import <Foundation/Foundation.h> 
#import <UIKit/UIKit.h> 

/* 
The ConsoleViewController's interface declares references to the network data used in negotiating with Shibboleth 
and a UITextView used to display the final result or errors. 
*/ 
@interface ConsoleViewController : UIViewController { 

NSMutableData *responseData; 
NSString *responseString; 
UITextView *console; 
} 

@end 

ConsoleController.m 
---------- 
#import "ApplicationDelegate.h" 
#import "ConsoleViewController.h" 


/* 
This delegate is used when making the second HTTP request with Shibboleth. If you're just getting here, start 
by reading the comments for ConsoleViewController below. 

All we need to do now is receive the response from the SP and display it. 
If all goes well, this should be the secured page originally requested. 
*/ 
@interface AuthenticationRedirectDelegate : NSObject { 

NSMutableData *authResponseData; 
NSString *authResponseString; 
UITextView *console; 
} 

@property (nonatomic retain) UITextView *console; 

@end 


/* 
Refer to the comments for the interface above. 
*/ 
@implementation AuthenticationRedirectDelegate 

@synthesize console; 

-(id)init { 
authResponseData = [[NSMutableData alloc] retain]; 
return self; 
} 


- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 
[authResponseData setLength:0]; 
} 


- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 
[authResponseData appendData:data]; 
} 


- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 
[console setText:[error localizedDescription]]; 
} 


/* 
Once the data is received from Shibboleth's SP, display it. 
*/ 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 

authResponseString = [[NSString alloc] initWithData:authResponseData encoding:NSUTF8StringEncoding]; 
[console setText:authResponseString]; 
[connection release]; 
} 


@end 


/* 
The implementation of the ConsoleViewController, and AuthenticationRedirectDelegate above, contain the real logic of 
this Shibboleth exercise. The ConsoleViewController performs the following: 
1. Prepare the initial HTTP request to a Shibboleth protected resource. 
2. Act as the delegate whilst Cocoa's URL Loading API receives the HTTP Response. 
NOTE: We instruct Cocoa in advance to take care of the SP redirecting to the IdP, accepting the server certificate, 
and submitting the user credentials 
3. Once the HTTP Response is finished loading, parse the <form action, RelayState and SAMLResponse from the IdP's 
response 
4. Call a utility method to prepare a second HTTP POST Request to the <form action/SP with the IdP's parameters 
NOTE: We do not need to transfer over any of Shibboleth's cookies, since Cocoa is doing this automatically 
5. Use a new instance of AuthenticationRedirectDelegate to receive the POST's response, which should be the secured 
page originally requested. 
6. Display the final content in the UITextView known as console. 
*/ 
@implementation ConsoleViewController 


/* 
A handy utility method for extracting a substring marked by two provided token strings. 
Used in parsing the HTML form returned by the IdP after the first HTTP Request. 
*/ 
+(id)substringFromString:(NSString *)source BetweenOpenToken:(NSString *)openToken AndCloseToken:(NSString *)closeToken { 

NSUInteger l = [source length]; 
NSUInteger openTokenLen = [openToken length]; 

NSUInteger openTokenLoc = ([source rangeOfString:openToken]).location; 
NSUInteger valueLoc = openTokenLoc + openTokenLen; 
NSRange searchRange = NSMakeRange(valueLoc, l - valueLoc); 
NSUInteger closeTokenLoc = ([source rangeOfString:closeToken options:NSCaseInsensitiveSearch range:searchRange]).location; 
searchRange = NSMakeRange(valueLoc, closeTokenLoc - valueLoc); 
NSString *result = [source substringWithRange:searchRange]; 

return result; 
} 


/* 
This function takes the three properties returned by the IdP after the first HTTP request and 
HTTP POSTs them to the SP as specified by the IdP in the "url" parameter. 
*/ 
-(void)authReturnTo:(NSURL *)url WithRelay:(NSString *)relayState AndSAML:(NSString *)samlResponse { 

// Here we assemble the HTTP POST body as usual. 
NSString *preBody = [[NSString alloc] initWithString:@"RelayState="]; 
preBody = [preBody stringByAppendingString:relayState]; 
preBody = [preBody stringByAppendingString:@"&"]; 
preBody = [preBody stringByAppendingString:@"SAMLResponse="]; 
preBody = [preBody stringByAppendingString:samlResponse]; 

/* The SAMLResponse parameter contains characters (+) that the SP expects to be URL encoded. 
    Here we simply manually URL encode those characters. You may wish to harden this with proper 
    URL encoding for production use. 
    */ 
NSString *httpBody = [preBody stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"]; 
NSData *httpBodyData = [httpBody dataUsingEncoding:NSUTF8StringEncoding]; 

NSString *httpContentLength = [NSString stringWithFormat:@"%d", [httpBodyData length]]; 

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url 
       cachePolicy:NSURLRequestReloadIgnoringCacheData 
       timeoutInterval:12.0]; 
[request setHTTPMethod:@"POST"]; 
[request setValue:httpContentLength forHTTPHeaderField:@"Content-Length"]; 
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; 

[request setHTTPBody:httpBodyData]; 

// Submit the HTTP POST using the second delegate class to receive the response 
AuthenticationRedirectDelegate *delegate = [[AuthenticationRedirectDelegate alloc] init]; 
delegate.console=console; 
[[NSURLConnection alloc] initWithRequest:request delegate:delegate]; 
} 


/* 
When this UIViewController finishes loading, automatically prepare and send a request to the Shibboleth SP Web Server 
for a secured resource. 
*/ 
- (void)viewDidLoad { 
[super viewDidLoad]; 

console = [[UITextView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 
[[self view] addSubview:console]; 

responseData = [[NSMutableData data] retain]; 

// TODO: Enter your own URL for a Shibboleth secured resource. 
NSURL *url = [NSURL URLWithString:@"<URL>"]; 

NSURLRequest *request = [NSURLRequest requestWithURL:url 
     cachePolicy:NSURLRequestUseProtocolCachePolicy 
     timeoutInterval:12.0]; 

[[NSURLConnection alloc] initWithRequest:request delegate:self]; 

/* Control flows to the delegate methods below */ 
} 


/* 
Refer to Apple's docs on the URL Loading System for details. 
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html 
*/ 
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 
    [responseData setLength:0]; 
} 


/* 
Refer to Apple's docs on the URL Loading System for details. 
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html 
*/ 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 
[responseData appendData:data]; 
} 

/* 
This implementation in the delegate let's Cocoa trust my SP Web Server's self-signed certificate. 
TODO: You will want to harden this for production use. 

Refer to Apple's docs on the URL Loading System for details. 
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html 
*/ 
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { 
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]; 
} 


/* 
This implementation for the delegate does two things: 
1. Respond to challenges for my server's self-signed certificate 
2. Respond to the IdP's challenge for the username and password. 
TODO: Enter your own username and password here. 
Refer to Apple's docs on the URL Loading System for details. 
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html 
*/ 
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 
// TODO: Enter the correct username and password below. 
/* 
    WARNING: Using an incorrect user name and password will result in your application being re-challenged 
    by the IdP. Cocoa will return to this function in a never-ending loop. This can result in the message 
    "NSPosixErrorDomain Too many open files". You'll need to perform additional coding to handle this. 
    */ 
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) 
    [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; 
else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]) 
    [challenge.sender useCredential:[NSURLCredential credentialWithUser:@"<USERNAME>" password:@"<PASSWORD>" persistence:NSURLCredentialPersistenceNone] forAuthenticationChallenge:challenge]; 
else 
    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; 
} 


/* 
You may wish to add more code here to log errors. 

Refer to Apple's docs on the URL Loading System for details. 
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html 
*/ 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 
[console setText:[error localizedDescription]]; 
} 


/* 
Once Cocoa has received a (hopefully) authenticated response from the IdP, we parse out the relevant pieces and prepare to 
HTTP POST them back to the SP as specified by the IdP in the <form action attribute. 

Refer to Apple's docs on the URL Loading System for details. 
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html 
*/ 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 
[connection release]; 
responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; 

if([responseString rangeOfString:@"SAMLResponse"].length < 1) 
{ 
    [console setText:[@"Unexpected response:\n]n" stringByAppendingString:responseString]]; 
    return; 
} 

NSString *relayState = [ConsoleViewController substringFromString:responseString BetweenOpenToken:@"RelayState\" value=\"" AndCloseToken:@"\"/>"]; 
NSString *SAMLResponse = [ConsoleViewController substringFromString:responseString BetweenOpenToken:@"SAMLResponse\" value=\"" AndCloseToken:@"\"/>"]; 
NSString *formAction = [ConsoleViewController substringFromString:responseString BetweenOpenToken:@"<form action=\"" AndCloseToken:@"\""]; 
NSURL *formActionURL = [[NSURL alloc] initWithString:formAction]; 
[self authReturnTo:formActionURL WithRelay:relayState AndSAML:SAMLResponse]; 
} 


@end 
+0

Rất hay, nhưng nó không hoạt động với xác thực dựa trên biểu mẫu nơi bạn cần phân tích cú pháp html hoặc hiển thị nó cho người dùng để tương tác. Trong trường hợp của tôi, người dùng cần phải lặp qua nhiều yếu tố xác thực, vì vậy tôi không thể vượt qua nó. Dù sao tôi có thể dịch mẫu của bạn sang C#, ngôn ngữ của tôi được lựa chọn để phát triển iPhone và sử dụng nó làm cơ sở. Cảm ơn – Monoman

0

Tôi đã triển khai thành công bằng cách sử dụng giải pháp của EC làm điểm xuất phát.Điều duy nhất tôi muốn nói thêm là bạn thực sự phải chú ý đến việc duy trì một yêu cầu duy nhất tại một thời điểm. Trong quá trình thực hiện của chúng tôi, quá trình xác thực sẽ bị lẫn lộn giữa nhiều yêu cầu không đồng bộ chạy đồng thời. Sử dụng NSOperation để điều tiết hàng đợi dường như làm việc tốt cho tôi.

0

Tôi đã làm được điều đó, nhưng phải mất một thời gian để hiểu từng bước của quy trình và tái tạo nó một cách hoàn hảo. Nếu tôi có thời gian, tôi có thể viết một hướng dẫn chi tiết, bởi vì tôi không tìm thấy bất kỳ sự giúp đỡ nào cho rất nhiều vấn đề tôi gặp phải. Vấn đề là, nó cũng phụ thuộc vào trang web bạn muốn kết nối, do đó, bạn có thể không theo cùng một con đường như của tôi (quá trình của nó là giống như một mô tả here).

Để xem mọi yêu cầu được trình duyệt của tôi (Chrome) kích hoạt để kết nối, tôi đã sử dụng các công cụ dành cho nhà phát triển Bảng điều khiển mạng, với 'Bảo tồn nhật ký' được chọn.

Một vài gợi ý:

  • 1 °) Bạn cần để có được "_idp_authn_lc_key ..." cookie. Có một yêu cầu đặt nó cho bạn, tìm nó.

  • 2 °) Bạn cần vé đăng nhập (LT -...). Có thể bạn sẽ tìm thấy nó trong phần nội dung của trang yêu cầu thông tin đăng nhập của bạn.

  • 3 °) Bạn cần một vé dịch vụ (ST -...). Một lần nữa, bạn sẽ tìm thấy nó trong trang mà yêu cầu trước đó trả về.

  • 4 °) Bạn cần SAMLResponse. Một lần nữa, bạn sẽ tìm thấy nó trong trang mà yêu cầu trước đó trả về.

  • 5 °) Cuối cùng, bạn có thể đăng nhập bằng cách gửi lại SAMLResponse cho nhà cung cấp dịch vụ. Bạn nên chăm sóc mã hóa, ở đây. Tôi đã có một vài '+' hoặc '=' mà tôi cần phải thay đổi thành '% 2B' và '% 3D'. Bạn sẽ được cung cấp một cookie "_idp_session", điều đó sẽ cho phép bạn kết nối lại mà không có tất cả mớ hỗn độn này.

Nếu ai đó cố gắng làm như vậy, tôi rất sẵn lòng trợ giúp! Chỉ cần gửi cho tôi một tin nhắn.