【问题标题】:Authenticating user against app engine endpoints针对应用引擎端点对用户进行身份验证
【发布时间】:2016-03-14 11:25:28
【问题描述】:

我正在创建我的第一个应用引擎应用,但在验证用户时遇到了问题。

我已经关注https://cloud.google.com/appengine/docs/python/endpoints/consume_android#making_authenticated_calls - 似乎有点神奇,我只是“setAccountName”并且它应该可以工作,但是我想它应该从 Android Audience 加载应用程序范围,然后检查如果我传递的帐户名实际上已经登录了设备。

API 调用有效,通过了身份验证,但遗憾的是 - 后端的“endpoints.get_current_user()”函数返回 None。

所以我一直在挖掘,但我似乎找不到任何关于这个主题的东西。我发现的最好的东西是http://blog.notdot.net/2010/05/Authenticating-against-App-Engine-from-an-Android-app - 但那是 6 年前的一篇文章,作者使用 HTTP 客户端,与端点库无关。

我所能想到的就是遵循一些“非端点”方式将“使用 Google 登录”添加到我的应用程序中,然后尝试将我将获得的凭据传递给我的 API 构建器,但这感觉不对,应该有更简单的方法来做到这一点。

那么,我是否遗漏了https://cloud.google.com/appengine/docs/python/endpoints/consume_android#making_authenticated_calls 中未提及的某些步骤?

实际代码(略简化)如下:

后端:

auth_api = endpoints.api(
    name='auth_api',
    version='v1.0',
    auth_level=endpoints.AUTH_LEVEL.REQUIRED,
    allowed_client_ids=[
        ANDROID_CLIENT_ID,
        WEB_CLIENT_ID,
        endpoints.API_EXPLORER_CLIENT_ID,
    ],
    audiences=[
        WEB_CLIENT_ID,
        endpoints.API_EXPLORER_CLIENT_ID,
    ],
    scopes=[
        'https://www.googleapis.com/auth/userinfo.profile',
        'https://www.googleapis.com/auth/userinfo.email',
    ],
)


@auth_api.api_class(resource_name='rating')
class RatingHandler(remote.Service):
    @endpoints.method(
        message_types.VoidMessage,
        RatingsMessage,
        path='rating/getRatings',
        http_method='GET',
    )
    def getRatings(self, request):
        rating_query = Rating.query(
            ancestor=ndb.Key(
                Account,
                endpoints.get_current_user().user_id(), // ERROR! endpoints.get_current_user() is None
            )
        ).order(-Rating.rating)

客户:

// Somewhere these lines exist
if (credential == null || credential.getSelectedAccountName() == null) {
    startActivityForResult(
        AuthUtils.getCredentials(getActivity()).newChooseAccountIntent(),
        AuthUtils.REQUEST_ACCOUNT_PICKER
    );
} else {
    LoadRatings();
}

@Override
public void onActivityResult(
    int requestCode,
    int resultCode,
    Intent data
) {
    super.onActivityResult(requestCode, resultCode, data);
    if (data != null && data.getExtras() != null) {
        String accountName =
            data.getExtras().getString(
                AccountManager.KEY_ACCOUNT_NAME
            );
        if (accountName != null) {
            credential = GoogleAccountCredential.usingAudience(
                getApplicationContext(),
                "server:client_id:" + Constants.ANDROID_AUDIENCE
            );
            credential.setSelectedAccountName(accountName);
            LoadRatings();
        }
    }
}

public void LoadRatings() {
    // AuthApi was auto-generated by Google App Engine based on my Backend
    AuthApi.Builder api = new AuthApi.Builder(
        AndroidHttp.newCompatibleTransport(),
        new AndroidJsonFactory(),
        credential
    ).setApplicationName(getPackageName());
    AuthApi service = api.build();
    try {
        ApiMessagesRatingRatedBeerListMessage result = service.rating().getRatings().
        // some stuff done with result, but the Exception is thrown in line above

【问题讨论】:

  • 您是在尝试使用 Google+ 帐户对用户进行身份验证,还是尝试进行其他身份验证?
  • 我想要的只是使用 Google 帐户对用户进行身份验证,并在后端提供可用的电子邮件地址。我让帐户选择器工作并设置帐户名称,就像cloud.google.com/appengine/docs/python/endpoints/… 描述的那样。 API 调用有效,但我的目标是后端没有可用的用户对象
  • @saiyr 我没有(我刚刚编辑了问题以包含 api 声明)。另外,如果我忘记添加客户端身份验证,我很确定请求会在不执行 API 代码的情况下被拒绝,因此不会引发错误

标签: android google-app-engine authentication google-cloud-endpoints


【解决方案1】:

好的,我想通了。当我从 API 声明中删除“范围”时,它可以工作。我还不确定我将如何访问用户的电子邮件/个人资料,但这至少是向前迈出的一步。

这个问题实际上之前已经提出过 - How to add more scopes in GoogleCloud Endpoints - 很遗憾,没有任何答案

【讨论】:

    【解决方案2】:

    您不会在后端为您创建 User 对象(即实体)。你必须自己做。例如:

    @Entity
    public class AppEngineUser {
        @Id
        private String email;
        private User user;
        private AppEngineUser() {}
        public AppEngineUser(User user) {
            this.user = user;
            this.email = user.getEmail();
        }
        public User getUser() {
            return user;
        }
        public Key<AppEngineUser> getKey() {
            return Key.create(AppEngineUser.class, email);
        }
    }
    

    当您创建 API 方法并像这样指定 User 对象时:

     @ApiMethod(name = "insertRecord", path = "insert_record", httpMethod = HttpMethod.POST)
        public Record insertRecord(User user,  Record record)
    
            // check if google user is authenticated
            throws UnauthorizedException {
            if (user == null) {
                throw new UnauthorizedException("Authorization required");
            }
    
            // user is authenticated... do some stuff!
    }
    

    User 对象是一种注入类型。实际上是:com.google.appengine.api.users.User。请参阅https://cloud.google.com/appengine/docs/java/endpoints/paramreturn_types 上的“注入类型”。

    这意味着如果用户向其 Google+ 帐户提供了正确的凭据,GAE 会注入 Google 用户对象。如果他们没有,那么User 将为空。如果是,您可以按照上述方法的方式抛出UnauthorizedException

    如果user 对象不为空,您现在可以获取用户的 gmail 地址等信息。从那里,您必须将这些值存储在您自己的自定义实体中,例如 AppEngineUser 并将其保存到数据存储中。然后您可以稍后用它做其他事情,例如加载它,检查用户是否已注册以及您自己做的其他事情。

    希望有帮助!

    【讨论】:

    • 我相信与您的 Java 示例等效的 python 是: current_user = endpoints.get_current_user() 如果 raise_unauthorized 和 current_user 为 None: raise endpoints.UnauthorizedException('Invalid token.') 对我来说是这样current_user 是 None (null),我不知道该怎么做才能使它不为 null
    • @mpiekarz 您的端点方法中的其他参数是否正确传递并且只有用户为空?
    • @mpiekarz 另外,如果您使用的是 Android,为什么不添加一个云端点模块并用 Java 构建整个东西呢?好向导:rominirani.com/2014/07/28/gradle-tutorial-series-an-overview
    • 我在我的问题中添加了一些简化的代码示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-19
    • 1970-01-01
    • 2016-07-28
    • 1970-01-01
    • 2019-12-09
    相关资源
    最近更新 更多