Здесь описаны мои заметки о том, как работает их апи. Очень надеюсь, команда разработчиков не увидит эту библиотеку и не поменяет ничего :)
Авторизация всех запросов базируется всего на двух ключах: куки ESRNSec и заголовок at. Оба получаются во время авторизации с помощью логина и пароля. Лезть в госуслуги я не хочу и не буду, может после того как уеду из России попробую описать их апи.
Для авторизации нужно:
- Сделать POST запрос на
https://asurso.ru/webapi/auth/getdata
Мы получим ответ в JSON и куки NSSESSIONID
{
"lt": "число 1*",
"ver": "число 2*",
"salt": "число 3*"
}
- Сделать POST запрос на
https://asurso.ru/webapi/login
с кукиNSSESSIONID
и телом в x-www-form-urlencoded в следующем формате:
LoginType: 1
cid: [id страны, всегда 2]
sid: [id региона]
pid: [id округа/района]
cn: [id города]
sft: [id ТИПА образовательной организации]
scid: [id образовательной организации]
UN: [логин, строка]
PW: [первые n символов хеша пароля, где n - длина самого пароля]
lt: [число 1*]
pw2: [полный хеш пароля]
ver: [число 2*]
Все ID вы можете найти в файле LOGINIDS.md
Чтобы получить значения полей PW и pw2 необходимо взять ваш пароль, хешировать его с MD5, а затем вначале конкатенировать к нему полученную в 1 шаге соль (число 3*) и снова хешировать. Таким образом полностью алгоритм будет выглядеть так:
pw2 = md5((число 3*) + md5(password))
Зачем хешировать пароль на фронтенде да еще и с MD5 лучше спросите разработчика tls и асурсо)
- В ответ мы получим JSON следующего вида:
{
"at": "[число at]",
"code": null,
"timeOut": 3600000,
"accessToken": "...",
"refreshToken": "...",
"accountInfo": {
"activeToken": null,
"secureActiveToken": "...",
"currentOrganization": {
"id": ...,
"name": "..."
},
"user": {
"id": ...,
"name": "..."
},
"userRoles": [],
"organizations": [
{
"id": ...,
"name": "..."
},
{
"id": ...,
"name": "..."
}
],
"loginTime": "2022-01-13T22:37:38.7883797",
"active": true,
"canLogin": true,
"storeTokens": true,
"accessToken": "..."
},
"tokenType": "Bearer",
"entryPoint": "/asp/SecurityWarning.asp",
"requestData": {
"warnType": "1"
},
"errorMessage": null
}
Отсюда нам нужно лишь число at, его очень важно сохранить, ведь без него вы не сможете делать ни один запрос. Более того, оно используется в браузере, ведь когда вы входите в систему это число будет постоянно с вами, а при переходе вы делаете не GET а POST запрос. Мне такая логика кажется очень странной, ведь если обновить страницу, число потеряется из-за удаления сессии в браузере, но не на бекенде.
Короче, забирайте "at" и идем дальше.
Куки ESRNSec ставится самим запросом (заголовок set-cookie). Самостоятельно генерировать ничего не нужно, просто сохраните значение.
Все запросы, кроме тех, что для входа, должны содержать в себе заголовок at с ключом. Для входа также не забудьте добавить referrer: https://asurso.ru/about.html
Вам понадобится получить studentID, в браузере он получается при GET запросе на https://asurso.ru/webapi/student/diary/init
В массиве students берем первый и единственный объект и там уже находим поле studentId.
Если вы походите по сайту то увидите что иногда делаются запросы html темплейтов, например pastmandatory.component и diaryday.component, не знаю зачем это нужно. Это что-то на ангулярском 😬
В основном я собирал их через значения внутри select, но некоторые пришлось парсить.
Получение почты происходит отправкой GET запроса на https://asurso.ru/asp/ajax/GetMessagesAjax.asp
с query-параметрами AT, nBoxID, jtStartIndex, jtPageSize
Для отправки используется форма и POST-запрос на https://asurso.ru/asp/Messages/sendsavemsg.asp
с content-type: application/x-www-form-urlencoded со следующими параметрами:
LoginType: 0
AT: вашТокенAt
AntiForgeryToken: xsrfТокен
MID:
MBID: 3
LTO: IDполучателя
LCC:
LBC:
TA:
NA:
PP:
DMID:
RT:
DESTINATION:
ShortAttach: 1
EDITUSERID: IDотправителя
ACC:
ABC:
SU: темаПисьма
BO: телоПисьма
STMSGREPORT:
VER, ATO, ACC, ABC, NEEDNOTIFY необязательные
LoginType, MBID, ShortAttach это константы
AntiForgeryToken можно получить только GET запросом на https://asurso.ru/asp/Messages/composemessage.asp?at=[вашAtТокен] и парсингом HTML кода страницы (css-селектор: form > [name=AntiForgeryToken])
Также нужно вместе с запросом на отправку почты отправить куки с ключом AntiForgeryToken, полученное из GET-запроса на composemessage.asp
LTO — ID получателя
LCC — ID получателя копии
LBC — ID получателя скрытой копии
ATO, ACC, ABC — три поля выше, но в виде текста (не обрабатываются бекендом)
NEEDNOTIFY — необязательно, если установлено значение 1, будет получено уведомление о прочтении
MID, TA, NA, PP, DMID, RT, DESTINATION, STMSGREPORT — неизвестно, что делают эти поля, они пустые
attachment — необязательно, ID вложенного файла
Для получения новостей используется метод GET на https://asurso.ru/webapi/announcements
с query-параметрами take и fullVersion, первый указывает кол-во получаемых новостей в массиве, а второй неизвестно что. В ответ получаем JSON массив с новостями. Если к новости прикреплен файл, он будет в массиве объектов attachments, где почему-то есть название исходного файла originalFileName.
Вообще-то это уязвимость в безопасности сервиса, пожалуйста всегда скрывайте название исходного файла или хотя бы предупреждайте о его использовании загрузчика.
Чтобы скачать файл надо сделать запрос на https://asurso.ru/webapi/attachments/[fileID]
и в ответ получим сам файл с соответствующим заголовком content-type, например application/msword. Также в ответе будут заголовки filename с url-encoded названием файла, expires равный -1, content-disposition и content-length.
В портфолио можно создавать свои собственные разделы и редактировать те, что добавлены по умолчанию.
Как мы уже поняли, асурсо работает на asp, asp работает на c#, c# сделан microsoft, microsoft управляется биллом гейтсом, а он в свою очередь рептилоид, поэтому сделал SignalR чтобы высасывать из людей душу и жизненнные силы пока они с ним разбираются.
В АСУ РСО для генерации файлов, например Отчет об успеваемости, необходимо запросить его через веб-сокет. Чтобы сократить размер пакета на 1.18 МБ (а именно столько весит официальный клиент signalr), я решил работать напрямую.
- Сделаем GET-запрос на https://asurso.ru/WebApi/signalr/negotiate c параметрами
clientProtocol=1.5 at=вашЗаголовокAt connectionData=[{"name":"queuehub"}]
Не забудьте все это закодировать в URL percent encoding
Также в куки обязятельно указываем ESRNSec токен. Заголовок at не нужен.
В ответ получаем JSON от сервера SignalR следующего вида:
{
"Url": "/WebApi/signalr",
"ConnectionToken": "aksjdjklaslk/knasndbhulqhfgsykgdfyky23fsd/lq3grbhasdbsjkhrhvkqlildfhsjlfdo",
"ConnectionId": "ajskdhjd-1i23ukv-aisyd-1289-fildshn1823",
"KeepAliveTimeout": 20,
"DisconnectTimeout": 30,
"ConnectionTimeout": 110,
"TryWebSockets": true,
"ProtocolVersion": "1.5",
"TransportConnectTimeout": 5,
"LongPollDelay": 0
}
Url — адрес куда надо делать следующий запрос (константа) ConnectionId — это UUIDv4 ConnectionToken — токен для подключения, его надо будет передать ProtocolVersion — версия, ее тоже надо будет передать
Остальное нас не интересует
- Создаем веб-сокет GET-запросом на адрес
wss://asurso.ru/WebApi/signalr/connect
с параметрами: (URL-параметры, через &)
transport=webSockets clientProtocol=1.5 at=вашЗаголовокAt connectionToken=токенПодключения connectionData=[{"name":"queuehub"}]
Параметр tid не обязателен.
Важно!! обязательно в заголовке в запросе на создание веб-сокета нужно послать куки ESRNSec токен, иначе получим ответ 403 Forbidden от SignalR.
Также между получением токена-подключена и созданием сокета стоит подождать около секунды.
- Подключившись к SignalR сразу получаем какие-то крипто-сообщения, их надо игнорировать. С открытым сокетом параллельно отправляем обычный GET-запрос на https://asurso.ru/WebApi/signalr/start со следующими параметрами:
transport: webSockets clientProtocol: 1.5 at: вашAtКлюч connectionToken: вашТокенПодключения connectionData: [{"name":"queuehub"}]
Обязательно в заголовке отправляем куки ESRNSec, дожидаемся ответа и игнорируем его содержание.
- Теперь возвращаемся к веб-сокету и отправляем сообщение
{"H":"queuehub","M":"StartTask","A":[13600184],"I":0}
. 13600184 — это номер задачи, привязанный к генерации Отчета об успеваемости и посещаемости ученика (PDF версия). Я долго искал в коде сайта, как генерируется ID тасков, но так и не понял. На данный момент можно использовать повторно предыдущие. Исходя из результатов моих экспериментов, другие числа и отсутствие параметра A возвращает ошибку{"I":"0","E":"There was an error invoking Hub method 'queuehub.StartTask'."}
, такую же ошибку получаем если отправить сообщение до третьего шага.
После успешной отправки получим ответ {"I":"0"}
(но это не точно), потом {"C":"s-0,...","M":[{"H":"QueueHub","M":"progress","A":[{"TaskId":13600184,"Status":"В процессе обработки"}]}]}
и наконец {"C":"s-0,1DBE6D4","M":[{"H":"QueueHub","M":"complete","A":[{"TaskId":13600184,"Data":"..."}]}]}
. Именно Data и будет ID файла. Но не торопитесь получать его — мы еще не уточнили формат (html или pdf) и информацию о генерируемом документе.
- Делаем POST-запрос на
https://asurso.ru/webapi/reports/studenttotal/queue
и если хотим получить в итоге PDF, а не HTML (по умолчанию), то добавляем в конце параметр?output=Pdf
.
В теле передаем JSON:
{
"selectedData": [
{
"filterId": "SID",
"filterValue": "592640",
"filterText": "Щелочков Виктор"
},
{
"filterId": "PCLID",
"filterValue": "...",
"filterText": "..."
},
{
"filterId": "period",
"filterValue": "2022-01-10T00:00:00.000Z - 2022-05-28T00:00:00.000Z",
"filterText": "10.01.2022 - 28.05.2022"
}
],
"params": [
{
"name": "SCHOOLYEARID",
"value": "..."
},
{
"name": "SERVERTIMEZONE",
"value": 4
},
{
"name": "FULLSCHOOLNAME",
"value": "..."
},
{
"name": "DATEFORMAT",
"value": "d\u0001mm\u0001yy\u0001."
}
]
}
params передавать не обязательно, filterText тоже
Не забудем также передать куки ESRNSec и заголовок at.
Ответ дожидаемся и игнорируем.
После этого можем получить результат привычным образом: https://asurso.ru/webapi/files/[fileID]
- (Необязательно) Вы можете сами отключиться от веб-сокета или завершить его с помощью сервера SignalR послав еще один POST-запрос на
https://asurso.ru/WebApi/signalr/abort
с параметрами
transport: webSockets clientProtocol: 1.5 at: вашAtКлюч connectionToken: токенПодключения connectionData: [{"name":"queuehub"}]
Тогда веб-сокет отключится сам по себе.
Форум открывается в отдельном окне, а HTML-код страницы генерируется на сервере, поэтому с получением данных возникают некоторые сложности: страницу надо парсить и селекторами искать тег table.
В сгенерированной таблице со списком тем никогда не будет открывающегося тега <tr>, только </tr>
С форума ничего нельзя удалить
Время, указанное на странице тоже генерируется на сервере, поэтому часовой пояс всех дат — Европа/Самара (UTC+4:00)
POST запрос на https://asurso.ru/webapi/assignments/[assignmentID]/answers?studentId=[studentID]
с заголовком at и телом x-www-form-urlencoded =text
— для текста (ключа нет), придет 204; для редактирования отправить тот же запрос; для удаления только =
Для прикрепления файла не нужно делать доп. запросов, просто загружаем файл и в теле указываем assignmentId
и assignmentFileResult: true
POST на https://asurso.ru/webapi/attachments с заголовком at, content-type: multipart/form-data (самостоятельно в заголовках не указывать, чтобы сгенерировался boundary!!!), тело:
file: (binary)
data: {"name":"empty.txt","description":"-"}
Название может быть любым, а description может быть null. В ответ придет ID файла в plain text
Для удаления сделайте запрос с методом DELETE на https://asurso.ru/webapi/attachments/[attachmentID]
и заголовком at.
-
\ ESRNSec составляется из множества ID сессий aspnet, разделенных символом &. То есть, переходя по страницам, вы получаете новые ID которые добавляется в один и тот же куки, не знаю зачем это нужно. Это что-то на асповском 😬
-
GET-Запрос на
https://asurso.ru/webapi/grade/assignment/types?all=false
дает нам расшифровку аббревиатур на сайте -
GET-Запрос на
https://asurso.ru/webapi/educcertificates/student/[id]
дает нам информацию о пользователе, но не так просто: для этого нужно составить сложный ESRNSec. А если подставить несуществующий studentID, то получим такое сообщение:Не удалось получить данные о сертификате.
, не знаю при чем тут сертификат. Это что-то на асурсовсом 😬 А если подставить существующий, то лично я получаю null (кроме собственного studentID) -
Разные ваши сессии отображаются как разные люди в списке онлайна
-
Сделайте GET-запрос на https://asurso.ru/webapi/attachments/uploadLimits без заголовка at, чтобы получить лимиты на загрузку файла
-
Если в адресе на любую страницу указан параметр at, то его же в теле запроса присылать не нужно, достаточно сделать GET запрос