Angular Запрос PUT запрещен по SSL

Я использую Angular 5 в качестве интерфейса с сервером загрузки Spring Boot REST. Все работает нормально, если не использовать SSL. Когда я переключаюсь на SSL, в конце концов, у меня все работает. Он работает для запросов GET, но до сих пор я не могу получить запрос PUT для прохождения.

Я предполагаю, что это какая-то проблема CORS, учитывая, что GET - простой запрос, и PUT, по-видимому, не (ссылка CORS), но я не могу понять, как исправить эту проблему.

На моих Spring Boot Rest Controllers у меня есть аннотация @CrossOrigin("*"), поэтому я не думаю, что это проблема, но я не уверен.

Другая часть головоломки заключается в том, что аутентификация осуществляется через сервер CAS. Я добавил следующую конфигурацию для свойств CAS. Это была заключительная часть, которая позволяла работать с GET, но я не уверен, что с ними поделать (если что угодно) для обработки запросов PUT:

cas.httpWebRequest.cors.enabled=true
cas.httpWebRequest.cors.allowOrigins[0]=*
cas.httpWebRequest.cors.allowMethods[0]=*
cas.httpWebRequest.cors.allowHeaders[0]=*

Вот мои заголовки запросов и ответ:

Запрос:

Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Connection: keep-alive
Content-Length: 1975
Content-Type: application/json
Cookie: JSESSIONID=117D9345E985D824E46…BF32; io=gLhCcBoZrfNcppioAAAB
Host: localhost:4200
Referer: https://localhost:4200/sales/proposals/dashboard
User-Agent: Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/57.0

Ответ (код состояния - 403 запрещен):

access-control-allow-origin: *
cache-control: no-cache, no-store, max-age=0, must-revalidate
content-length: 56
content-type: application/json;charset=UTF-8
date: Wed, 03 Jan 2018 16:39:14 GMT
expires: 0
pragma: no-cache
strict-transport-security: max-age=31536000 ; includeSubDomains
x-content-type-options: nosniff
X-Firefox-Spdy: h2
x-frame-options: DENY
x-powered-by: Express
x-xss-protection: 1; mode=block

Служба Angular работает на https://localhost:4200.

Служба загрузки Spring работает на https://localhost:8493.

Служба CAS работает на https://localhost:8443.

Нет сообщений об ошибках, которые я видел в любом из журналов. Я хотел бы иметь возможность понять, почему запрос PUT запрещен, а затем как его исправить, чтобы запрос PUT также работал. Спасибо!

EDIT: Добавить Spring Конфигурация безопасности загрузки

<http pattern="/**" entry-point-ref="casEntryPoint">
            <intercept-url pattern="/api/holidays" access="permitAll"/>
            <intercept-url pattern="/api/unit**" access="permitAll"/>
            <intercept-url pattern="/**" access="isAuthenticated()" />

            <custom-filter ref="casAuthenticationFilter" before="CAS_FILTER"/>

            <csrf/>         
    </http>

    <global-method-security pre-post-annotations="enabled"/>

    <!-- CAS Config -->
    <beans:bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
            <beans:property name="loginUrl" value="${cas.server.host.login_url}"/>
            <beans:property name="serviceProperties" ref="serviceProperties"/>
    </beans:bean>

    <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
            <beans:property name="service" value="${app.server.host.url}login/cas"></beans:property>
    </beans:bean>

    <beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
            <beans:property name="authenticationManager" ref="authenticationManager"/>
    </beans:bean>

    <beans:bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
            <beans:property name="ticketValidator" ref="ticketValidator"></beans:property>
            <beans:property name="serviceProperties" ref="serviceProperties"></beans:property>
            <beans:property name="key" value="Key"></beans:property>
            <beans:property name="authenticationUserDetailsService" ref="userDetailsWrapper"/>
    </beans:bean>

    <beans:bean id="userDetailsWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
        <beans:property name="userDetailsService" ref="userDetails"></beans:property>
    </beans:bean>

    <ldap-user-service id="userDetails"
        server-ref="ldapServer"
        group-search-base="ou=ERPGroups,OU=MyBusiness"
        group-search-filter="(member={0})"
        user-search-base="ou=SBSUsers,OU=Users,OU=MyBusiness"
        user-search-filter="(sAMAccountName={0})" />

    <ldap-server id="ldapServer" url="${ldap.urls}/${ldap.base}" manager-dn="${ldap.username}" manager-password="${ldap.password}" />

    <beans:bean id="ticketValidator" class="org.jasig.cas.client.validation.Cas30ServiceTicketValidator">
            <beans:constructor-arg value="${cas.server.host.url}"></beans:constructor-arg>
    </beans:bean>

    <authentication-manager alias="authenticationManager">  
        <authentication-provider ref="casAuthenticationProvider" />
    </authentication-manager>

EDIT: Добавить Angular Конфигурация прокси-сервера

{
    "/api": {
        "target" : "https://localhost:8493",
        "changeOrigin": true,
        "secure" : false
    }
}

Ответ 1

CORS применяется браузером, но в этом случае вы получаете 403 с сервера. Как отмечали другие, в вашем запросе отсутствует заголовок CSRF и, вероятно, почему сервер отклоняет ваш запрос. Я не могу понять, почему это произошло, когда вы перешли на HTTPS, но я могу сказать, что у вас есть <csrf/> в вашей конфигурации, но нет заголовка для соответствия.

Чтобы проверить эту теорию, вы можете отключить защиту CSRF, изменив:

<csrf/>

в

<csrf disabled="true"/>

Начиная с Spring 4 CSRF-защита включена по умолчанию, поэтому просто удалить <csrf/> будет недостаточно.

Как только вы подтвердили, что CSRF был проблемой, и вы больше не получаете 403, вы должны включить его обратно таким образом, чтобы Angular поддерживал. Это важная защита, которая защитит ваших пользователей. Angular ожидает файл cookie с именем XSRF-TOKEN, который он отправит обратно с заголовком X-XSRF-TOKEN. Они настраиваются, но должны соответствовать Spring по умолчанию, если вы используете последние Spring и последние Angular. Следуйте Spring документации, чтобы добавить:

<http>
    <!-- ... -->
    <csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
    class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
    p:cookieHttpOnly="false"/>

Ответ 2

В вашей конфигурации безопасности загрузки spring я вижу, что csrf включен. Но в вашем запросе я не вижу cookie XSRF-TOKEN и заголовок X-XSRF-TOKEN. Вот почему сервер не принимает запрос и отвечает с ответом 403.

Возможно, ваш браузер не создает соединение SSL. Обычно это происходит, когда браузер находит неверный сертификат или сам подписанный сертификат или сертификат, подписанный центром сертификации, которому не доверяют.

Если вы используете браузер Chrome, вы можете включить его для локального хоста для целей тестирования, включив свойство allow-insecure-localhost. Введите этот адрес в адресной строке, нажмите Включить и перезапустите Chrome.

chrome://flags/#allow-insecure-localhost

Обратитесь к этой ссылке за дополнительной информацией, чтобы включить ее в chrome и firefox. https://improveandrepeat.com/2016/09/allowing-self-signed-certificates-on-localhost-with-chrome-and-firefox/

Ответ 3

Я думаю, что у вас должен быть токен X-XSRF-TOKEN в заголовке запроса, так как csrf включен в вашем conf conf безопасности. Для меня это сработало:

 constructor(private http: HttpClient, private tokenExtractor: HttpXsrfTokenExtractor) {
    }

Затем извлеките токен

const token = this.tokenExtractor.getToken() as string;

и добавьте его в заголовок

this.http.post<any>(url, body, {headers: new HttpHeaders().set('X-XSRF-TOKEN', token)})

Надеюсь, это поможет.

Ответ 4

HTTP-запросы, которые потенциально могут что-то изменить на сервере, такие как PUT, POST, DELETE и PATCH, не знают, разрешено ли им исполнять запрос, если они не были указаны заранее.

Они могут знать, читая заголовки более раннего запроса, которые позволили им изменить запрошенный ресурс, или они будут запускать запрос OPTIONS непосредственно перед этим. Это называется "предполетным" запросом.

Если вы не внедрили метод OPTIONS для ответа заголовками CORS, это то, что вам нужно сделать.

Посмотрите здесь для более подробной информации.

Ответ 5

Я столкнулся с аналогичной проблемой: My frontEnd находится в Angular4, Back-end в Spring, а аутентификация, которую я использовал, была проверкой на основе JWT. Я получал эту ошибку для моего запроса POST, но, я думаю, это относится и к PUT, так как оба относятся к "непростым запросам" (псевдоним для предполетного запроса).

Мой пользовательский интерфейс работает некоторое время, а затем я получаю следующую ошибку:

Невозможно загрузить https://localhost:8080/myApp/services/stationCollection/viewCollected. В запрошенном ресурсе нет заголовка "Access-Control-Allow-Origin".

После анализа я узнал, что запрос перед полетом проходит с полным успехом, его основной запрос, которому становится отказано в доступе к серверу (вы можете проверить, что на вкладке Сеть в инструментах chrome dev и получить детали). Тип отправляемого запроса CORS - это "непростые запросы". Тот факт, что, если вы отправляете один и тот же запрос, на тот же сервер с Rest Client/curl, он отлично работает.

Когда вы просматриваете эту ошибку в Интернете, вы обнаруживаете потерянные решения, все приводящие к настройке CORS на стороне сервера, но тот факт, что я уже реализовал CORS.

Возможное решение:

Все это привело к тому, что я нашел решение для одного и того же, и после многократного поиска я обнаружил, что существует возможность взлома/альтернативы для этой проблемы, как лучше всего определено в следующем блоге:

https://laracasts.com/discuss/channels/laravel/cros-access-control-allow-origin-not-in-the-headers

В блоге в основном предлагается создать промежуточное программное обеспечение, между всеми запросами, передаваемыми из интерфейса пользователя, в Front-end, и если вы знаете, что ваше приложение является аутентичным, добавьте

myOptions.headers.append('Access-Control-Allow-Origin', '*');

в средней посуде. Надеюсь, это поможет вам.

Ответ 6

Попробуйте добавить этот заголовок со стороны сервера Access-Control-Allow-Credentials: true

дополнительная информация

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

Ответ 7

Почему бы вам не попробовать это в своем загрузочном приложении Spring?

HttpHeaders headers = new HttpHeaders();

headers.add("Access-Control-Allow-Origin", "*");
headers.add("Access-Control-Allow-Credentials", "true");
headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS, HEAD");
headers.add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");

return new ResponseEntity<>(service.update(input), headers, HttpStatus.OK);

Более подробные данные можно найти здесь https://spring.io/blog/2015/06/08/cors-support-in-spring-framework

Ответ 8

Изменение конфигурации Spring, чтобы позволить CORS, возможно, не очень хорошая идея. То, что я сделал, это установить сервер nginx и использовать его в качестве обратного прокси. Ниже приведен пример nginx.conf:

# Spring backend
location /api
{
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_pass http://localhost:8493;
}

# Angular front end
location /
{
  root www/apps;
  try_files $uri $uri/ /index.html;
}