как аутентифицировать веб-сайт с несколькими хостами shibboleth с помощью httr в R

примечание: ipums international и ipums usa, вероятно, используют одну и ту же систему. ipums usa позволяет быстрее зарегистрироваться. если вы хотите протестировать свой код, попробуйте https://usa.ipums.org/usa-action/users/request_access, чтобы зарегистрироваться!

я пытаюсь программно загрузить файл с https://international.ipums.org/ с помощью языка R и хттр. мне нужно использовать httr, а не RCurl, потому что мне нужно после аутентификации загружать большие файлы не в ОЗУ, а непосредственно на диск. в настоящее время это возможно только с httr как насколько я знаю

приведенный ниже воспроизводимый код документирует все мои попытки получить доступ со страницы входа (https://international.ipums.org/international-action/users/login) на главную страницу пост-аутентификации. любые советы или подсказки будут оценены! Спасибо!

my_email <- "[email protected]"
my_password <- "password"

tf <- tempfile()

# use httr, because i need to download a large file after authentication
# and only httr supports that with its `write_disk()` option
library(httr)

# turn off ssl verify, otherwise the subsequent GET command will fail
set_config( config( ssl_verifypeer = 0L ) )

GET( "https://international.ipums.org/Shibboleth.sso/Login?target=https%3A%2F%2Finternational.ipums.org%2Finternational-action%2Fmenu" )

# connect to the starting login page of the website
( a <- GET( "https://international.ipums.org/international-action/users/login" , verbose( info = TRUE ) ) )

# which takes me through to a lot of websites, but ultimately (in my browser) lands at
shibboleth_url <- "https://live.identity.popdata.org:443/idp/Authn/UserPassword"

# construct authentication information?
base_values <- list( "j_username" = my_email , "j_password" = my_password )
idp_values <- list( "j_username" = my_email , "j_password" = my_password ,  "_idp_authn_lc_key"=subset( a$cookies , domain == "live.identity.popdata.org" )$value , "JSESSIONID" = subset( a$cookies , domain == "#HttpOnly_live.identity.popdata.org" )$value )
ipums_values <- list( "j_username" = my_email , "j_password" = my_password ,  "_idp_authn_lc_key"=subset( a$cookies , domain == "live.identity.popdata.org" )$value , "JSESSIONID" = subset( a$cookies , domain == "international.ipums.org" )$value)

# i believe this is where the main login should happen, but it looks like it's failing
GET( shibboleth_url , query = idp_values )
POST( shibboleth_url , body = base_values )
writeBin( GET( shibboleth_url , query = idp_values )$content , tf )

readLines( tf )
# The MPC account authentication system has encountered an error
# This error can sometimes occur if you did not close your browser after logging out of an application previously.  It may also occur for other reasons.  Please close your browser and try your action again."                                                                      

writeBin( GET( "https://live.identity.popdata.org/idp/profile/SAML2/Redirect/SSO" , query = idp_values )$content , tf )
POST( "https://live.identity.popdata.org/idp/profile/SAML2/Redirect/SSO" , body = idp_values )
readLines( tf )
# same error as above

# return to the main login page..
writeBin( GET( "https://international.ipums.org/international-action/menu" , query = ipums_values )$content , tf )
readLines( tf )
# ..not logged in

person Anthony Damico    schedule 16.01.2016    source источник
comment
Думали ли вы о RSelenium для этого?   -  person Thomas    schedule 20.01.2016
comment
@Thomas, привет, я не знаю, с чего начать. я был бы открыт для него, пока он может загрузить произвольно большой файл после аутентификации (что httr может, но RCurl не может)   -  person Anthony Damico    schedule 20.01.2016
comment
не так много, чтобы попробовать, не имея там учетной записи :(   -  person cyberj0g    schedule 20.01.2016
comment
@cyberj0g см. редактирование вверху, вы сможете легко получить учетную запись   -  person Anthony Damico    schedule 20.01.2016


Ответы (2)


Вы должны использовать set_cookies() для отправки файлов cookie на сервер:

library(httr)
library(rvest)
#my_email <- "xxx"
#my_password <- "yyy"
tf <- tempfile()
set_config( config( ssl_verifypeer = 0L ) )

# Get first page
p1 <- GET( "https://international.ipums.org/international-action/users/login" , verbose( info = TRUE ) )

# Post Login credentials
b2 <- list( "j_username" = my_email , "j_password" = my_password )
c2 <- c(JSESSIONID=p1$cookies[p1$cookies$domain=="#HttpOnly_live.identity.popdata.org",]$value,
           `_idp_authn_lc_key`=p1$cookies[p1$cookies$domain=="live.identity.popdata.org",]$value)
p2 <- POST(p1$url,body = b2, set_cookies(.cookies = c2), encode="form" )

# Parse hidden fields
h2 <- read_html(p2$content)
form <-  h2 %>% html_form() 

# Post hidden fields
b3 <- list( "RelayState"=form[[1]]$fields[[1]]$value, "SAMLResponse"=form[[1]]$fields[[2]]$value)
c3 <- c(JSESSIONID=p1$cookies[p1$cookies$domain=="#HttpOnly_live.identity.popdata.org",]$value,
           `_idp_session`=p2$cookies[p2$cookies$name=="_idp_session",]$value,
           `_idp_authn_lc_key`=p2$cookies[p2$cookies$name=="_idp_authn_lc_key",]$value)
p3 <- POST( form[[1]]$url , body=b3, set_cookies(.cookies = c3), encode = "form")

# Get interesting page
c4 <- c(JSESSIONID=p3$cookies[p1$cookies$domain=="international.ipums.org" && p3$cookies$name=="JSESSIONID",]$value,
           `_idp_session`=p3$cookies[p3$cookies$name=="_idp_session",]$value,
           `_idp_authn_lc_key`=p3$cookies[p3$cookies$name=="_idp_authn_lc_key",]$value)
p4 <- GET( "https://international.ipums.org/international-action/menu", set_cookies(.cookies = c4) )
writeBin(p4$content , tf )
readLines( tf )[55]

Так как результат

[1] "    <li class=\"lastItem\"><a href=\"/international-action/users/logout\">Logout</a></li>"

Я думаю, вы вошли в систему...

person HubertL    schedule 21.01.2016
comment
идеально. Благодарю. указано здесь github.com/ajdamico/asdfree/commit/ - person Anthony Damico; 23.01.2016

@HubertL сделал много шагов в правильном направлении, однако, я думаю, его ответ неполный.

Прежде всего, главное, на что следует обратить внимание при реализации автоматической веб-авторизации, — это файлы cookie, используемые во время «обычного» ручного рабочего процесса. Вы можете легко следить за ними с помощью инструментов разработки в любом современном браузере: введите здесь описание изображения

Здесь мы видим JSESSIONID и _shibsession* куки, первый содержит идентификатор сеанса JSP веб-сайта, второй, скорее всего, предназначен исключительно для авторизации шибболета. Сервера, наверное, как-то привязаны, но JSESSIONID не требует авторизации и вы ее получаете сразу после открытия сайта. Итак, мы должны получить _shibsession* cookie для авторизации нашего JSESSIONID. Вот что такое процесс авторизации Shibboleth с множеством редиректов. Смотрите комментарии в коде.

login_ipums = function(user, password)
{
  require(httr)
  require(rvest)

  set_config( config( ssl_verifypeer = 0L ) )

  #important - httr preserves cookies on subsequent requests to the same host, we don't need that because of sessions expiration
  handle_reset("https://usa.ipums.org/")

  #set login and password
  login1 = GET( "https://usa.ipums.org/usa-action/users/login" )
  form_auth = list( "j_username" = user , "j_password" = password )

  l1_cookies=login1$cookies$value
  names(l1_cookies)=login1$cookies$name

  #receive auth tokens as html hidden fields in a form
  login2 = POST(login1$url, body = form_auth, set_cookies(.cookies=l1_cookies), encode="form")
  login2_form = read_html(login2$content) %>% html_form() 

  l2_cookies=login2$cookies$value
  names(l2_cookies)=login2$cookies$name

  #submit the form back (browser submits it back automatically with JS)
  login3 = POST(login2_form[[1]]$url, body=list(RelayState=login2_form[[1]]$fields$RelayState$value, 
                                                SAMLResponse=login2_form[[1]]$fields$SAMLResponse$value), 
                set_cookies(.cookies=l2_cookies), 
                encode="form")

  #now we have what we came for - _shibsession_* and JSESSION id cookie
  login_cookies = login3$cookies$value
  names(login_cookies)=login3$cookies$name

  return=login_cookies
}

После вызова login_ipums у нас будут следующие файлы cookie:

> cookies=login_ipums(my_email, my_password)
> names(cookies)
[1] "JSESSIONID"      
[2] "_idp_authn_lc_key"             
[3] "_shibsession_7573612e69..."

Здесь у нас есть и JSESSIONID, и _shibsession_*, используемые для авторизации на уровне сайта. _idp_authn_lc_key, наверное, не нужен, но оставить не помешает.

Теперь вы можете легко загружать такие файлы:

cookies=login_ipums(my_email, my_password)
target = GET("https://usa.ipums.org/usa-action/downloads/extract_files/usa_00001.dat.gz",
         set_cookies(.cookies=cookies),
         write_disk("file.bin", overwrite = TRUE))

ВАЖНОЕ ПРИМЕЧАНИЕ. Как видите, я использовал IPUMS USA, а не International. Чтобы проверить этот код в своей учетной записи, замените usa на international везде, включая *-action в URL-адресах.

person cyberj0g    schedule 23.01.2016
comment
удивительно, спасибо! Код @HubertL у меня работает, похоже, у вас тоже. благодарю вас!! - person Anthony Damico; 23.01.2016
comment
Хорошо, но обратите внимание, что код @HubertL никогда не устанавливает файл cookie _shibsession_* (фактический токен авторизации) явно, он (возможно, непреднамеренно) полагается на механизм сохранения файлов cookie httr, и это может быть проблемой при производстве. - person cyberj0g; 23.01.2016