Здравствуйте!
Есть desktop приложение, которое авторизует пользователя вконтакте. Путём проб и ошибок в оконцовке пришёл к такому варианту авторизации(получение ACCESS_TOKEN)
В этом методе мы проходим по ссылке, которая будет спрашивать, дать ли этому приложению доступ?
Берём со страницы хэши:
ip_h, lg_h, to.
Они нужны для отправки POST запроса на авторизацию.
Попутно вытягиваем печеньку "remixlhk", без которого ВК не хочет даже здороваться.
private void getCookieremixlhk() throws IOException {
URL url = new URL("https://oauth.vk.com/authorize?client_id=id_вашей_группыredirect_uri=https://oauth.vk.com/blank.html&scope=friends,groups&response_type=token&revoke=1");
ArrayList<String> listHeaders = new ArrayList<>();
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
for (Map.Entry<String, List<String>> pair : conn.getHeaderFields().entrySet()) {
listHeaders.addAll(pair.getValue());
}
for (String listi : listHeaders) {
if (listi.contains("remixlhk")) {
remixlhk = listi.split("=", 2)[1].split(";", 2)[0];
}
}
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
while (in.ready()) {
String temp = in.readLine();
if (temp.contains("ip_h"))
ip_h = temp.split("value=\"", 2)[1].split("\"")[0];
if (temp.contains("lg_h"))
lg_h = temp.split("value=\"", 2)[1].split("\"")[0];
if (temp.contains("name=\"to\""))
to = temp.split("value=\"", 2)[1].split("\"")[0];
}
in.close();
}
Дальше мы отправляем POST запрос на авторизацию.
Из страницы ответа берём ссылку на подтверждение доступа приложению...
private void fullAuthorization() throws IOException {
URL url = new URL("https://login.vk.com/?act=login&soft=1");
Map<String, Object> params = new LinkedHashMap<>();
params.put("_origin", "https://oauth.vk.com");
params.put("email", email);
params.put("expire", "0");
params.put("ip_h", ip_h);
params.put("lg_h", lg_h);
params.put("pass", pass);
params.put("to", to);
params.put("Cookie", "remixlhk="+remixlhk);
StringBuilder postData = new StringBuilder();
for (Map.Entry<String, Object> param : params.entrySet()) {
if (postData.length() != 0) postData.append('&');
postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
postData.append('=');
postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
}
byte[] postDataBytes = postData.toString().getBytes("UTF-8");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
conn.setDoOutput(true);
conn.getOutputStream().write(postDataBytes);
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
while (in.ready()) {
String temp = in.readLine();
if (temp.contains("grant_access")) {
linkAccept = temp.split("form method=\"post\" action=\"")[1].split("\"", 2)[0];
}
}
}
... и проходим по ней, передав печеньку.
Из URL страницы ответа получаем токен и айди пользователя.
private void acceptAndGetToken() throws IOException {
URL url = new URL(linkAccept);
Map<String, Object> params = new LinkedHashMap<>();
params.put("Cookie", "remixlhk="+remixlhk);
StringBuilder postData = new StringBuilder();
for (Map.Entry<String, Object> param : params.entrySet()) {
if (postData.length() != 0) postData.append('&');
postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
postData.append('=');
postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
}
byte[] postDataBytes = postData.toString().getBytes("UTF-8");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
conn.setDoOutput(true);
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String URL = conn.getURL().toString();
ACCESS_TOKEN = URL.split("token=", 2)[1].split("&", 2)[0];
vk_id = Integer.valueOf(URL.split("user_id=", 2)[1]);
}
Ну тут создаем пользователя для обращения к VK API(которое будет использоваться и дальше) и достаём его имя, чтобы поприветствовать.
private void getVkFirstNameCurrentUser() throws IOException, ClientException, ApiException {
actor = new UserActor(vk_id, ACCESS_TOKEN);
vkFirstName = vkApiClient.account().getProfileInfo(actor).execute().getFirstName();
}
Собственно проблема. Даже две.
1. Практически всегда авторизация проходит только со второго раза, ошибка выскакивает при попытке авторизации, то есть при получении ссылки из метода 2(fullAuthorization). Бывает, что проходит с первого раза, бывает только со второго. Третий раз никогда не требуется. Если первый раз не прошло, закрываю ошибку и повторяю попытку авторизации. Всё получается. Есть идеи, в чём может быть проблема?
2. Самая тупая проблема, просто магия. Я, конечно, в магию верю, но может у кого нибудь будет идея, как эта магия работает?
Для GUI использую JavaFX. Есть FXML файл следующего содержания.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.web.WebView?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="260.0" prefWidth="296.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mainpackage.view.login.LoginPageController">
<children>
<ButtonBar layoutX="5.0" layoutY="209.0" prefHeight="40.0" prefWidth="246.0" AnchorPane.bottomAnchor="11.0" AnchorPane.rightAnchor="26.0">
<buttons>
<Button mnemonicParsing="false" onAction="#handleOkButtonClicked" text="OK" />
<Button mnemonicParsing="false" onAction="#handleCancelButtonClicked" text="Cancel" />
<Button mnemonicParsing="false" onAction="#handleNoAuthButtonClicked" text="No Auth" />
</buttons>
</ButtonBar>
<TextField fx:id="loginTextField" layoutX="26.0" layoutY="67.0" prefHeight="25.0" prefWidth="246.0" />
<Label layoutX="28.0" layoutY="42.0" text="Login" />
<Label layoutX="28.0" layoutY="109.0" text="Password" />
<Label layoutX="32.0" layoutY="14.0" prefWidth="153.0" text="Authorization on VK.com" AnchorPane.leftAnchor="70.0" AnchorPane.rightAnchor="70.0" AnchorPane.topAnchor="10.0">
<font>
<Font name="Arial Bold" size="12.0" />
</font>
</Label>
<PasswordField fx:id="passwordTextField" layoutX="26.0" layoutY="135.0" prefHeight="25.0" prefWidth="246.0" />
<WebView fx:id="webViewOnLogin" prefHeight="0.1" prefWidth="0.1" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Label fx:id="textAfterAuthorization" alignment="CENTER" contentDisplay="CENTER" layoutX="27.0" layoutY="177.0" prefHeight="17.0" prefWidth="246.0" text="Авторизация занимает 5-10 секунд" />
</children>
</AnchorPane>
Обратите внимание на элемент WebView практически в самом конце. Изначально авторизация была путём открывания WebView в приложении, но захотелось сделать прямую авторизацию. Красивше всё таки.
Пока делал, WebView был на месте, после того, как все получилось(авторизация), стал чистить код и удалил WebView. Всё сломалось. Не хочет он логиниться, говорит, пароль неверный.
Убрал все упоминания WebView из кода, остался только в FXML файле. Он больше нигде не используется. Размеры его 0.1 на 0.1. Но без него авторизация не работает совершенно.
Есть идеи, как можно расколдовать принцессу?