有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

一段时间后,java OAuth“401:无效凭据”

基于这个示例plus-appengine-sample,我使用googledriveapiOAuth2身份验证创建了一个简单的应用程序

因此,我有两个servlet实现:AbstractAppEngineAuthorizationCodeServletAbstractAppEngineAuthorizationCodeCallbackServlet,它们应该为我完成所有的艰苦工作(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();
    }
}

身份验证流与驱动器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是否需要使用存储的刷新令牌请求新的访问令牌?我使用offline访问类型,因此在第一次请求时,应该将刷新令牌传递给回调servlet

这里"401 Unauthorized" when trying to watch changes on Google Drive with Java API Client是一些可能存在问题的提示,但是如果我使用这些servlet,处理令牌过期不应该是我的情况(如果我错了,请纠正我)。此外,作用域DriveScopes.DRIVE_READONLY似乎可以读取“驱动器”树结构(获取给定文件夹的文件等)。问题出在哪里


共 (1) 个答案

  1. # 1 楼答案

    您需要首先指定脱机/长期访问需要刷新令牌,然后保存刷新令牌,以便在访问令牌过期时使用。您可以使用刷新令牌请求新的访问令牌,直到用户撤销您对其帐户的访问。请参见此处的官方文件:

    https://developers.google.com/accounts/docs/OAuth2WebServer#refresh