将身份验证添加到 Android 应用Add authentication to your Android app

摘要Summary

本教程介绍如何使用支持的标识提供者将身份验证添加到 Android 上的待办事项列表快速入门项目。In this tutorial, you add authentication to the todolist quickstart project on Android by using a supported identity provider. 本教程基于 Get started with Mobile Apps (移动应用入门)教程,必须先完成该教程。This tutorial is based on the Get started with Mobile Apps tutorial, which you must complete first.

注册应用以进行身份验证并配置 Azure 应用服务Register your app for authentication and configure Azure App Service

首先,需要在标识提供者站点上注册应用,然后在移动应用后端设置提供者生成的凭据。First, you need to register your app at an identity provider's site, and then you will set the provider-generated credentials in the Mobile Apps back end.

  1. 请按照以下提供者特定的说明来配置首选标识提供者:Configure your preferred identity provider by following the provider-specific instructions:

  2. 为要在应用中支持的各提供者重复上述步骤。Repeat the previous steps for each provider you want to support in your app.

将应用添加到允许的外部重定向 URLAdd your app to the Allowed External Redirect URLs

安全身份验证要求为应用定义新的 URL 方案。Secure authentication requires that you define a new URL scheme for your app. 此方案允许在完成身份验证过程后,身份验证系统重定向到应用。This allows the authentication system to redirect back to your app once the authentication process is complete. 在本教程中,我们自始至终使用 URL 方案 appnameIn this tutorial, we use the URL scheme appname throughout. 但是,可以使用任何你所选的 URL 方案。However, you can use any URL scheme you choose. 对于移动应用程序而言,它应是唯一的。It should be unique to your mobile application. 在服务器端启用重定向:To enable the redirection on the server side:

  1. Azure 门户中,选择应用服务。In the Azure portal, select your App Service.

  2. 单击“身份验证/授权” 菜单选项。Click the Authentication / Authorization menu option.

  3. 在“允许的外部重定向 URL” 中,输入 appname://easyauth.callbackIn the Allowed External Redirect URLs, enter appname://easyauth.callback. 此字符串中的 appname 是移动应用程序的 URL 方案。The appname in this string is the URL Scheme for your mobile application. 它应该遵循协议的正常 URL 规范(仅使用字母和数字,并以字母开头)。It should follow normal URL specification for a protocol (use letters and numbers only, and start with a letter). 应记下此字符串,因为在一些地方需要使用此 URL 方案调整移动应用代码。You should make a note of the string that you choose as you will need to adjust your mobile application code with the URL Scheme in several places.

  4. 单击 “确定”Click OK.

  5. 单击“保存” 。Click Save.

将权限限制给已经过身份验证的用户Restrict permissions to authenticated users

默认情况下,可匿名调用移动应用后端中的 API。By default, APIs in a Mobile Apps back end can be invoked anonymously. 接下来,需限制为仅可访问已验证的客户端。Next, you need to restrict access to only authenticated clients.

  • Node.js 后端(通过 Azure 门户)Node.js back end (via the Azure portal) :

    在移动应用设置中,单击“简易表” 并选择相应的表。In your Mobile Apps settings, click Easy Tables and select your table. 单击“更改权限” ,为所有权限选择“仅限已验证的访问” ,并单击“保存” 。Click Change permissions, select Authenticated access only for all permissions, and then click Save.

  • .NET 后端 (C#) :.NET back end (C#):

    在服务器项目中,导航到“控制器” > “TodoItemController.cs”。 In the server project, navigate to Controllers > TodoItemController.cs. [Authorize] 属性添加到“TodoItemController”类,如下所示。 Add the [Authorize] attribute to the TodoItemController class, as follows. 若要限制为仅可访问特定方法,还可只向这些方法应用此属性(而非类)。To restrict access only to specific methods, you can also apply this attribute just to those methods instead of the class. 重新发布服务器项目。Republish the server project.

    [Authorize]
    public class TodoItemController : TableController<TodoItem>
    
  • Node.js 后端(通过 Node.js 代码)Node.js backend (via Node.js code) :

    若要访问表时需验证身份,请向 Node.js 服务器脚本添加以下行:To require authentication for table access, add the following line to the Node.js server script:

    table.access = 'authenticated';
    

    有关更多详细信息,请参阅如何:要求在访问表时进行身份验证For more details, see How to: Require authentication for access to tables. 若要了解如何从站点下载快速入门代码项目,请参阅如何:使用 Git 下载 Node.js 后端快速入门代码项目To learn how to download the quickstart code project from your site, see How to: Download the Node.js backend quickstart code project using Git.

  • 在 Android Studio 中,打开你按照Get started with Mobile Apps教程完成的项目。In Android Studio, open the project you completed with the tutorial Get started with Mobile Apps. 从“运行” 菜单中单击“运行应用” ;验证启动该应用后,是否会引发状态代码为 401(“未授权”)的未经处理的异常。From the Run menu, click Run app, and verify that an unhandled exception with a status code of 401 (Unauthorized) is raised after the app starts.

    发生此异常的原因是应用尝试以未经身份验证的用户身份访问后端,但 TodoItem 表现在要求身份验证。This exception happens because the app attempts to access the back end as an unauthenticated user, but the TodoItem table now requires authentication.

接下来,需要更新应用,以便在从移动应用后端请求资源之前对用户进行身份验证。Next, you update the app to authenticate users before requesting resources from the Mobile Apps back end.

向应用程序添加身份验证Add authentication to the app

  1. 在 Android Studio 中打开项目。Open the project in Android Studio.

  2. 在 Android Studio 的“项目资源管理器”中,打开 ToDoActivity.java 文件,并添加以下 import 语句。In Project Explorer in Android Studio, open the ToDoActivity.java file and add the following import statements.

     ```java
     import java.util.concurrent.ExecutionException;
     import java.util.concurrent.atomic.AtomicBoolean;
    
     import android.content.Context;
     import android.content.SharedPreferences;
     import android.content.SharedPreferences.Editor;
    
     import com.microsoft.windowsazure.mobileservices.authentication.MobileServiceAuthenticationProvider;
     import com.microsoft.windowsazure.mobileservices.authentication.MobileServiceUser;
     ```
    
  3. 将以下方法添加到 ToDoActivity 类:Add the following method to the ToDoActivity class:

     ```java
     // You can choose any unique number here to differentiate auth providers from each other. Note this is the same code at login() and onActivityResult().
     public static final int GOOGLE_LOGIN_REQUEST_CODE = 1;
    
     private void authenticate() {
         // Sign in using the Google provider.
         mClient.login(MobileServiceAuthenticationProvider.Google, "{url_scheme_of_your_app}", GOOGLE_LOGIN_REQUEST_CODE);
     }
    
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         // When request completes
         if (resultCode == RESULT_OK) {
             // Check the request code matches the one we send in the login request
             if (requestCode == GOOGLE_LOGIN_REQUEST_CODE) {
                 MobileServiceActivityResult result = mClient.onActivityResult(data);
                 if (result.isLoggedIn()) {
                     // sign-in succeeded
                     createAndShowDialog(String.format("You are now signed in - %1$2s", mClient.getCurrentUser().getUserId()), "Success");
                     createTable();
                 } else {
                     // sign-in failed, check the error message
                     String errorMessage = result.getErrorMessage();
                     createAndShowDialog(errorMessage, "Error");
                 }
             }
         }
     }
    

    此代码会创建一个用于处理 Google 身份验证过程的方法。This code creates a method to handle the Google authentication process. 出现的对话框中会显示已经过身份验证的用户 ID。A dialog displays the ID of the authenticated user. 只能在身份验证成功后继续操作。You can only proceed on a successful authentication.

    Note

    如果使用的标识提供者不是 Google,请将传递给 login 方法的值更改为下列值之一:MicrosoftAccountFacebookTwitterwindowsazureactivedirectoryIf you are using an identity provider other than Google, change the value passed to the login method to one of the following values: MicrosoftAccount, Facebook, Twitter, or windowsazureactivedirectory.

  4. onCreate 方法中,在实例化 MobileServiceClient 对象的代码后面添加以下代码行。In the onCreate method, add the following line of code after the code that instantiates the MobileServiceClient object.

     authenticate();
    

    此调用启动身份验证过程。This call starts the authentication process.

  5. onCreate 方法中 authenticate(); 后面的剩余代码移到新的 createTable 方法。Move the remaining code after authenticate(); in the onCreate method to a new createTable method. 其应如下所示:This looks like the following:

     private void createTable() {
    
         // Get the table instance to use.
         mToDoTable = mClient.getTable(ToDoItem.class);
    
         mTextNewToDo = (EditText) findViewById(R.id.textNewToDo);
    
         // Create an adapter to bind the items with the view.
         mAdapter = new ToDoItemAdapter(this, R.layout.row_list_to_do);
         ListView listViewToDo = (ListView) findViewById(R.id.listViewToDo);
         listViewToDo.setAdapter(mAdapter);
    
         // Load the items from Azure.
         refreshItemsFromTable();
     }
    
  6. 将下面的 RedirectUrlActivity 代码片段添加到 AndroidManifest.xml,确保可以重定向。Add the following snippet of RedirectUrlActivity to AndroidManifest.xml to ensure redirect works.

     <activity android:name="com.microsoft.windowsazure.mobileservices.authentication.RedirectUrlActivity">
         <intent-filter>
             <action android:name="android.intent.action.VIEW" />
             <category android:name="android.intent.category.DEFAULT" />
             <category android:name="android.intent.category.BROWSABLE" />
             <data android:scheme="{url_scheme_of_your_app}"
                 android:host="easyauth.callback"/>
         </intent-filter>
     </activity>
    
  7. 将 redirectUriScheme 添加到 Android 应用程序的 build.gradleAdd redirectUriScheme to build.gradle of your Android application.

    android {
        buildTypes {
            release {
                // … …
                manifestPlaceholders = ['redirectUriScheme': '{url_scheme_of_your_app}://easyauth.callback']
            }
            debug {
                // … …
                manifestPlaceholders = ['redirectUriScheme': '{url_scheme_of_your_app}://easyauth.callback']
            }
        }
    }
    
  8. 将 com.android.support:customtabs:23.0.1 添加到 build.gradle 中的依赖项:Add com.android.support:customtabs:23.0.1 to the dependencies in your build.gradle:

    依赖项 { // ... 编译“com.android.support:customtabs:23.0.1” }dependencies { // ... compile 'com.android.support:customtabs:23.0.1' }

  9. 然后,从“运行”菜单中单击“运行应用”启动应用,并使用所选的标识提供者登录。From the Run menu, click Run app to start the app and sign in with your chosen identity provider.

成功登录后,应用应可以正常运行,并且你应能查询后端服务并对数据进行更新。When you are successfully signed in, the app should run without errors, and you should be able to query the back-end service and make updates to data.

在客户端上缓存身份验证令牌Cache authentication tokens on the client

上一示例介绍了标准登录过程,此过程要求在应用每次启动时客户端同时联系标识提供者和后端 Azure 服务。The previous example showed a standard sign-in, which requires the client to contact both the identity provider and the back-end Azure service every time the app starts. 此方法效率较低,并且许多用户同时启动应用时可能产生使用率相关方面的问题。This method is inefficient, and you can have usage-related issues if many customers try to start your app simultaneously. 更好的方法是缓存 Azure 服务返回的授权令牌,并在使用基于提供者的登录之前首先尝试使用此令牌。A better approach is to cache the authorization token returned by the Azure service, and try to use this first before using a provider-based sign-in.

Note

无论使用客户端管理的还是服务管理的身份验证,都可以缓存由后端 Azure 服务颁发的令牌。You can cache the token issued by the back-end Azure service regardless of whether you are using client-managed or service-managed authentication. 本教程使用服务管理的身份验证。This tutorial uses service-managed authentication.

  1. 打开 ToDoActivity.java 文件并添加以下 import 语句:Open the ToDoActivity.java file and add the following import statements:

     ```java
     import android.content.Context;
     import android.content.SharedPreferences;
     import android.content.SharedPreferences.Editor;
     ```
    
  2. 将以下成员添加到 ToDoActivity 类。Add the following members to the ToDoActivity class.

     ```java
     public static final String SHAREDPREFFILE = "temp";    
     public static final String USERIDPREF = "uid";    
     public static final String TOKENPREF = "tkn";    
     ```
    
  3. 在 ToDoActivity.java 文件中,为 cacheUserToken 方法添加下面的定义。In the ToDoActivity.java file, add the following definition for the cacheUserToken method.

     ```java
     private void cacheUserToken(MobileServiceUser user)
     {
         SharedPreferences prefs = getSharedPreferences(SHAREDPREFFILE, Context.MODE_PRIVATE);
         Editor editor = prefs.edit();
         editor.putString(USERIDPREF, user.getUserId());
         editor.putString(TOKENPREF, user.getAuthenticationToken());
         editor.commit();
     }    
     ```
    

    此方法将用户 ID 和令牌存储在标记为私有的首选项文件中。This method stores the user ID and token in a preference file that is marked private. 这可保护对缓存的访问,这样设备上的其他应用便无权访问此令牌。This should protect access to the cache so that other apps on the device do not have access to the token. 因为应用的首选项已经过沙盒处理。The preference is sandboxed for the app. 但是,如果有人获取了设备的访问权,则它们可能会通过其他方式获得对令牌缓存的访问权。However, if someone gains access to the device, it is possible that they may gain access to the token cache through other means.

    Note

    如果使用令牌访问的数据非常敏感,并且有人可能会获得设备的访问权限,则可以使用加密进一步保护令牌。You can further protect the token with encryption, if token access to your data is considered highly sensitive and someone may gain access to the device. 但是,完全安全的解决方案超出了本教程的范围并且取决于具体的安全要求。A completely secure solution is beyond the scope of this tutorial, however, and depends on your security requirements.

  4. 在 ToDoActivity.java 文件中,为 loadUserTokenCache 方法添加下面的定义。In the ToDoActivity.java file, add the following definition for the loadUserTokenCache method.

     ```java
     private boolean loadUserTokenCache(MobileServiceClient client)
     {
         SharedPreferences prefs = getSharedPreferences(SHAREDPREFFILE, Context.MODE_PRIVATE);
         String userId = prefs.getString(USERIDPREF, null);
         if (userId == null)
             return false;
         String token = prefs.getString(TOKENPREF, null);
         if (token == null)
             return false;
    
         MobileServiceUser user = new MobileServiceUser(userId);
         user.setAuthenticationToken(token);
         client.setCurrentUser(user);
    
         return true;
     }
     ```
    
  5. ToDoActivity.java 文件中,将 authenticateonActivityResult 方法替换为下面这种使用令牌缓存的方法。In the ToDoActivity.java file, replace the authenticate and onActivityResult methods with the following ones, which uses a token cache. 如果要使用的帐户不是 Google 帐户,请更改登录提供者。Change the login provider if you want to use an account other than Google.

     ```java
     private void authenticate() {
         // We first try to load a token cache if one exists.
         if (loadUserTokenCache(mClient))
         {
             createTable();
         }
         // If we failed to load a token cache, signin and create a token cache
         else
         {
             // Sign in using the Google provider.
             mClient.login(MobileServiceAuthenticationProvider.Google, "{url_scheme_of_your_app}", GOOGLE_LOGIN_REQUEST_CODE);
          }
       }           
    
       @Override
       protected void onActivityResult(int requestCode, int resultCode, Intent data) {
           // When request completes
           if (resultCode == RESULT_OK) {
               // Check the request code matches the one we send in the sign-in request
               if (requestCode == GOOGLE_LOGIN_REQUEST_CODE) {
                  MobileServiceActivityResult result = mClient.onActivityResult(data);
                  if (result.isLoggedIn()) {
                     // sign-in succeeded
                     createAndShowDialog(String.format("You are now signed in - %1$2s", mClient.getCurrentUser().getUserId()), "Success");
                     cacheUserToken(mClient.getCurrentUser());
                     createTable();
                  } else {
                      // sign-in failed, check the error message
                      String errorMessage = result.getErrorMessage();
                      createAndShowDialog(errorMessage, "Error");
                  }
             }
        }
    }
    ```
    
  6. 构建此应用并使用有效帐户来测试验证。Build the app and test authentication using a valid account. 至少两次运行它。Run it at least twice. 在第一次运行期间,应会收到要求登录并创建令牌缓存的提示。During the first run, you should receive a prompt to sign in and create the token cache. 之后,每次运行时,系统都会尝试加载用于身份验证的令牌缓存。After that, each run attempts to load the token cache for authentication. 而你无需再次登录。You should not be required to sign in.

后续步骤Next steps

完成此基本身份验证教程后,请考虑继续学习以下教程之一:Now that you completed this basic authentication tutorial, consider continuing on to one of the following tutorials:

  • 为 Android 应用启用脱机同步Enable offline sync for your Android app. 了解如何使用移动应用后端向应用添加脱机支持。Learn how to add offline support to your app by using a Mobile Apps back end. 使用脱机同步,用户可以与移动应用进行交互—查看、添加或修改数据—,即使在没有网络连接时也是如此。With offline sync, users can interact with a mobile app—viewing, adding, or modifying data—even when there is no network connection.