Iniciar sesión en appengine desde el cliente android

Estoy intentando ingresar a appengine al motor de la aplicación y acceder a la API del servicio del usuario en el motor de la aplicación. Básicamente, quiero ser capaz de ver quién está conectado a mis servlets. Estoy utilizando el flujo de autenticación de obtener authtoken de android y luego gettting una cookie ASID (o SACID) desde el motor de la aplicación. La cookie se envía con la solicitud http al servlet appengine. Esto parece funcionar bien, sin embargo, cuando intento obtener el usuario con este código:

UserService userService = UserServiceFactory.getUserService(); User user= userService.getCurrentUser(); 

Usuario siempre es nulo. Mi pregunta es que estoy perdiendo algo aquí? ¿Por qué el servicio de usuario devuelve un usuario nulo? A continuación se muestra mi appengine y código android. Cualquier ayuda sería muy apreciada!

Motor de aplicación:

 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); } } 

Código de 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; } } 

}

El código de Android anterior obtiene un token ClientLogin de la API de Cuentas de Google. Para iniciar sesión y obtener el usuario actual a través de UserService , la aplicación GAE también debe utilizar la API de cuentas de Google para la autenticación ('Configuración de la aplicación' -> 'Opciones de autenticación').

Me di cuenta de que es muy fácil agregar autenticación a un punto final AppEngine existente – sólo tiene que agregar el parámetro com.google.appengine.api.users.User a su método. Y finalmente encontré lo que está pasando bajo el capó y cómo autenticar de la misma manera un servlet arbitrario. Así que para autenticar en Android lado que necesita: 1) elegir la cuenta:

 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) obtener el objeto Credential:

 GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this, "server:client_id:{id}.apps.googleusercontent.com"); credential.setSelectedAccountName(accountName); 

3) crear el objeto HttpRequest de Google y hacer la solicitud:

 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 

Para autenticar la solicitud en AppEngine puede utilizar la clase interna WebApisUserService declarada en la biblioteca "appengine-endpoints.jar". Esta es sólo la clase utilizada por AppEngine internamente en puntos finales. Desafortunadamente este constructor de clase y otros métodos requeridos están protegidos del uso externo por lo que necesitamos usar la reflexión para acceder a él. La clase de ayuda completa es la siguiente:

 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; } } 

Y aquí es cómo usar este ayudante:

 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); } } 

Este enfoque se verificó con AppEngine 1.8.6. Espero que Google abra la clase WebApisUserService públicamente para que no se requiera reflexión.

  • Inicio de sesión de Android - Mejor implementación
  • Permiso de denegación: proveedor de apertura com.android.providers.contacts.ContactsProvider2
  • Cómo utilizar la sesión para trabajar entre php webservices y android
  • El recuento de sesión se multiplica cuando uso Google Analytics Android SDK v4
  • Funcionalidad de cierre de sesión en android
  • ¿La forma preferida de conectar AudioEffect a la mezcla global?
  • Sesión agotada y cierre automático de sesión de la aplicación después de pocas horas en android
  • Longitud de sesión corta en Google Analytics para Android
  • Mantenga la sesión entre loadUrl de Android WebView
  • Sesión de inicio de sesión - Actividades
  • Cómo hacer la reanudación de sesión SSL en Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.