如何在iPhone应用程序中处理身份验证

时间:2023-01-12 20:11:04

I'm currently developing my first native iPhone app (though I have many years of experience as a web developer). I'm having some difficulty understanding the best way to handle login and I'm looking for some advice on the best way to go about it. The more I think about all the things that can go wrong during login, the more my brain wants to jump out of my head. I'm getting really frustrated with this and could really use some advice from some more experienced iPhone developers. Thanks in advance for your help.

我目前正在开发我的第一个本地iPhone应用程序(尽管我有多年的web开发经验)。我在理解处理登录的最佳方式上遇到了一些困难,我正在寻找一些关于处理登录的最佳方式的建议。我越想登录时可能出错的地方,我的大脑就越想跳出来。我真的很沮丧,我真的可以从一些更有经验的iPhone开发者那里得到一些建议。谢谢你的帮助。

My goal is to support Facebook Connect in the first version of the app, and then to support other SSO services (Twitter, Google, etc.), as well as my own user account system in future versions. The current plan is to have a MySQL table on the server that looks something like this:

我的目标是在应用程序的第一个版本中支持Facebook Connect,然后支持其他SSO服务(Twitter、谷歌等等),以及我自己的用户帐户系统在以后的版本中。目前的计划是在服务器上有一个MySQL表,看起来像这样:

users (id, nickname, facebook_id, ...)

When a user logs into the app via Facebook for the first time, an entry will be created in this table for them. You may think this isn't necessary, but it will allow me to expand to other services later on. For example, I could do this:

当用户第一次通过Facebook登录到应用程序时,将在该表中为他们创建一个条目。您可能认为这不是必需的,但它将允许我稍后扩展到其他服务。例如,我可以这样做:

users (id, nickname, facebook_id, twitter_id, google_id, username, ...)

This table would have nullable fields for facebook_id, twitter_id, google_id, and username. If the user logs in with facebook, they'll have a facebook_id. Twitter users will have a twitter_id, Google users a google_id, and my own users will have a username. They'll all be uniquely identified by my own id regardless of what login system they're using.

这个表将包含facebook_id、twitter_id、google_id和用户名的空字段。如果用户登录facebook,他们将有一个facebook_id。Twitter用户将有一个twitter_id,谷歌用户将有一个google_id,我自己的用户将有一个用户名。不管他们使用的是什么登录系统,它们都将通过我自己的id进行唯一标识。

So I'm pretty comfortable with the back-end implementation of user accounts. I can setup a web service that the app can call to create/retrieve users, verify logins, etc. No problem.

所以我对用户帐户的后端实现非常满意。我可以设置一个web服务,应用程序可以调用它来创建/检索用户,验证登录等等。

The problem I'm having is implementing a proper login flow with the iPhone UI components. My particular app uses a UITabBarController that serves as the main navigation. One of the tabs is labeled "My Account" and contains information about the currently logged in user. If the user clicks on the "My Account" tab, they are presented with a table view that serves as a submenu. It has options such as "My Profile", "Settings", and some other things. If they click on any of these menu items and they aren't logged in, then I use the presentViewController function to pop up a login screen. They click "login with facebook" and go through the typical Facebook authorization process. When they've completed that process, I use dismissViewController to remove the login page and display the page they were trying to access. If they cancel the login or if the login fails, then I use popViewControllerAnimated on the UINavigationController to send them back up to the "My Account" submenu. For those of you who are having a difficult time envisioning this, check out the Amazon app. It is almost the exact same thing (just click the "More" tab when you're not logged in and try to click one of the menu items underneath it).

我遇到的问题是使用iPhone UI组件实现正确的登录流。我的应用程序使用UITabBarController作为主要导航。其中一个选项卡被标记为“My Account”,并包含有关当前登录用户的信息。如果用户单击“My Account”选项卡,将显示作为子菜单的表视图。它有“我的个人资料”、“设置”等选项。如果他们点击任何一个菜单项,而他们没有登录,那么我使用presentViewController函数弹出一个登录屏幕。他们点击“登录facebook”,然后进行典型的facebook授权过程。当他们完成这个过程后,我使用dismissViewController来删除登录页面并显示他们试图访问的页面。如果他们取消登录,或者登录失败,那么我使用UINavigationController上的popViewControllerAnimated将它们发送回“My Account”子菜单。对于那些很难想象到这一点的人来说,看看亚马逊的应用吧。它几乎是完全一样的东西(当你没有登录时,点击“更多”选项卡,然后尝试点击它下面的菜单项)。

That all works pretty well and I'm happy with it. But here's where I get confused:

这些都很好,我很满意。但这里我感到困惑:

What the heck do I do if they're several levels deep into the UINavigationController within the "My Account" tab and their login session expires?

如果他们在UINavigationController的“我的账户”选项卡中登录会话过期,我该怎么办呢?

Let's take Facebook login for example. Facebook uses session tokens to keep users logged in. The tokens expire after a certain amount of time. Let's say the user navigated down into "My Account", then clicked "My Profile", and then clicked on "Edit" and are shown a screen where they can edit their profile information. So they obviously need to be authenticated in order to view this page. In fact, they're 2-3 levels deep into pages that they need to be authenticated to see. Now let's say they get interrupted by a phone call or something and forget all about what they were doing. The next time they access the app is a week later when their login session has expired. I can handle this in a few ways. None of them seem great to me.

以Facebook登录为例。Facebook使用会话令牌来保持用户登录。令牌在一定时间后过期。假设用户导航到“My Account”,然后单击“My Profile”,然后单击“Edit”,屏幕上显示他们可以编辑个人信息。因此,要查看这个页面,显然需要对它们进行身份验证。事实上,他们有2-3层的深度,需要通过身份验证才能看到。现在,假设他们被电话打断了,忘记了他们在做什么。他们下一次访问应用程序是在一个星期后,当他们的登录会话已经过期。我可以用几种方法来处理。我觉得他们都不怎么样。

Solution #1

The Facebook SDK will automatically call a method on the AppDelegate class that notifies me of the expired session. Since I am notified of the session expiration at the AppDelegate level, I have no idea what page the user is currently looking at and whether or not they need to be authenticated in order to use it. To get around this, I can have all ViewControllers that require login to extend a "ProtectedViewController" class or something that indicates the user should be logged in to see that page. Then when the AppDelegate is notified of the session expiration, it will try to figure out what the current ViewController is and check if it extends "ProtectedViewController". If it does, then present a login screen. If the user successfully logs in, then everything proceeds as normal. If not, then return the user to the first screen of the app where they have to start all over. This is bad because the user will lose anything they've typed in already, but I don't see any way to avoid it with this solution.

Facebook SDK将自动调用AppDelegate类上的一个方法,该方法会通知我过期的会话。由于在AppDelegate级别会通知我会话过期,所以我不知道用户正在查看哪个页面,也不知道用户是否需要通过身份验证才能使用它。为了解决这个问题,我可以让所有的ViewControllers都需要登录来扩展一个“ProtectedViewController”类,或者一些指示用户应该登录到该页面的内容。然后,当AppDelegate被通知会话过期时,它会尝试找出当前的ViewController是什么,并检查它是否扩展了“ProtectedViewController”。如果是,那么显示一个登录屏幕。如果用户成功登录,则一切照常进行。如果没有,那么将用户返回到应用程序的第一个屏幕,在那里他们必须从头开始。这很糟糕,因为用户将会丢失他们已经输入的任何内容,但是我没有办法用这个解决方案来避免它。

Solution #2

Ignore the session expiration event at the AppDelegate level and instead do this: before any action is taken that requires a user to be logged in (e.g. when the user clicks "Save" on their "Edit Profile" page), check if they are still logged in. If they aren't, then present a login screen. If the user fails to login, then send them back to the start screen. This solution is a pain in the ass to code because I have to perform a check on practically everything the user does within the protected area of the app -- when they view a page, when they click a button -- just about everything.

忽略AppDelegate级别的会话过期事件,而是这样做:在执行任何需要用户登录的操作之前(例如,当用户单击“Edit Profile”页面上的“Save”时),检查用户是否仍然登录。如果没有,则显示一个登录屏幕。如果用户未能登录,则将其发送回开始屏幕。这个解决方案对代码来说是一种痛苦,因为我必须对用户在应用程序的保护区域内做的所有事情进行检查——当他们查看页面时,当他们点击一个按钮时——几乎所有的事情。

I would also prefer to avoid sending the user all the way back to the start screen of the app if they fail to re-authenticate. Instead, in this case, I'd prefer to send the user back up the UINavigationController to the "My Account" menu -- which is the closest page that doesn't require login. Sure, I could hardcode that, but I'm looking for a solution/pattern that works a little more naturally and that I can reuse in other apps.

如果用户无法重新验证,我也希望避免将用户一直发送回应用程序的开始屏幕。相反,在这种情况下,我宁愿将用户发送回UINavigationController到“我的账户”菜单——这是最接近的不需要登录的页面。当然,我可以对它进行硬编码,但我正在寻找一种更自然的解决方案/模式,以便在其他应用程序中重用。

I would really appreciate some guidance. Surely I'm not the first person in the world who has needed to solve this problem. Unfortunately, Google hasn't been much help.

我非常感谢您的指导。当然,我不是世界上第一个需要解决这个问题的人。不幸的是,谷歌并没有多大帮助。

Thanks.

谢谢。

EDIT: Another idea is to subclass UIViewController (e.g. "ProtectedViewController") and implement the "viewWillAppear" method. Inside this method, I can check if the user is logged in. If not, then I slide up a login page. I still don't know how to handle the case when they fail to login, though. This solution has a problem, though: if the user's session expires while they're using the app, then I won't re-authenticate them until the next time they click on a new view. If they're already looking at say an "edit" page and click the "save" button, then they won't be re-authenticated. But perhaps this is a step closer to the solution.

编辑:另一个想法是子类化UIViewController(例如。“ProtectedViewController”)并实现“viewWillAppear”方法。在此方法中,我可以检查用户是否已登录。如果没有,那么我将滑上一个登录页面。但是,当他们无法登录时,我仍然不知道该如何处理。这个解决方案有一个问题:如果用户的会话在使用应用程序时过期,那么我在下次他们点击新视图之前不会对他们重新进行身份验证。如果他们已经在查看“编辑”页面并单击“保存”按钮,那么他们将不会被重新验证。但这或许是离解决方案更近的一步。

2 个解决方案

#1


1  

Don't forget that the app delegate is the one that adds the whole tab bar to the main UIWindow. On detection of credentials failing, you could simply remove the UITabBarController from the UIWindow, and replace it only with your own login view to re-authenticate. That eliminates any possibility they could interact with anything in the tab structure, but when restored means whatever position they are at within the tabs is preserved (since you would simply remove the tab bar controllers view but leave the controller intact).

别忘了app委托是将整个标签栏添加到主UIWindow的那个。在检测凭据失败时,您可以简单地从UIWindow中删除UITabBarController,并只将其替换为您自己的登录视图来重新验证。这就消除了它们与选项卡结构中的任何内容交互的可能性,但当恢复时,则意味着它们在选项卡中的任何位置都将被保留(因为您只需删除选项卡栏控制器视图,但保留控制器的完整)。

#2


0  

Here how I managed it in a recent project using singletons.

下面是我在最近的一个项目中使用单例的管理方式。

Create a singleton class, say LoginManager that has a method called,

创建一个单例类,比如LoginManager它有一个方法,

-(UserInfo*) getValidatedUser: (UIView*) senderView

Inside this method check to see if the token is still valid.

在此方法中检查标记是否仍然有效。

If it is not valid simply create a new view that forces the user to login using FB credentials and overlay it on top of the "senderView" so that the user is forced to login, like this:

如果它不是有效的,只需创建一个新视图,强制用户使用FB凭证登录,并将其覆盖在“senderView”之上,以便用户*登录,如下所示:

[senderView addSubview:loginView]; 

Once the token is valid, you return back the user information.

一旦令牌有效,您将返回用户信息。

With this basic logic in place you can now call this getValidatedUser method from your view controller classes whenever you need a valid credential to do something:

有了这个基本的逻辑,您现在可以在需要有效凭据时从视图控制器类调用getValidatedUser方法:

UserInfo* myUser = [loginManagerObj  getValidatedUser:self.view]

The method internally decides if a login page should be shown or not. Hope this helps.

该方法在内部决定是否应该显示登录页面。希望这个有帮助。

#1


1  

Don't forget that the app delegate is the one that adds the whole tab bar to the main UIWindow. On detection of credentials failing, you could simply remove the UITabBarController from the UIWindow, and replace it only with your own login view to re-authenticate. That eliminates any possibility they could interact with anything in the tab structure, but when restored means whatever position they are at within the tabs is preserved (since you would simply remove the tab bar controllers view but leave the controller intact).

别忘了app委托是将整个标签栏添加到主UIWindow的那个。在检测凭据失败时,您可以简单地从UIWindow中删除UITabBarController,并只将其替换为您自己的登录视图来重新验证。这就消除了它们与选项卡结构中的任何内容交互的可能性,但当恢复时,则意味着它们在选项卡中的任何位置都将被保留(因为您只需删除选项卡栏控制器视图,但保留控制器的完整)。

#2


0  

Here how I managed it in a recent project using singletons.

下面是我在最近的一个项目中使用单例的管理方式。

Create a singleton class, say LoginManager that has a method called,

创建一个单例类,比如LoginManager它有一个方法,

-(UserInfo*) getValidatedUser: (UIView*) senderView

Inside this method check to see if the token is still valid.

在此方法中检查标记是否仍然有效。

If it is not valid simply create a new view that forces the user to login using FB credentials and overlay it on top of the "senderView" so that the user is forced to login, like this:

如果它不是有效的,只需创建一个新视图,强制用户使用FB凭证登录,并将其覆盖在“senderView”之上,以便用户*登录,如下所示:

[senderView addSubview:loginView]; 

Once the token is valid, you return back the user information.

一旦令牌有效,您将返回用户信息。

With this basic logic in place you can now call this getValidatedUser method from your view controller classes whenever you need a valid credential to do something:

有了这个基本的逻辑,您现在可以在需要有效凭据时从视图控制器类调用getValidatedUser方法:

UserInfo* myUser = [loginManagerObj  getValidatedUser:self.view]

The method internally decides if a login page should be shown or not. Hope this helps.

该方法在内部决定是否应该显示登录页面。希望这个有帮助。