简体   繁体   English

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

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

I have a use case that looks like this: 我有一个用例如下:

  1. User enters https://mydomain.appspot.com/listen 用户输入https://mydomain.appspot.com/listen
  2. User is redirected to Google for Authentication 用户被重定向到Google进行身份验证
  3. If success, application sends http request to Google to enable push notifications for changes in a specific file(Sheet) on Google Drive 如果成功,则应用程序向Google发送http请求,以启用推送通知,以通知Google云端硬盘中特定文件(表格)的更改
  4. User enters Google Sheets and edits the file. 用户输入Google表格并编辑文件。
  5. Google sends a http post to my application( https://mydomain.appspot.com/notifications ) with file id and some other data. Google将http帖子发送到我的应用程序( https://mydomain.appspot.com/notifications ),其中包含文件ID和一些其他数据。
  6. My application receives the http post, verifies file id and tries to open the file to see the content. 我的应用程序收到http帖子,验证文件ID并尝试打开文件以查看内容。

Step 6 doesn't work. 第6步不起作用。 I get a NullPointerException on the second line when doing this: 这样做时,我在第二行得到了NullPointerException:

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

I don't really know how I should solve this. 我真的不知道该怎么解决。 In step 1-3 the user logs in and grants access to the file. 在步骤1-3中,用户登录并授予对该文件的访问权限。 Step 5-6 is triggered from Google. 步骤5-6是由Google触发的。 If it was triggered from the user, then the user could be redirected to a login page. 如果是由用户触发的,则可以将用户重定向到登录页面。 That is not an option since the request is coming from Google. 这不是一个选择,因为请求来自Google。

Is there any way to make this work? 有什么办法可以使这项工作吗? Note: The file in question belongs to a specific user. 注意:有问题的文件属于特定用户。 It is not owned by some kind of service account. 它不属于某种服务帐户。

I have based my Sheet authentication on the sample provided by Google. 我的工作表身份验证基于Google提供的示例。 Looks something like this: 看起来像这样:

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


    }
}

Here is the code for logging the user in, represents step 1-3: 这是用于登录用户的代码,代表步骤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);
    }
}

The utils class: 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();
    }
}

The callback when "login" is done: 完成“登录”后的回调:

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

}

At step 3 you would need to save (in the datastore, for example) the mapping information tying together the notification registration, the doc and the user (there must be some context info in the registration /notification as the same user can watch multiple docs and multiple users can watch the same doc) 在第3步中,您需要保存(例如,在数据存储区中)将通知注册,文档和用户联系在一起的映射信息(注册/通知中必须有一些上下文信息,因为同一用户可以观看多个文档并且多个用户可以观看同一文档)

At step 6 the app retrieves the saved mapping info based on the notification (the post request) context and can then identify the user credentials that needs to be used when attempting to open the file. 在第6步,应用程序基于通知(发布请求)上下文检索保存的映射信息,然后可以标识尝试打开文件时需要使用的用户凭据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 UserService.getCurrentUser()返回null - UserService.getCurrentUser() returns null Google App Engine getCurrentUser()返回null - Google App Engine getCurrentUser() returns null User user = userService.getCurrentUser(); 抛出NullPointerException - User user = userService.getCurrentUser(); throws a NullPointerException Google App Engine上的UserService问题 - Problems with UserService on google app engine Firebase google authentication getCurrentUser.getEmail() 返回 null,但它的返回值为 getCurrentUser.getDisplayName() - Firebase google authentication getCurrentUser.getEmail() returns null, but its returning value for getCurrentUser.getDisplayName() .getCurrentUser() 从不返回 null - firebase - .getCurrentUser() never returns null - firebase Spring Security + Google App Engine + UserService:注销的正确方法是什么? - Spring Security + Google App Engine + UserService: What is the correct way to logout? Firebase身份验证为getCurrentUser()返回NULL - Firebase Authentication returns NULL for getCurrentUser() App Engine中的UserService,OAuth和AJAX - UserService, OAuth, and AJAX in App Engine 登录后如何使用Java在应用引擎上保留查询字符串(使用Google的`userService`)? - How to preserve a Query String after Login(using Google's `userService`) on app engine using Java?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM