使用iPhone UIWebView的Asp.Net Forms身份validation

我正在写一个使用表单身份validation的Asp.net MVC 2应用程序,目前我在通过Web进行身份validation/login时遇到了iPhone应用程序的问题。 我们开发了一个使用UIWebView控件的简单iPhone应用程序。 在这个阶段,所有的应用程序都导航到我们的Asp.Net网站。 很简单,对吧? 问题是,用户无法越过login页面。 repro步骤是:

  • 打开iPhone应用程序。
  • 该应用程序导航到主页。
  • 用户没有被authentication,所以他们被redirect到login屏幕/页面
  • 用户input正确的用户名和密码。 点击提交。
  • 在服务器端,用户通过身份validation,生成一个cookie,并使用FormsAuthentication.GetAuthCookie发送给客户端。
  • 服务器发送redirect将用户发送到正确的主页。

但用户被redirect回login屏幕!

我已经做了一些广泛的debugging,我知道的是:

Cookie正在发送给客户端,客户端正在存储Cookie。 在iPhonedebugging器中validation了这一点,并使用Javsascript在页面上显示cookie数据。 该cookie正在被发送回服务器。 在Visual Studiodebugging器中validation了这一点。 这是正确的cookie(这是设置相同的)。 即使auth cookie包含在Request对象中,User.Identity.IsAuthenticated属性也会由于某种原因返回false。 我已经validationiPhone应用程序设置为接受cookie,并且它们在客户端上。

这是有趣的事情:如果你打开iPhone上的Safari浏览器,并直接进入我们的网站,它工作正常。

它在iPad上也有相同的行为,因为它不会超越login屏幕。 这个仿真器和设备上的repros。

这个网站已经testingIE 7-8,Safari(Windows),黑莓,IEMobile 6.5,电话7,它的工作发现。 唯一不适用的情况是iPhone应用程序中的UIWebView。

我有完全相同的问题,但与另一个设备(诺基亚N8),并将问题追溯到用户代理。

IIS使用正则expression式来匹配User-Agentstring。 问题的根源在于它没有任何匹配的特定设备的正则expression式,并且在使用Default属性的最低匹配级别之一结束。 默认属性表示浏览器不支持cookies。

解:

  1. 在名为App_Browsers的Web项目中添加一个文件夹(右键单击项目,select: Add > Add ASP.NET Folder > App_Browsers )。
  2. 在该文件夹中添加文件(右键单击,select: Add > New Item )。 该文件可以有任何名称,但必须有.browser结尾。
  3. 添加一个良好的匹配expression式和正确的function(或将更改添加到Default )。

两个例子:

 <browsers> <browser id="NokiaN8" parentID="Mozilla"> <identification> <userAgent match="NokiaN8" /> </identification> <capabilities> <capability name="browser" value="NokiaN8" /> <capability name="cookies" value="true" /> </capabilities> </browser> </browsers> 

或者更改默认值:

 <browsers> <browser refID="Default"> <capabilities> <capability name="cookies" value="true" /> </capabilities> </browser> </browsers> 

更多信息: 浏览器定义文件模式

我们find的解决scheme是创build一个文件(generic.browser),并包含这个XML告诉Web服务器,“Mozilla”和默认浏览器设置应该都支持cookies。

 <browser refID="Mozilla" > <capabilities> <capability name="cookies" value="true" /> </capabilities> </browser> 

这在ASP.NET 4.5中得到了解决,所有的浏览器都支持cookies,所以不需要额外的.browser文件。

从我做的研究来看,你无法设置用户代理的原因是UIWebView在发送请求之前设置了用户代理的值,也就是说,在你从你的代码发出请求之后。

解决这个问题的诀窍是使用一种叫做“方法调配”的方法,这是一种高级的,有潜在危险的Objective-C概念,用你提供的方法来replace标准方法。 最终的结果是,当你的请求被发送出去,并且框架代码添加了User-Agent的时候,它将被愚弄到你使用的方法。

下面解释我做了什么来实现这一点,但我不是Objective-C的专家,并build议你做一些研究,以熟悉自己的技术。 特别是那里有一个链接,比我说的更好,但是目前我找不到。

1)在NSObject上添加一个类别以允许混合。

 @interface NSObject (Swizzle) + (BOOL) swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector; @end @implementation NSObject (Swizzle) + (BOOL) swizzleMethod:(SEL) origSelector withMethod:(SEL)newSelector { Method origMethod= class_getInstanceMethod(self, origSelector); Method newMethod= class_getInstanceMethod(self, newSelector); if (origMethod && newMethod) { if (class_addMethod(self, origSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { class_replaceMethod(self, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); } else { method_exchangeImplementations(origMethod, newMethod); } return YES; } return NO; } @end 

2)子类NSMutableURLRequest允许swizzle:

 @interface NSMutableURLRequest (MyMutableURLRequest) + (void) setupUserAgentOverwrite; @end @implementation NSMutableURLRequest (MyMutableURLRequest) - (void) newSetValue:(NSString*)value forHTTPHeaderField:(NSString*)field { if ([field isEqualToString:@"User-Agent"]) { value = USER_AGENT; // ie, the value I want to use. } [self newSetValue:value forHTTPHeaderField:field]; } + (void) setupUserAgentOverwrite { [self swizzleMethod:@selector(setValue:forHTTPHeaderField:) withMethod:@selector(newSetValue:forHTTPHeaderField:)]; } @end 

3)调用静态方法来换出方法。 我在didFinishLaunchingWithOptions中做了这个调用:

 // Need to call this method so that User-Agent get updated correctly: [NSMutableURLRequest setupUserAgentOverwrite]; 

4)然后像这样使用它。 (连接委托将数据保存在可变数组中,然后在完成加载时使用loadData方法手动设置UIWebView)。

 - (void)loadWithURLString:(NSString*)urlString { NSURL *url = [NSURL URLWithString:urlString]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; _connection = [NSURLConnection connectionWithRequest:request delegate:self]; [_connection start]; } 

我有同样的问题,在这里研究和巩固一个完整的解决scheme(从上面的答案和其他线程): http : //www.bloggersworld.com/index.php/asp-net-forms-authentication-iphone-cookies/

  1. 你有没有在标记中指定DestinationPageUrl?

  2. 你有没有在web.config中指定defaultURL?

示例web.config

 <authentication mode="Forms"> <forms loginUrl="~/Login.aspx" defaultUrl="~/CustomerArea/Default.aspx"/> </authentication> 

示例DestinationPageUrl

  <asp:Login ID="Login" runat="server" DestinationPageUrl="~/Secret/Default.aspx" /> 

最后,你有没有在cookiesjar看看,如果你的会话cookie实际存在?

UIWebView的cookie在哪里存储?

造成这种情况的原因显然与这样一个事实有关,即如果用户代理不知道,那么浏览器被认为不接受cookie(正如其他人所回答的那样),而是IIS将ASPXAUTH值放在URL中。

然而,MVC路由系统显然错过了这种可能性,这显然是一个错误,因此它正在变得混乱。

虽然用自定义用户代理添加.browser解决了这个问题,但并不能保证其他的用户代理也能解决,事实上我发现android的K9浏览器也有这个问题,如果有一个像elmeh这样的日志系统来追踪这样的错误,那么这只是一个解决scheme。

另一方面,如果所有的浏览器都接受cookies,那么添加一个默认值就会出现这个问题,这显然是IIS不这么认为的原因。

然而,除了明确地添加用户代理之外,还可以在global.asaxRegiterRoutes()方法中添加一个显式处理程序来忽略它,如下所示:

  routes.MapRoute( "CookieLess", // Route name "(F({Cookie}))/{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); 

但是在这种情况下,除非要编写自定义路由处理程序,否则必须复制所有路由条目以匹配无Cookie的情况。

或者我们可以使用上面的无cookie路由将用户发送到一个错误页面,说明他的浏览器目前不受支持,并向用户代理发送一个警报给网站pipe理员处理。