2017-01-31 3 views
0

ich einen Anwendungsfall haben, der wie folgt aussieht:UserService.getCurrentUser() gibt null auf Google App Engine

  1. Benutzer gibt https://mydomain.appspot.com/listen
  2. Benutzer auf Google für die Authentifizierung umgeleitet wird
  3. Wenn Erfolg, Anwendung sendet eine HTTP-Anforderung an Google, um Push-Benachrichtigungen für Änderungen in einer bestimmten Datei (Blatt) auf Google Drive zu aktivieren
  4. Der Benutzer gibt Google Tabellen ein und bearbeitet die Datei.
  5. Google sendet eine http-Post an meine Anwendung (https://mydomain.appspot.com/notifications) mit Datei-ID und einigen anderen Daten.
  6. Meine Anwendung empfängt den HTTP-Post, überprüft die Datei-ID und versucht, die Datei zu öffnen, um den Inhalt anzuzeigen.

Schritt 6 funktioniert nicht. Ich erhalte eine Nullpointer in der zweiten Zeile, wenn dies zu tun:

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

Ich weiß nicht wirklich, wie ich dieses Problem lösen soll. In Schritt 1-3 meldet sich der Benutzer an und gewährt Zugriff auf die Datei. Schritt 5-6 wird von Google ausgelöst. Wenn es vom Benutzer ausgelöst wurde, könnte der Benutzer auf eine Anmeldeseite umgeleitet werden. Dies ist keine Option, da die Anfrage von Google stammt.

Gibt es eine Möglichkeit, dies zu funktionieren? Hinweis: Die fragliche Datei gehört einem bestimmten Benutzer. Es gehört keinem Dienstkonto.

Ich habe meine Blatt-Authentifizierung auf dem von Google bereitgestellten Beispiel basiert. Sieht ungefähr so ​​aus:

public class ConcreteSheetWriter implements SheetWriter { 


    public ConcreteSheetWriter(DriveFileMaker driveFileMaker) { 
     DriveFileMaker driveFileMaker1 = driveFileMaker; 

     try { 
      httpTransport = GoogleNetHttpTransport.newTrustedTransport(); 
      dataStoreFactory = AppEngineDataStoreFactory.getDefaultInstance(); //TODO replace with appenginedatastore otherwise restart is painful 
     } catch (Throwable t) { 
      t.printStackTrace(); 
      // System.exit(1); TODO potentially fix for app engine 
      logger.warning("Could not connect to sheets"); 
      throw new RuntimeException(t); 
     } 


    } 

    private static Credential authorize(HttpTransport HTTP_TRANSPORT, DataStoreFactory dataStoreFactory) throws IOException { 
     // Load client secrets. 
     InputStream in = 
       ConcreteSheetWriter.class.getResourceAsStream(SECRET_PATH); 
     GoogleClientSecrets clientSecrets = 
       GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in)); 
     /* THE CODE BELOW IN THIS METHOD REPRESENT STEP 6 */ 
     // Build flow and trigger user authorization request. 
     GoogleAuthorizationCodeFlow flow = 
       new GoogleAuthorizationCodeFlow.Builder(
         HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES) 
         .setDataStoreFactory(dataStoreFactory) 
         .setAccessType("offline") 
         .build(); 
     /* 

     The credentials before deploying to GAE. Problems when deploying on GAE 
     Credential credential = new AuthorizationCodeInstalledApp(
       flow, new LocalServerReceiver()).authorize("user"); 
     */ 
     final UserService userService = UserServiceFactory.getUserService(); 
     final User user = userService.getCurrentUser(); 
     logger.info("User is " + user); 
     final String userId = user.getUserId(); 
     final Credential credential = flow.loadCredential(userId); 
     return credential; 
    } 

    @Override 
    public List<List<String>> read(String changedFileId) { 
     Sheets service = null; 
     final String range = "Sheet1!A1:AF30"; 
     try { 
      service = getSheetsService(authorize(httpTransport, dataStoreFactory), httpTransport); 
      ValueRange spreadsheets = service.spreadsheets().values().get(changedFileId, range).execute(); 
      return convert(spreadsheets.getValues()); 
     } catch (IOException e) { 
      throw new CouldNotCommunicateWithGoogleSheetsException(e); 
     } 


    } 
} 

Hier ist der Code für die Anmeldung der Benutzer in repräsentiert Schritt 1-3:

public class PlusSampleServlet extends AbstractAppEngineAuthorizationCodeServlet { 
    private final static Logger logger = Logger.getLogger(PlusSampleServlet.class.getName()); 
    private static final long serialVersionUID = 1L; 

    private final DriveUtilityService driveUtilityService; 


    public PlusSampleServlet() { 
     //omitted 
    } 

    private static void addLoginLogoutButtons(HttpServletRequest req, HttpServletResponse resp, StringBuilder resultFromWatch, UserService userService, String thisUrl, PrintWriter respWriter) throws IOException { 

     //omitted 
    } 

    private static Optional<Channel> watchFile(Drive service, String fileId, 
               String channelId, String channelType, String channelAddress) throws IOException { 
     final Channel returnValue; 
     final Channel channel = new Channel(); 
     channel.setId(channelId); 
     channel.setType(channelType); 
     channel.setAddress(channelAddress); 
     final Drive.Files tmp = service.files(); 
     returnValue = tmp.watch(fileId, channel).execute(); 
     return Optional.fromNullable(returnValue); 
    } 

    @Override 
    public void doGet(HttpServletRequest req, HttpServletResponse resp) 
      throws IOException, ServletException { 

     AuthorizationCodeFlow authFlow = initializeFlow(); 
     final String userId = getUserId(req); 
     Credential credential = authFlow.loadCredential(userId); 
     logger.info("Executing listener activation for user " + userId); 
     StringBuilder resultFromWatch = new StringBuilder(); 
     Drive drive = new Drive.Builder(Utils.HTTP_TRANSPORT, Utils.JSON_FACTORY, credential).setApplicationName("t").build(); 

     try { 

      Optional<Channel> channel = watchFile(drive, driveUtilityService.getFileId(), driveUtilityService.getChannelId(), "web_hook", driveUtilityService.getPushUrl()); 
      String channelStringTmp; 
      if (channel.isPresent()) { 
       channelStringTmp = channel.get().toString(); 
      } else { 
       channelStringTmp = "null..."; 
      } 
      resultFromWatch.append(channelStringTmp); 
     } catch (Exception e) { 
      resultFromWatch.append(e.getMessage()); 
     } 

     final UserService userService = UserServiceFactory.getUserService(); 
     final String thisUrl = req.getRequestURI(); 
     // Send the results as the response 
     PrintWriter respWriter = resp.getWriter(); 
     resp.setStatus(200); 
     resp.setContentType("text/html"); 

     addLoginLogoutButtons(req, resp, resultFromWatch, userService, thisUrl, respWriter); 

     logger.warning("user is " + userId + " sample has done its job and channel " + resultFromWatch.toString()); 
    } 

    @Override 
    protected AuthorizationCodeFlow initializeFlow() throws ServletException, IOException { 
     return Utils.initializeFlow(); 
    } 

    @Override 
    protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { 
     return Utils.getRedirectUri(req); 
    } 
} 

Die utils Klasse:

class Utils { 
    static final String MAIN_SERVLET_PATH = "/plussampleservlet"; 
    static final String AUTH_CALLBACK_SERVLET_PATH = "/oauth2callback"; 
    static final UrlFetchTransport HTTP_TRANSPORT = new UrlFetchTransport(); 
    static final JacksonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); 
    private final static Logger logger = Logger.getLogger(Utils.class.getName()); 
    /** 
    * Global instance of the {@link DataStoreFactory}. The best practice is to make it a single 
    * globally shared instance across your application. 
    */ 
    private static final AppEngineDataStoreFactory DATA_STORE_FACTORY = 
      AppEngineDataStoreFactory.getDefaultInstance(); 
    private static final Set<String> SCOPES = getScopes(); 
    private static GoogleClientSecrets clientSecrets = null; 


    private static Set<String> getScopes() { 
     List<String> scopeList = Arrays.asList(DriveScopes.DRIVE_READONLY, SheetsScopes.SPREADSHEETS_READONLY); 
     Set<String> scopes = Sets.newHashSet(); 
     scopes.addAll(scopeList); 
     return scopes; 
    } 

    private static GoogleClientSecrets getClientSecrets() throws IOException { 
     if (clientSecrets == null) { 
      clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, 
        new InputStreamReader(Utils.class.getResourceAsStream("/plus_secret.json"))); 
      Preconditions.checkArgument(!clientSecrets.getDetails().getClientId().startsWith("Enter ") 
          && !clientSecrets.getDetails().getClientSecret().startsWith("Enter "), 
        "Download client_secrets.json file from https://code.google.com/apis/console/?api=plus " 
          + "into plus-appengine-sample/src/main/resources/client_secrets.json"); 
     } 
     logger.info("Something asked for the secret"); 
     return clientSecrets; 
    } 

    static GoogleAuthorizationCodeFlow initializeFlow() throws IOException { 
     logger.info("flow is initialized soon"); 
     return new GoogleAuthorizationCodeFlow.Builder(
       HTTP_TRANSPORT, JSON_FACTORY, getClientSecrets(), SCOPES).setDataStoreFactory(
       DATA_STORE_FACTORY).setAccessType("offline").build(); 
    } 

    static String getRedirectUri(HttpServletRequest req) { 
     GenericUrl requestUrl = new GenericUrl(req.getRequestURL().toString()); 
     requestUrl.setRawPath(AUTH_CALLBACK_SERVLET_PATH); 
     logger.info("retrieved redirecturl"); 
     return requestUrl.build(); 
    } 
} 

Der Rückruf bei " Login "ist fertig:

public class PlusSampleAuthCallbackServlet 
     extends AbstractAppEngineAuthorizationCodeCallbackServlet { 
    private final static Logger logger = Logger.getLogger(PlusSampleAuthCallbackServlet.class.getName()); 

    private static final long serialVersionUID = 1L; 

    @Override 
    protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) 
      throws ServletException, IOException { 
     resp.sendRedirect(Utils.MAIN_SERVLET_PATH); 
     logger.info("ON success"); 
    } 

    @Override 
    protected void onError(
      HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) 
      throws ServletException, IOException { 
     String nickname = UserServiceFactory.getUserService().getCurrentUser().getNickname(); 
     resp.getWriter().print("<h3>Hey " + nickname + ", why don't you want to play with me?</h1>"); 
     resp.setStatus(200); 
     resp.addHeader("Content-Type", "text/html"); 
     logger.info("ON error"); 
     return; 
    } 

    @Override 
    protected AuthorizationCodeFlow initializeFlow() throws ServletException, IOException { 
     logger.info("initializing flow"); 
     return Utils.initializeFlow(); 
    } 

    @Override 
    protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { 
     logger.info("get redirect"); 
     return Utils.getRedirectUri(req); 
    } 

} 

Antwort

0

In Schritt 3 müssen Sie (beispielsweise im Datenspeicher) die Zuordnungsinformationen speichern, die die Benachrichtigungsregistrierung, das Dokument und den Benutzer miteinander verbinden (in der Registrierung/Benachrichtigung müssen einige Kontextinformationen vorhanden sein, da derselbe Benutzer mehrere sehen kann) Dokumente und mehrere Benutzer können dasselbe Dokument anzeigen)

In Schritt 6 ruft die App die gespeicherten Zuordnungsinformationen basierend auf dem Kontext der Benachrichtigung (der Postanforderung) ab und kann dann die Benutzeranmeldeinformationen angeben, die beim Öffnen verwendet werden müssen die Datei.

+0

Danke. Das klingt vernünftig. Ich bin mir nicht sicher, wie das geht. Ich habe Code hinzugefügt, um Schritt 3 zu zeigen (gestartet in PlusSampleServlet-> Utils-> PlusSampleAuthCallbackServlet) und dann ist Schritt 6 in der authorize-Methode im ConcreteSheetWriter. Ich möchte tun, was Sie vorschlagen, aber ich kann nicht herausfinden, wie. Ist die Lösung für Sie offensichtlich? Hilfe wird sehr geschätzt. –

+0

Definitiv nicht auf Detailebene - Ich bin ein pyton Benutzer :) Vielleicht etwas in dieser Richtung? http://stackoverflow.com/questions/13777842/how-to-get-offline-token-and-refresh-token-and-auto-refresh-access-to-google-api –