在没有Google帐户登录的情况下将您的客户端验证到Cloud Endpoints

Mic*_*cro 24 authentication google-app-engine android google-cloud-endpoints google-oauth

我一直在研究如何使用Cloud Endpoints验证您的客户端(Android,iOS,web-app),无需用户按照文档显示的方式使用他们的Google帐户登录.

这样做的原因是我想要保护我的API或"将其锁定"仅限于我指定的客户端.有时我会有一个没有用户登录的应用程序.我讨厌纠缠我的用户现在登录,因此我的API是安全的.或者其他时候,我只想在网站上管理自己的用户,而不是使用Google+,Facebook或其他任何登录身份验证.

首先,让我首先展示您使用Cloud Endpoints API使用文档中指定的Google帐户登录信息验证Android应用的方式.在那之后,我将向您展示我的发现以及我需要帮助的解决方案的潜在领域.

(1)指定授权向API后端发出请求的应用程序的客户端ID(clientIds),以及(2)将User参数添加到要通过授权保护的所有公开方法.

public class Constants {
      public static final String WEB_CLIENT_ID = "1-web-apps.apps.googleusercontent.com";
      public static final String ANDROID_CLIENT_ID = "2-android-apps.googleusercontent.com";
      public static final String IOS_CLIENT_ID = "3-ios-apps.googleusercontent.com";
      public static final String ANDROID_AUDIENCE = WEB_CLIENT_ID;

      public static final String EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email";
    }


import com.google.api.server.spi.auth.common.User; //import for the User object

    @Api(name = "myApi", version = "v1",
         namespace = @ApiNamespace(ownerDomain = "${endpointOwnerDomain}",
         ownerName = "${endpointOwnerDomain}",
         packagePath="${endpointPackagePath}"),
         scopes = {Constants.EMAIL_SCOPE}, 
         clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID,
                      Constants.IOS_CLIENT_ID,
                      Constants.API_EXPLORER_CLIENT_ID},
                      audiences = {Constants.ANDROID_AUDIENCE})

    public class MyEndpoint {

        /** A simple endpoint method that takes a name and says Hi back */
        @ApiMethod(name = "sayHi")
        public MyBean sayHi(@Named("name") String name, User user) throws UnauthorizedException {
            if (user == null) throw new UnauthorizedException("User is Not Valid");
            MyBean response = new MyBean();
            response.setData("Hi, " + name);

            return response;
        }

    } 
Run Code Online (Sandbox Code Playgroud)

(3)在Android中调用Asynctask中的API方法,确保传入以下credential变量Builder:

class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
        private static MyApi myApiService = null;
        private Context context;

        @Override
        protected String doInBackground(Pair<Context, String>... params) {
            credential = GoogleAccountCredential.usingAudience(this,
            "server:client_id:1-web-app.apps.googleusercontent.com");
            credential.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
            if(myApiService == null) {  // Only do this once
                MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
                        new AndroidJsonFactory(), credential)
                    // options for running against local devappserver
                    // - 10.0.2.2 is localhost's IP address in Android emulator
                    // - turn off compression when running against local devappserver
                    .setRootUrl("http://<your-app-engine-project-id-here>/_ah/api/")
                    .setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
                        @Override
                        public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
                            abstractGoogleClientRequest.setDisableGZipContent(true);
                        }
                    });
                    // end options for devappserver

                myApiService = builder.build();
            }

            context = params[0].first;
            String name = params[0].second;

            try {
                return myApiService.sayHi(name).execute().getData();
            } catch (IOException e) {
                return e.getMessage();
            }
        }

        @Override
        protected void onPostExecute(String result) {
            Toast.makeText(context, result, Toast.LENGTH_LONG).show();
        }
    }
Run Code Online (Sandbox Code Playgroud)

发生的事情是,在您的Android应用中,您首先显示Google帐户选择器,将Google帐户电子邮件存储在您的共享首选项中,然后将其设置为GoogleAccountCredential对象的一部分(有关如何执行操作的详细信息).

Google App Engine服务器会收到您的请求并进行检查.如果Android客户端是您在@Api表示法中指定的客户端之一,则服务器会将com.google.api.server.spi.auth.common.User对象注入您的API方法.现在,您有责任在API方法中检查该User对象是否null存在.如果User对象是null,则应在方法中引发异常以防止其运行.如果您不进行此检查,则会执行您的API方法(如果您尝试限制对它的访问,则禁止执行).

您可以ANDROID_CLIENT_ID转到Google Developers Console.在那里,您提供Android应用程序的包名称和SHA1,它为您生成一个Android客户端ID,供您在@Api注释中使用(或将其放在Constants类似上面指定的可用性类中).

我已经对上述所有内容进行了一些广泛的测试,这是我发现的:

如果您在@Api注释中指定了虚假或无效的Android clientId ,则该User对象将null位于您的API方法中.如果您正在检查,if (user == null) throw new UnauthorizedException("User is Not Valid");那么您的API方法将无法运行.

这是令人惊讶的,因为它似乎在Cloud Endpoints中进行了一些幕后验证,检查Android ClientId是否有效.如果它无效,它将不会返回该User对象 - 即使最终用户登录到他们的Google帐户并且该帐户GoogleAccountCredential有效.

我的问题是,有谁知道如何在我的Cloud Endpoints方法中自行检查那种类型的ClientId验证?HttpHeader例如,这些信息可以传播吗?

Cloud Endpoints中的另一种注入类型是javax.servlet.http.HttpServletRequest.您可以在API方法中获取此类请求:

@ApiMethod(name = "sayHi")
            public MyBean sayHi(@Named("name") String name, HttpServletRequest req) throws UnauthorizedException {

                String Auth = req.getHeader("Authorization");//always null based on my tests
                MyBean response = new MyBean();
                response.setData("Hi, " + name);

                return response;
            }

        }  
Run Code Online (Sandbox Code Playgroud)

但我不确定是否有必要的信息或如何获得它.

当然某处必须有一些数据告诉我们客户是否是经过授权和指定的客户@Api clientIds.

通过这种方式,您可以将API锁定到Android应用程序(以及可能的其他客户端),而无需纠缠最终用户登录(或只创建自己的简单用户名+密码登录).

尽管如此,你必须传入nullBuilder喜欢的第三个参数:

MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),new AndroidJsonFactory(),null)

然后在您的API方法中提取调用是否来自经过身份验证的客户端,并抛出异常或运行您想要的任何代码.

我知道这是可能的,因为当使用a GoogleAccountCredentialBuilder,不知何故,Cloud Endpoints知道该调用是否来自经过身份验证的客户端,然后根据该User方法将其对象注入API方法.

这些信息可能会以某种方式出现在标题或正文中吗?如果是这样,我怎样才能把它拿出来以后检查我的API方法中是否存在?

注意:我阅读了有关此主题的其他帖子.它们提供了传递您自己的身份验证令牌的方法 - 这很好 - 但如果有人反编译您的.apk仍然不安全.我认为如果我的假设有效,您将能够在没有任何登录的情况下将您的Cloud Endpoints API锁定到客户端.

Google Cloud Endpoints的自定义身份验证(而非OAuth2)

将我的"应用"验证为Google Cloud Endpoints而不是"用户"

没有Google帐户的Google Cloud端点

编辑: 我们使用了Google云平台的金牌支持,并且几周来一直与他们的支持团队来回交谈.这是我们的最终答案:

"不幸的是,我对此没有任何好运.我已经询问了我的团队,并检查了所有文档.看起来使用OAuth2是你唯一的选择.原因是因为端点服务器在它之前处理身份验证到达你的应用程序.这意味着你将无法开发自己的身份验证流程,并且会得到与您在令牌中看到的结果非常相似的结果.

我很乐意为您提交功能请求.如果您可以提供有关OAuth2流为何不适合您的客户的更多信息,我可以将其余信息放在一起并将其提交给产品经理."

(皱眉的脸) - 然而,也许它还有可能吗?

jir*_*ray 1

我已经使用自定义标头“授权”实现了端点身份验证,并且效果很好。就我而言,此令牌是在登录后设置的,但应该与您的应用程序一样工作。检查您的测试,因为该值应该在那里。检索该标头的方法确实是:

String Auth = req.getHeader("Authorization");

您可以更进一步,定义您自己的身份验证器实现,并将其应用到您的安全 API 调用。