使用JavaMail API连接到GMail,而不启用不太安全的访问

使用JavaMail API连接到GMail,而不启用不太安全的访问,java,oauth-2.0,gmail,jakarta-mail,Java,Oauth 2.0,Gmail,Jakarta Mail,是否有任何方法可以使用JavaMail API连接到GMail而不启用“不太安全的访问”您必须使用OAuth2进行身份验证,以避免启用不太安全的访问 很难找到文件 示例代码: Properties props = (Properties) System.getProperties().clone(); props.put("mail.imaps.ssl.enable", "true"); props.put("mail.imaps.auth.mechanisms", "XOAUTH2")

是否有任何方法可以使用JavaMail API连接到GMail而不启用“不太安全的访问”

您必须使用OAuth2进行身份验证,以避免启用不太安全的访问

很难找到文件

示例代码:

Properties props = (Properties) System.getProperties().clone();
props.put("mail.imaps.ssl.enable", "true");
props.put("mail.imaps.auth.mechanisms", "XOAUTH2");
// props.put("mail.debug.auth", "true");

Session session = Session.getDefaultInstance(props);
// session.setDebug(true);

store = session.getStore("imaps");
String accessToken = getAccessToken(user, clientId, clientSecret);
store.connect(hostname, user, accessToken);
下一个技巧是获取访问令牌

这里有一个您可以在本地使用的:

您首先需要在创建一个应用程序,并获取客户端id和机密JSON

您可以在任何端口上启动本地web服务器并使用
http://localhost
重定向任何端口上的URL(需要在前面提到的屏幕上添加)


如果搜索“JavaMail OAuth2”可以直接找到文档,文档会有多难找到?除非您知道它与OAuth2相关,否则没有迹象表明它应该是搜索查询的一部分;如果没有OAuth2关键字,所有最重要的结果都会显示使用用户名/密码,并且您可能需要启用不太安全的访问
private static String getAccessToken(String emailAddress, String clientId, String clientSecret) throws Exception {
    NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
    JsonFactory jsonFactory = Utils.getDefaultJsonFactory();

    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(jsonFactory, new StringReader("{\n" +
            "  \"installed\": {\n" +
            "    \"client_id\": \"" + clientId + "\",\n" +
            "    \"client_secret\": \"" + clientSecret + "\"\n" +
            "  }\n" +
            "}"));
    GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(transport, jsonFactory, clientSecrets, Collections.singleton(GMAIL_SCOPE)).setAccessType("offline").build();

    AtomicReference<String> redirectUri = new AtomicReference<>();

    String authorizationCode = doRedirect("code=([^&$]+)", RE.wrapConsumer(p -> {
        redirectUri.set("http://localhost:" + p);
        Desktop.getDesktop().browse(flow.newAuthorizationUrl().setRedirectUri(redirectUri.get()).toURI());
    }));

    // second redirect URL needs to be set and match the one on the newAuthorization flow but isn't actually used
    GoogleTokenResponse execute = flow.newTokenRequest(authorizationCode).setRedirectUri(redirectUri.get()).execute();

    String refreshToken = execute.getRefreshToken();
    String accessToken = execute.getAccessToken();

    return accessToken;
}

private static String doRedirect(String pattern, Consumer<Integer> portConsumer) {
    try {
        ServerSocket socket = new ServerSocket(0);
        portConsumer.accept(socket.getLocalPort());
        Socket connection = socket.accept();
        BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        OutputStream out = new BufferedOutputStream(connection.getOutputStream());
        PrintStream pout = new PrintStream(out);

        try {
            String request = in.readLine();
            Matcher matcher = Pattern.compile(pattern).matcher(request);
            String response = "<html><body>Window can be closed now.</body></html>";
            pout.println("HTTP/1.1 200 OK");
            pout.println("Server: MyApp");
            pout.println("Content-Type: text/html");
            pout.println("Content-Length: " + response.length());
            pout.println();
            pout.println(response);
            pout.flush();
            if (matcher.find())
                return matcher.group(1);
            else
                throw new RuntimeException("Could not find match");
        } finally {
            in.close();
        }
    } catch (Exception ex) {
        throw new RuntimeException("Error while listening for local redirect", ex);
    }
}
GoogleAuthorizationCodeFlow flow = 
    new GoogleAuthorizationCodeFlow.Builder(transport, jsonFactory, clientSecrets, Collections.singleton(GMAIL_SCOPE))
        .setApprovalPrompt("force") // Needed only if you users didn't accept this earlier
        .setAccessType("offline")
        .build();