Угловой запрос PUT запрещен через SSL

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

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

на моих контроллерах Spring Boot Rest у меня есть аннотация @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

угловая служба работает на https://localhost:4200.

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

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

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

EDIT: добавить конфигурацию безопасности Spring Boot

<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: добавить конфигурацию углового Прокси

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

8 ответов


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

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

<csrf/>

в:

<csrf disabled="true"/>

начиная с весны 4 Защита CSRF включена по умолчанию так просто удаление <csrf/> будет недостаточно.

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

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

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

Ваш браузер, вероятно, не создает SSL-соединение. Это обычно происходит, когда браузер находит неправильный сертификат или самозаверяющий сертификат или сертификат, подписанный сертификацией Авторитет, которому не доверяют.

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

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

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


Я думаю, что у вас должен быть маркер X-XSRF-TOKEN в заголовке запроса, так как csrf включен в вашем Security 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)})

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


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

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

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

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


я столкнулся с аналогичной проблемой, мой интерфейс находится в Angular4, Back-end весной и аутентификация, которую я использовал, была аутентификацией на основе 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

блог в основном просит вас создать промежуточное ПО между всеми запросами, передаваемыми из пользовательского интерфейса в интерфейс, и если вы знаете, что ваше приложение является подлинным, добавьте

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

в середине изделия. Надеюсь, это поможет вам.


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

подробнее

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


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

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


изменение конфигурации 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;
}