Войти в приложение с Android-клиента - PullRequest
4 голосов
/ 21 марта 2012

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

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

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

Движок приложения:

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

}

Ответы [ 2 ]

3 голосов
/ 22 марта 2012

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

0 голосов
/ 24 октября 2013

Я заметил, что добавить аутентификацию в существующую конечную точку 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 публично, поэтому рефлексия не потребуется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...