2012-03-21 2 views
4

Я пытаюсь войти в приложение, чтобы подключиться к движку приложения и получить доступ к API-интерфейсу пользователя в приложении. В основном я хочу видеть, кто входит в мои сервлеты. Я использую поток аутентификации, получая authtoken от android, а затем получаю файл cookie ASID (или SACID) из движка приложения. Затем cookie отправляется с HTTP-запросом на сервлет-приложение. Кажется, что все работает нормально, однако, когда я пытаюсь получить пользователя с помощью этого кода:Войдите в appengine от клиента android

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

Пользователь всегда имеет значение null. Мой вопрос: я что-то упустил? Почему служба пользователя возвращает нулевого пользователя? Ниже приведен код приложения и андроида. Любая помощь будет принята с благодарностью!

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

}

ответ

3

Андроида выше код получает маркер ClientLogin из API аккаунтов Google. Для входа в систему и получения текущего пользователя через UserService, приложение GAE должно использовать API учетных записей Google для аутентификации («Параметры приложения» -> «Параметры проверки подлинности»).

+0

Сессии включены. Я попробовал из браузера и получил такую ​​же ошибку. Мне нужно будет еще немного выкапывать. Спасибо – Patrick

+0

Вам нужна аутентификация для ресурса в web.xml? –

+0

Я не был так, я добавил его сейчас и проверял. Еще не повезло. Теперь я перенаправлен на страницу входа. – Patrick

0

Я заметил, что очень легко добавить аутентификацию к существующей конечной точке AppEngine - вам просто нужно добавить параметр com.google.appengine.api.users.User к вашему методу. И, наконец, я обнаружил, что происходит под капотом, и как аутентифицировать таким же образом произвольный сервлет. Так для проверки подлинности на 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 вы можете использовать встроенный класс WebApisUserService, объявленный в библиотеке appengine-endpoints.jar. Это всего лишь класс, используемый 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 публично, поэтому размышления не потребуются.

Смежные вопросы