Токен Spring Security 4 curl csrf не найден

Я знаю, что этот вопрос уже много раз задавали, и он применим по-разному, и, основываясь на вопросах, которые я уже задавал в Интернете и здесь, у меня больше вопросов, чем ответов.

Итак, у меня есть веб-сервис Spring MVC 4.0 RESTful, который, как я знаю, работает нормально. Я знаю, что он работает нормально, потому что он проходит мои модульные тесты. Модульный тест с дополнительной безопасностью и "csrf()" как часть тестовой безопасности заставляет его работать. Я заметил, что эта часть данных добавлена:

Parameters = {_csrf=[435f9968-f643-47e9-9d15-23a5cb361b1d]}

Веб-служба RESTful защищена Spring Security 4, а защита csrf включена по умолчанию. Все веб-сервисы с GET отлично работают, именно POST мешает мне с ошибкой 403.

Вот завиток, который я пытаюсь реализовать:

curl \
-d '{"SiteId\:"548","SubjectId":TEST_sub","description":"TEST_des"}' \
--cookie "csrftoken=?????????????" \
--cookie "JSESSIONID=openam~A7FF59011F982FBB91C2F908E143327D" \
--cookie  "iPlanetDirectoryPro=AQIC5wM2LY4Sfcy_7ZIwOhUsE4KK922nQEue1Bgm7wAtX_k.*AAJTSQACMDE.*; path=/; domain=.mydomain.net" \
-H "X-CSFRToken: ??????" -H "Accept: */*" -H "Content-Type: */*" \
-H "openam-token: AQIC5wM2LY4Sfcy_7ZIwOhUsE4KK922nQEue1Bgm7wAtX_k.*AAJTSQACMDE.*" \
https://spring.mydomain.net/api/subject/build

Итак, я знаю, что параметр -d явно выполняет POST; Я знаю, что --cookie для iPlanetDirectoryPro необходим для моего приложения. Я знаю, что нужны -H для Accept, Content-Type и openam_token.

Меня смущает следующее: -cookie для токена csfr... откуда я беру эти данные? -cookie для идентификатора сеанса или JSESSIONID? Когда я впервые захожу на свой веб-сайт, у меня есть файл cookie, например:

JSESSIONID=openam~A7FF59011F982FBB91C2F908E143327D; path=/sso/; domain=spring-auth.mydomain.net; Secure; HttpOnly

Теперь, когда дело доходит до заголовка: это X-CSFR-TOKEN или X-CSFRToken... Я видел оба.

У меня есть SimpleCORSFilter, но этот заголовок не является одним из разрешенных, так ли это? Я могу добавить его в список допустимых заголовков.

Меня беспокоит помещение _csfr в ​​данные JSON. Используя сопоставитель Джексона, если бы я его добавил, я боюсь, что веб-служба не будет работать, потому что _csrf не имеет соответствующего объекта, поэтому я просто предположил бы, что не буду его там, если бы я это сделал.

Итак, я сделал много проб и ошибок из завитка, и сегодня мне не повезло с этим. Когда я заставлю завиток работать, я должен передать его подрядчику, работающему над пользовательским интерфейсом. Они используют JavaScript для создания вызовов AJX на серверную часть, поэтому они могут передавать все, что им нужно, обратно в URL-адрес, отправлять данные обратно и добавлять любые заголовки или файлы cookie, которые им нужны для передачи данных обратно. Я оставляю это для них, чтобы понять.

Любая помощь в том, чтобы заставить мой завиток работать, была бы здоровой. Спасибо!

ОБНОВЛЕНИЕ: когда пользователь входит в мое приложение, мы используем производную от примера SiteMinder. То есть мы получаем cookie от OpenAM при успешной аутентификации. Затем мы используем пример siteminder для вызова CustomUserDetailsService, который использует этот файл cookie для вызова OpenAM и получения имени пользователя. Затем мы выполняем поиск в базе данных по этому имени пользователя, чтобы получить роли для этого пользователя. Ни в одном из этих случаев мы не получаем какой-либо токен csrf. Итак, для всех примеров, которые я видел на StackOverflow, у меня НЕТ токена csrf, который я мог бы вернуть. При этом я добавил следующий веб-сервис RESTful.

@RequestMapping(value = "/csrf-token", method = RequestMethod.GET)
public @ResponseBody String getCsrfToken(HttpServletRequest request)
{
    CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
    return token.getToken();
}

Я могу успешно вызвать этот URL-адрес в любое время и вернуть токен обратно клиенту. Например: "45757c25-1af7-40d3-ab8b-1a9cd823efc5"

Итак, я тестирую это в двух местах: RESTClient для Firefox и curl...

в RESTClient я могу сделать запрос и получить токен csrf, затем я пытаюсь поместить его в RESTClient следующим образом:

URL: https://spring.mydomain.net/api/subject/build?_csrf=[45757c25-1af7-40d3-ab8b-1a9cd823efc5]
Headers:  
openam_token: AQIC5wM2LY4SfczkVMBTIq4pyjcec7QkyN0PJbY3NdmF8WE.*AAJTSQACMDE.*
X-CSRF-TOKEN: [45757c25-1af7-40d3-ab8b-1a9cd823efc5]

Есть ли что-то еще, что мне нужно, потому что это еще не работает.

И в завитке:

curl \
-d '{"SiteId\:"548","SubjectId":TEST_sub","description":"TEST_des"}' \
--cookie "csrftoken=45757c25-1af7-40d3-ab8b-1a9cd823efc5" \
--cookie "JSESSIONID=openam~A7FF59011F982FBB91C2F908E143327D" \
--cookie  "iPlanetDirectoryPro=AQIC5wM2LY4Sfcy_7ZIwOhUsE4KK922nQEue1Bgm7wAtX_k.*AAJTSQACMDE.*; path=/; domain=.mydomain.net" \
-H "X-CSFR-TOKEN: 45757c25-1af7-40d3-ab8b-1a9cd823efc5" 
-H "Accept: */*" -H "Content-Type: */*" \
-H "openam-token: AQIC5wM2LY4Sfcy_7ZIwOhUsE4KK922nQEue1Bgm7wAtX_k.*AAJTSQACMDE.*" \
https://spring.mydomain.net/api/subject/build

Опять же, что мне не хватает, чтобы сделать эту работу????

Должен ли я как-то комбинировать свой вызов в RESTClient, чтобы получить этот токен, И затем на мой POST? Как мне объединить их в curl? Я знаю, что на сеанс приходится один токен csrf, я хочу знать, что могу сделать один вызов и получить этот токен, а затем использовать его в следующем же запросе для передачи обратно для POST.

Я не занимаюсь внешним интерфейсом, у нас есть другая компания, которая работает над пользовательским интерфейсом и вызовами Ajax, я хочу иметь возможность протестировать свой вызов веб-службы, прежде чем я передам его им.

Спасибо еще раз!


person tjholmes66    schedule 29.01.2016    source источник


Ответы (2)


После долгих проб и ошибок я наконец узнал, как я могу заставить это работать.

У меня была правильная идея, основанная на предыдущей публикации, чтобы мой собственный контроллер возвращал токен, и это выделено выше. Когда я позвонил в эту службу, чтобы получить токен, я также позаботился о том, чтобы получить файл cookie для JSESSION. ЭТО сеанс, который запросил запрос, поэтому я должен был убедиться, что я схватил его, как только я получил токен обратно:

Cookie:  JSESSIONID=3A74721AD18E758D7B8DC54FB32A6515; Path=/services/; HttpOnly
csrf_token: db7e3100-bd2d-4ecf-8c0d-20b8b4e17b20

Теперь, либо с Firefox RESTClient, либо с curl, вам понадобятся две вещи:

curl \
-d '{"SiteId\:"548","SubjectId":TEST_sub","description":"TEST_des"}' \
--cookie '\JSESSIONID=3A74721AD18E758D7B8DC54FB32A6515;' \
--cookie  "iPlanetDirectoryPro=AQIC5wM2LY4Sfcy_7ZIwOhUsE4KK922nQEue1Bgm7wAtX_k.*AAJTSQACMDE.*; path=/; domain=.mydomain.net" \
-H "X-CSFR-TOKEN: db7e3100-bd2d-4ecf-8c0d-20b8b4e17b20" 
-H "Accept: */*" -H "Content-Type: application/json" \
-H "openam-token: AQIC5wM2LY4Sfcy_7ZIwOhUsE4KK922nQEue1Bgm7wAtX_k.*AAJTSQACMDE.*" \
https://spring.mydomain.net/api/subject/build

Обратите внимание, что файл cookie для идентификатора JSESSION взят из моего запроса, который в первую очередь получил токен csrf (db7e3100-bd2d-4ecf-8c0d-20b8b4e17b20). Обратите внимание, что мне нужен X-CSFR-TOKEN для включения токена, и я включил этот заголовок в SimpleCORSFilter.

Итак, это сработало!!!!! Мне удалось правильно передать токен по URL-адресу, и я вижу, что он регистрируется. Я вижу доступ к контроллеру и данные, которые я передал.

Контроллер REST определяется как:

@RequestMapping(value = "/build", method = RequestMethod.POST, produces = "application/json", headers = "Content-Type=application/json")
public MyEntity build(@RequestBody SubjectImportDTO subjectImportDTO)
{
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    User user = null;
    if (principal instanceof User)
    {
        user = ((User) principal);
    }
    MyEntity entity = service.build(subjectImportDTO);
    return entity;
}

Теперь URL-адрес найден, вызывается, выполняет работу с серверной частью и готов вернуть объект ... НО я получаю ошибку 404 либо от curl, либо от Firefox RESTClient. Я думал, что возникает ошибка 404, если мы даже не можем найти конечную точку REST для начала, так что это еще один вопрос, который у меня есть.

person tjholmes66    schedule 02.02.2016
comment
Ошибка 404 после вызова была связана с тем, что в этом методе метода сборки не было @ResponseBody, потому что я использовал @Controller в этом классе. общедоступная сборка MyEntity(@RequestBody SubjectImportDTO subjectImportDTO) должна быть: public @ResponseBody MyEntity build(@RequestBody SubjectImportDTO subjectImportDTO) ... и это решило эту проблему. - person tjholmes66; 29.02.2016
comment
Опечатка в команде curl: X-CSFR-TOKEN - это csRF, поэтому X-CSRF-TOKEN ... на случай, если кто-то другой скопирует и вставит его и удивится, почему он не работает. :) - person rudi; 17.05.2018

Я не думаю, что это полностью отвечает на ваш вопрос, но вот что я сделал, чтобы добавить токен csrf:

$(function () {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
        xhr.setRequestHeader(header, token);
    });
});

Я посмотрел на один из заголовков моего запроса и его «X-CSRF-TOKEN».

Что касается curl, я нашел другие способы тестирования, обычно путем отключения CSRF в отдельной тестовой конфигурации.

person Brian Kates    schedule 29.01.2016
comment
Одна из вещей, которые я обнаружил, заключается в том, что токен csrf сначала передается клиенту. Я вообще не захожу через свое веб-приложение, по крайней мере, не так, как обычно. Ни в одном из моих запросов к бэкэнду, где я выполняю HTTP GET, токена csrf вообще нет. Как мне заставить запрос GET вернуть мне токен, чтобы я мог повторно использовать его в будущем POST? - person tjholmes66; 29.01.2016