针对应用引擎端点对用户进行身份验证
Authenticating user against app engine endpoints
我正在创建我的第一个 App Engine 应用程序,但在对用户进行身份验证时遇到了问题。
我已经按照 https://cloud.google.com/appengine/docs/python/endpoints/consume_android#making_authenticated_calls - 看起来有点神奇,我只是 "setAccountName" 它应该可以工作,但是 w/e,我想它应该加载应用程序来自 Android Audience 的范围,然后检查我传递的帐户名是否已实际登录到设备。
API 调用有效,通过了身份验证,但遗憾的是 - 后端 returns None.
上的 "endpoints.get_current_user()" 函数
所以我一直在挖掘,但似乎找不到任何关于该主题的内容。我发现的最好的东西是 http://blog.notdot.net/2010/05/Authenticating-against-App-Engine-from-an-Android-app - 但那是 6 年前的一篇文章,作者使用 HTTP 客户端,与端点库无关。
我所能想到的就是按照一些 "non-endpoints" 的方式将 "login with 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
您不会在为您创建的后端上拥有 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 上的 "injected types"。
这意味着如果用户向其 Google+ 帐户提供了正确的凭据,GAE 将注入 Google 用户对象。如果没有,则 User
将为空。如果是,您可以按照上述方法抛出一个 UnauthorizedException
。
如果 user
对象不为空,您现在可以获得用户的 gmail 地址等信息。从那里,您必须将这些值存储在您自己的自定义实体中,例如 AppEngineUser
并将其保存到数据存储区。然后你可以稍后用它做其他事情,比如加载它,检查用户是否注册以及你自己做的任何其他事情。
希望对您有所帮助!
好的,我明白了。当我从 API 声明中删除 "scopes" 时,它起作用了。我还不确定如何访问用户的电子邮件/个人资料,但这至少向前迈进了一步。
这个问题实际上已经被提出过 - How to add more scopes in GoogleCloud Endpoints - 遗憾的是,没有任何答案
我正在创建我的第一个 App Engine 应用程序,但在对用户进行身份验证时遇到了问题。
我已经按照 https://cloud.google.com/appengine/docs/python/endpoints/consume_android#making_authenticated_calls - 看起来有点神奇,我只是 "setAccountName" 它应该可以工作,但是 w/e,我想它应该加载应用程序来自 Android Audience 的范围,然后检查我传递的帐户名是否已实际登录到设备。
API 调用有效,通过了身份验证,但遗憾的是 - 后端 returns None.
上的 "endpoints.get_current_user()" 函数所以我一直在挖掘,但似乎找不到任何关于该主题的内容。我发现的最好的东西是 http://blog.notdot.net/2010/05/Authenticating-against-App-Engine-from-an-Android-app - 但那是 6 年前的一篇文章,作者使用 HTTP 客户端,与端点库无关。
我所能想到的就是按照一些 "non-endpoints" 的方式将 "login with 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
您不会在为您创建的后端上拥有 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 上的 "injected types"。
这意味着如果用户向其 Google+ 帐户提供了正确的凭据,GAE 将注入 Google 用户对象。如果没有,则 User
将为空。如果是,您可以按照上述方法抛出一个 UnauthorizedException
。
如果 user
对象不为空,您现在可以获得用户的 gmail 地址等信息。从那里,您必须将这些值存储在您自己的自定义实体中,例如 AppEngineUser
并将其保存到数据存储区。然后你可以稍后用它做其他事情,比如加载它,检查用户是否注册以及你自己做的任何其他事情。
希望对您有所帮助!
好的,我明白了。当我从 API 声明中删除 "scopes" 时,它起作用了。我还不确定如何访问用户的电子邮件/个人资料,但这至少向前迈进了一步。
这个问题实际上已经被提出过 - How to add more scopes in GoogleCloud Endpoints - 遗憾的是,没有任何答案