簡體   English   中英

UserService.getCurrentUser()在Google App Engine上返回null

[英]UserService.getCurrentUser() returns null on Google App Engine

我有一個用例如下:

  1. 用戶輸入https://mydomain.appspot.com/listen
  2. 用戶被重定向到Google進行身份驗證
  3. 如果成功,則應用程序向Google發送http請求,以啟用推送通知,以通知Google雲端硬盤中特定文件(表格)的更改
  4. 用戶輸入Google表格並編輯文件。
  5. Google將http帖子發送到我的應用程序( https://mydomain.appspot.com/notifications ),其中包含文件ID和一些其他數據。
  6. 我的應用程序收到http帖子,驗證文件ID並嘗試打開文件以查看內容。

第6步不起作用。 這樣做時,我在第二行得到了NullPointerException:

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

我真的不知道該怎么解決。 在步驟1-3中,用戶登錄並授予對該文件的訪問權限。 步驟5-6是由Google觸發的。 如果是由用戶觸發的,則可以將用戶重定向到登錄頁面。 這不是一個選擇,因為請求來自Google。

有什么辦法可以使這項工作嗎? 注意:有問題的文件屬於特定用戶。 它不屬於某種服務帳戶。

我的工作表身份驗證基於Google提供的示例。 看起來像這樣:

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


    }
}

這是用於登錄用戶的代碼,代表步驟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);
    }
}

utils類:

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

完成“登錄”后的回調:

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

}

在第3步中,您需要保存(例如,在數據存儲區中)將通知注冊,文檔和用戶聯系在一起的映射信息(注冊/通知中必須有一些上下文信息,因為同一用戶可以觀看多個文檔並且多個用戶可以觀看同一文檔)

在第6步,應用程序基於通知(發布請求)上下文檢索保存的映射信息,然后可以標識嘗試打開文件時需要使用的用戶憑據。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM