0
我有一個看起來像這樣一個用例:UserService.getCurrentUser()返回null在谷歌應用程序引擎
- 用戶進入https://mydomain.appspot.com/listen
- 用戶被重定向到谷歌的認證
- 如果成功,應用程序向Google發送http請求以啓用針對Google雲端硬盤上特定文件(工作表)的更改的推送通知
- 用戶輸入Google表格並編輯該文件。
- Google通過文件ID和其他一些數據向我的應用程序(https://mydomain.appspot.com/notifications)發送一個http文章。
- 我的應用程序收到http post,驗證文件ID並嘗試打開文件以查看內容。
步驟6不起作用。這樣做,當我在第二行一個NullPointerException:
final UserService userService = UserServiceFactory.getUserService();
final User user = userService.getCurrentUser();
我真的不知道我應該怎麼解決這個問題。在步驟1-3中,用戶登錄並授予對該文件的訪問權限。 Google會觸發步驟5-6。如果它是由用戶觸發的,那麼用戶可以被重定向到登錄頁面。由於請求來自Google,因此這不是一種選擇。
有什麼辦法可以使這項工作?注意:有問題的文件屬於特定用戶。它不屬於某種服務帳戶。
我在Google提供的示例中使用了我的Sheet認證。看起來是這樣的:
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(在PlusSampleServlet-> Utils-> PlusSampleAuthCallbackServlet中啓動),然後在ConcreteSheetWriter的授權方法中執行步驟6。我想要做你的建議,但我不知道如何。解決方案對你來說顯而易見嗎?非常感謝幫助。 –
絕對不是在細節層面 - 我是一個pyton用戶:)也許沿着這些線? http://stackoverflow.com/questions/13777842/how-to-get-offline-token-and-refresh-token-and-auto-refresh-access-to-google-api –