我使用 Google Drive API 创建了简单的应用 OAuth2身份验证,基于此示例plus-appengine-sample
所以,我有两个servlet实现:AbstractAppEngineAuthorizationCodeServlet
和AbstractAppEngineAuthorizationCodeCallbackServlet
,它们应该为我完成所有艰苦的工作(oauth工作流程)。
public class DriveServlet extends AbstractAppEngineAuthorizationCodeServlet {
private static final String MY_APP_NAME = "Drive API demo";
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
AuthorizationCodeFlow authFlow = initializeFlow();
Credential credential = authFlow.loadCredential(getUserId(req));
if (credential == null) {
resp.sendRedirect(authFlow.newAuthorizationUrl()
.setRedirectUri(OAuthUtils.getRedirectUri(req)).build());
return;
}
Drive drive = new Drive.Builder(OAuthUtils.HTTP_TRANSPORT_REQUEST,
OAuthUtils.JSON_FACTORY, credential).setApplicationName(MY_APP_NAME).build();
// API calls (examines drive structure)
DriveMiner miner = new DriveMiner(drive);
req.setAttribute("miner", miner);
RequestDispatcher view = req.getRequestDispatcher("/Drive.jsp");
view.forward(req, resp);
}
@Override
protected AuthorizationCodeFlow initializeFlow() throws ServletException, IOException {
return OAuthUtils.initializeFlow();
}
@Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
return OAuthUtils.getRedirectUri(req);
}
}
public class OAuthCallbackServlet extends AbstractAppEngineAuthorizationCodeCallbackServlet {
private static final long serialVersionUID = 1L;
@Override
protected AuthorizationCodeFlow initializeFlow() throws ServletException, IOException {
return OAuthUtils.initializeFlow();
}
@Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
return OAuthUtils.getRedirectUri(req);
}
@Override
protected void onSuccess(HttpServletRequest req, HttpServletResponse resp,
Credential credential) throws ServletException, IOException {
resp.sendRedirect(OAuthUtils.MAIN_SERVLET_PATH);
}
@Override
protected void onError(HttpServletRequest req, HttpServletResponse resp,
AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException {
String nickname = UserServiceFactory.getUserService().getCurrentUser().getNickname();
resp.getWriter().print(
"<h3>I am sorry" + nickname+ ", an internal server error occured. Try it later.</h1>");
resp.setStatus(500);
resp.addHeader("Content-Type", "text/html");
return;
}
}
public class OAuthUtils {
private static final String CLIENT_SECRETS_FILE_PATH = "/client_secrets.json";
static final JacksonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
static final UrlFetchTransport HTTP_TRANSPORT_REQUEST = new UrlFetchTransport();
private static final Set<String> PERMISSION_SCOPES = Collections.singleton(DriveScopes.DRIVE_READONLY);
private static final AppEngineDataStoreFactory DATA_STORE_FACTORY = AppEngineDataStoreFactory.getDefaultInstance();
private static final String AUTH_CALLBACK_SERVLET_PATH = "/oauth2callback";
static final String MAIN_SERVLET_PATH = "/drive";
private static GoogleClientSecrets clientSecrets = null;
private OAuthUtils() {}
private static GoogleClientSecrets getClientSecrets() throws IOException {
if (clientSecrets == null) {
InputStream jsonStream = OAuthUtils.class.getResourceAsStream(CLIENT_SECRETS_FILE_PATH);
InputStreamReader jsonReader = new InputStreamReader(jsonStream);
clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, jsonReader);
}
return clientSecrets;
}
static GoogleAuthorizationCodeFlow initializeFlow() throws IOException {
return new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT_REQUEST,
JSON_FACTORY, getClientSecrets(), PERMISSION_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);
return requestUrl.build();
}
}
身份验证流程按预期工作以及Drive API调用,但不知何故,经过一段时间后,我在刷新时遇到此异常:
Uncaught exception from servlet
com.google.api.client.googleapis.json.GoogleJsonResponseException: 401
{
"code" : 401,
"errors" : [{ "domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError" }],
"message" : "Invalid Credentials"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:145)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:312)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1049)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:410)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:343)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:460)
at sk.ennova.teamscom.drive.DriveMiner.getRootFolderId(DriveMiner.java:46)
at org.apache.jsp.Drive_jsp._jspService(Drive_jsp.java:61)
似乎令牌已已过期,但是servlet是否需要使用存储的刷新令牌来请求新的访问令牌?我使用离线访问类型,因此应在第一次请求时将刷新令牌传递给回调servlet。
这里"401 Unauthorized" when trying to watch changes on Google Drive with Java API Client是一些可能是问题的提示,但如果我使用这些servlet,处理令牌过期不应该是我的情况(如果我错了,请纠正我)。另外,范围DriveScopes.DRIVE_READONLY
似乎可以读取“驱动”树结构(获取给定文件夹的文件等)。哪个可能是问题?
答案 0 :(得分:1)
您需要首先指定需要刷新令牌以进行脱机/长期访问,然后保存刷新令牌以供以后在访问令牌过期时使用。您可以使用刷新令牌请求新的访问令牌,直到用户撤消对其帐户的访问权限。请参阅此处的官方文档:
https://developers.google.com/accounts/docs/OAuth2WebServer#refresh