从android客户端登录app引擎

时间:2012-03-21 19:43:57

标签: android google-app-engine login

我正在尝试登录到应用引擎的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;
    }
}

}

2 个答案:

答案 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课程,这样就不需要反思。