2012-03-21 3 views
4

appengine에 로그인하여 app 엔진에 액세스하고 App Engine의 User Service API에 액세스하려고합니다. 기본적으로 누가 내 서블릿에 로그인했는지 볼 수 있기를 원합니다. 나는 안드로이드에서 authtoken을 가져 와서 앱 엔진에서 ASID (또는 SACID) 쿠키를 gettting하는 인증 흐름을 사용하고 있습니다. 쿠키는 http 요청과 함께 appengine 서블릿으로 전송됩니다. 이 모든 코드는 작동하지만이 코드를 사용하여 사용자를 얻으려는 경우 다음과 같이 나타납니다.android 클라이언트에서 appengine에 로그인

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

사용자는 항상 null입니다. 내 질문에 내가 여기서 뭔가를 놓친거야? 사용자 서비스가 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); 
} 
} 

안드로이드 코드 :

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

위의 구글 계정 API에서 ClientLogin에 토큰을 받고있다 안드로이드 코드입니다. 로그인하고 UserService을 통해 현재 사용자를 얻으려면 GAE 앱이 Google 계정 API를 사용하여 인증을해야합니다 ('애플리케이션 설정'-> '인증 옵션').

+0

세션을 사용할 수 있습니다. 나는 브라우저에서 시도하고 동일한 오류가 발생했습니다. 좀 더 파고해야 겠어. 감사합니다. – Patrick

+0

web.xml에서 리소스에 대한 인증이 필요합니까? –

+0

나는 그것을 지금 추가하고 테스트하지 않았다. 아직도 운이 없다. 이제 로그인 페이지로 리디렉션됩니다. – Patrick

0

기존 AppEngine 끝점에 인증을 추가하는 것이 매우 쉽다는 것을 알았습니다. 메소드에 com.google.appengine.api.users.User 매개 변수를 추가하기 만하면됩니다. 마지막으로 나는 후드 아래에서 진행되는 작업과 동일한 방식으로 임의 서블릿을 인증하는 방법을 발견했습니다. 그래서 안드로이드 측에서 인증하는 당신이 필요로하는 구글 HttpRequest에 객체를 생성)

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

3 요청합니다 : 수) 자격 증명 개체를

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 : 1) 계정을 선택

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 측에서 요청을 인증하려면 "appengine-endpoints.jar"라이브러리에 선언 된 내부 클래스 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; 
    } 
} 

여기에이 도우미 사용하는 방법입니다 : AppEngine에 1.8.6로 확인

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

이 접근합니다. Google에서 WebApisUserService 클래스를 공개적으로 열어서 리플렉션이 필요하지 않기를 바랍니다.