2009-12-20 158 views
8

是否有人将iPhone应用程序与Shibboleth身份提供程序集成?谷歌搜索没有提出任何事情,所以我直接问大师。将iPhone应用程序与Shibboleth集成

如果以前没有多内斯,是可行这样做呢?

+0

你说的是一个Web应用程序或本机应用程序?原生应用程序 –

+0

;或iPhone的网页可以验证本地应用程序? – user353829

回答

15

这个问题的答案都为“是”。

我是一个Java的家伙,所以被要求两周前:

  • 学习Objective-C
  • 写一个原生iPhone应用程序
  • 与Shibboleth的编程验证的
  • 下载显示的Shibboleth保护的数据文件

...心里有点望而生畏。由于没有任何论坛帖子可以帮忙,这促使我分享我的经验。

下面是一些有希望的非常有用的示例代码的概述。如果这有帮助,请投我的答案!它值得我的时间:)

在iPhone上的应用程序下载Shibbolized资源几周,下面需要发生:

  1. 使用URL API的可可提交的HTTP请求有问题的资源。
  2. 的请求实现一个委托类:
  3. 响应的SP再直接对IdP(可可自动提供)
  4. 响应服务器证书信任挑战
  5. 响应用户的凭据挑战
  6. 响应错误具有两个参数
  7. 编程HTTP POST的两个参数(如果需要)
  8. 接收的IdP的“绑定模板”为已认证用户,HTML表单从而重新引导用户回SP从IdP回到SP。
  9. Cookies是自动存储和可可的前锋礼貌再次
  10. 实现第二URL请求委托接收最初请求数据。

下面是苹果和Shibboleth的一些有用的参考资料:

,希望我可以包括所有用于快速演示源。

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

不错,但它不会与基于表单的认证,其中你需要解析HTML或将其呈现给用户的交互工作。在我的情况下,用户需要迭代多个认证因素,所以我无法克服它。无论如何,我可以将您的示例翻译为C#,这是我开发iPhone的首选语言,并将其用作基础。谢谢 – Monoman

0

我成功实施了使用EC解决方案作为起点。我唯一要补充的是你一定要注意一次只保留一个请求。在我们的实现中,身份验证过程会在多个同时运行的异步请求之间混淆。使用NSOperation来扼杀队列似乎对我很好。

0

我设法做到了这一点,但花了我一些时间来了解整个过程的每一步并将其完美地重现。如果我有时间,我可能会写一个详细的教程,因为我没有找到很多我有问题的任何帮助。问题是,这也取决于你想连接到网站上,所以你也许像我(它的过程是一样的一个描述here)不按照相同的路径。

要查看我的浏览器(Chrome)发起的每个连接请求,我使用了开发人员工具网络面板,并选中了“保留日志”。

一些提示:

  • 1°),你需要得到 “_idp_authn_lc_key ......” 饼干。有一个请求为你设置,找到它。

  • 2°)您需要登录票据(LT -...)。您可能会在页面正文中找到询问您的凭据的信息。

  • 3°)您需要一张服务票据(ST -...)。同样,您会在页面中找到前一个请求返回的内容。

  • 4°)您需要SAMLResponse。同样,您会在页面中找到前一个请求返回的内容。

  • 5°)最后,您可以通过将SAMLResponse发送回服务提供商来登录。你应该照顾编码,在这里。我有几个“+”或“=”我需要更改为“%2B”和“%3D”。你将得到一个“_idp_session”的cookie,这将让你没有这一切混乱重新连接。

如果有人试图做同样的事情,我很乐意提供帮助!给我发一条消息。

相关问题