我正在尝试登录到应用引擎的appengine并访问应用引擎中的用户服务API。基本上我希望能够看到谁登录到我的servlet。我正在使用从android获取authtoken的身份验证流程,然后从app引擎获取ASID(或SACID)cookie。然后将cookie与http请求一起发送到appengine servlet。这似乎一切正常,但是当我尝试使用此代码获取用户时:
UserService userService = UserServiceFactory.getUserService();
User user= userService.getCurrentUser();
用户始终为空。我的问题是我在这里遗漏了什么?为什么用户服务返回空用户?下面是我的appengine和android代码。任何帮助将不胜感激!
App引擎:
public class MyServlet extends HttpServlet {
public void process(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
resp.setContentType("text/plain");
UserService userService = UserServiceFactory.getUserService();
User user= userService.getCurrentUser();
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
process(req, resp);
}
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
process(req, resp);
}
}
Android代码:
public class AppEngineClient {
static final String BASE_URL = Util.getBaseUrl(this);
private static final String AUTH_URL = BASE_URL + "/_ah/login";
private static final String AUTH_TOKEN_TYPE = "ah";
private final Context mContext;
private final String mAccountName;
private static final String TAG = "AppEngineClient";
public AppEngineClient(Context context, String accountName) {
this.mContext = context;
this.mAccountName = accountName;
}
public HttpResponse makeRequest(String urlPath, List<NameValuePair> params) throws Exception {
HttpResponse res = makeRequestNoRetry(urlPath, params, false);
if (res.getStatusLine().getStatusCode() == 500) {
res = makeRequestNoRetry(urlPath, params, true);
}
return res;
}
private HttpResponse makeRequestNoRetry(String urlPath, List<NameValuePair> params, boolean newToken)
throws Exception {
// Get auth token for account
Account account = new Account(mAccountName, "com.google");
String authToken = getAuthToken(mContext, account);
if (newToken) { // invalidate the cached token
AccountManager accountManager = AccountManager.get(mContext);
accountManager.invalidateAuthToken(account.type, authToken);
authToken = getAuthToken(mContext, account);
}
// Get SACSID cookie
DefaultHttpClient client = new DefaultHttpClient();
String continueURL = BASE_URL;
URI uri = new URI(AUTH_URL + "?continue=" +
URLEncoder.encode(continueURL, "UTF-8") +
"&auth=" + authToken);
HttpGet method = new HttpGet(uri);
final HttpParams getParams = new BasicHttpParams();
HttpClientParams.setRedirecting(getParams, false); // continue is not used
method.setParams(getParams);
HttpResponse res = client.execute(method);
Header[] headers = res.getHeaders("Set-Cookie");
if (res.getStatusLine().getStatusCode() != 302 ||
headers.length == 0) {
return res;
}
String sascidCookie = null;
for (Header header: headers) {
if (header.getValue().indexOf("SACSID=") >=0) {
// let's parse it
String value = header.getValue();
String[] pairs = value.split(";");
ascidCookie = pairs[0];
}
}
// Make POST request
uri = new URI(BASE_URL + urlPath);
HttpPost post = new HttpPost(uri);
UrlEncodedFormEntity entity =
new UrlEncodedFormEntity(params, "UTF-8");
post.setEntity(entity);
post.setHeader("Cookie", ascidCookie);
post.setHeader("X-Same-Domain", "1"); // XSRF
res = client.execute(post);
return res;
}
private String getAuthToken(Context context, Account account) throws PendingAuthException {
String authToken = null;
AccountManager accountManager = AccountManager.get(context);
try {
AccountManagerFuture<Bundle> future =
accountManager.getAuthToken (account, AUTH_TOKEN_TYPE, false, null, null);
Bundle bundle = future.getResult();
authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
if (authToken == null) {
throw new PendingAuthException(bundle);
}
} catch (OperationCanceledException e) {
Log.w(TAG, e.getMessage());
} catch (AuthenticatorException e) {
Log.w(TAG, e.getMessage());
} catch (IOException e) {
Log.w(TAG, e.getMessage());
}
return authToken;
}
public class PendingAuthException extends Exception {
private static final long serialVersionUID = 1L;
private final Bundle mAccountManagerBundle;
public PendingAuthException(Bundle accountManagerBundle) {
super();
mAccountManagerBundle = accountManagerBundle;
}
public Bundle getAccountManagerBundle() {
return mAccountManagerBundle;
}
}
}
答案 0 :(得分:3)
上面的Android代码是从Google Accounts API获取ClientLogin令牌。要通过UserService
登录并获取当前用户,GAE应用也必须使用Google Accounts API进行身份验证(“应用程序设置” - >“验证选项”)。
答案 1 :(得分:0)
我注意到向现有AppEngine端点添加身份验证非常容易 - 您只需要在方法中添加com.google.appengine.api.users.User参数即可。最后,我发现了幕后的内容以及如何以相同的方式对任意servlet进行身份验证。 因此,要在Android端进行身份验证,您需要: 1)选择帐户:
private void signIn()
{
startActivityForResult(GoogleAccountCredential.usingAudience(this, "server:client_id:{id}.apps.googleusercontent.com").newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode)
{
case REQUEST_ACCOUNT_PICKER:
if (data != null && data.getExtras() != null)
{
String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null)
{
// TODO save accountName
}
}
break;
}
}
2)获取Credential对象:
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this, "server:client_id:{id}.apps.googleusercontent.com");
credential.setSelectedAccountName(accountName);
3)创建Google HttpRequest对象并发出请求:
HttpTransport transport = new NetHttpTransport();
HttpRequestFactory requestFactory = transport.createRequestFactory(credential);
GenericUrl url = new GenericUrl(UPLOAD_SERVICE_URL);
HttpRequest request = requestFactory.buildGetRequest(url);
HttpResponse resp = request.execute();
// TODO check response
要在AppEngine端验证请求,您可以使用在&#34; appengine-endpoints.jar&#34;中声明的内部类WebApisUserService。图书馆。这只是AppEngine内部在端点中使用的类。不幸的是,这个类构造函数和其他必需的方法受到保护,不受外部使用的影响,所以我们需要使用反射来访问它。完整的帮助程序类如下:
public class WebApisUserServiceHelper
{
public static WebApisUserService createInstance(boolean isClientIdWhitelistEnabled)
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException
{
Constructor<WebApisUserService> constructor;
constructor = WebApisUserService.class.getDeclaredConstructor(Boolean.TYPE);
constructor.setAccessible(true);
WebApisUserService ret = constructor.newInstance(isClientIdWhitelistEnabled);
return ret;
}
public static User getCurrentUser(WebApisUserService service, HttpServletRequest req, String appName, List<String> audiences, List<String> clientIds)
throws NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
String token = getAuthToken(service, req);
if (token != null)
{
List<String> allowedScopes = new ArrayList<String>();
allowedScopes.add("https://www.googleapis.com/auth/userinfo.email");
return getCurrentUser(service, token, allowedScopes, audiences, clientIds);
}
return null;
}
private static User getCurrentUser(WebApisUserService service, String token, List<String> allowedScopes, List<String> allowedAudiences, List<String> allowedClientIds)
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Method method = WebApisUserService.class.getDeclaredMethod("getCurrentUser", String.class, List.class, List.class, List.class);
method.setAccessible(true);
Object ret = method.invoke(service, token, allowedScopes, allowedAudiences, allowedClientIds);
if (ret instanceof User) return (User) ret;
return null;
}
private static String getAuthToken(WebApisUserService service, HttpServletRequest request)
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Method method = WebApisUserService.class.getDeclaredMethod("getAuthToken", HttpServletRequest.class);
method.setAccessible(true);
Object ret = method.invoke(service, request);
if (ret instanceof String) return (String) ret;
return null;
}
}
以下是如何使用此帮助程序:
public class MyServlet extends HttpServlet
{
private final WebApisUserService auth = createAuthService();
private static WebApisUserService createAuthService()
{
try
{
return WebApisUserServiceHelper.createInstance(false);
}
catch (Exception e)
{
log.log(Level.WARNING, "Failed to create WebApisUserServiceFactory instance. Exception: %s", e.toString());
}
return null;
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
{
try
{
User user = authenticateUserSafe(req);
if (user == null)
{
resp.sendError(401, "auth required");
return;
}
String str = String.format("User id: %s, nick: %s, email: %s", user.getUserId(), user.getNickname(), user.getEmail());
resp.getWriter().write(str);
}
catch (Throwable e)
{
resp.getWriter().write("Exception: " + e);
}
}
private User authenticateUserSafe(HttpServletRequest req)
{
try
{
return authenticateUser(req);
}
catch (Exception e)
{
log.log(Level.WARNING, "Failed to authenticate user. Exception: %s", e.toString());
}
return null;
}
private User authenticateUser(HttpServletRequest req)
throws NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
List<String> audiences = new ArrayList<String>();
audiences.add(Ids.ANDROID_AUDIENCE);
List<String> clientIds = new ArrayList<String>();
clientIds.add(Ids.WEB_CLIENT_ID);
clientIds.add(Ids.ANDROID_CLIENT_ID);
return WebApisUserServiceHelper.getCurrentUser(auth, req, "{id}", audiences, clientIds);
}
}
使用AppEngine 1.8.6检查此方法。我希望Google公开开放WebApisUserService课程,这样就不需要反思。