Обеспечение интегрированной безопасности и работы CORS с Asp.Net, Angular 6 и IIS - PullRequest
0 голосов
/ 25 октября 2018

У меня есть приложение с интерфейсом Angular и веб-сервисом Asp.Net.Я использую сущность HttpClient в Angular для выполнения вызовов веб-службы. Я настроил CORS, и мои GET отлично работают во всем приложении, используя следующую базовую структуру

let url = environment.base_url + 'api/SubDoc/GetDocument?docID=' + docID;

this.httpClient.get(url,
  {
    withCredentials: true
  })
  .subscribe(
    response => {
      console.log(response);
      newData = response;
      this.currentDocument = newData;
    }
  );

Я доказал, что CORS работает, потому чтоЯ могу подключиться к одной и той же конечной точке службы из сеанса локального хоста или из сеансов приложений, запущенных на нашем QA и наших производственных серверах, если я правильно отредактирую значения Origin при вызове заголовков и методов EnableCors

        config.EnableCors(new EnableCorsAttribute(origins: "http://localhost:2453,http://localhost:2452,http://***,http://10.100.101.46", headers: headers, methods: methods) { SupportsCredentials = true });

определены как следующие строки:

        headers = "Origin, Content-Type, X-Auth-Token, content-type,application/x-www-form-urlencoded";
        methods = "GET,PUT,POST,OPTIONS,DELETE";

Однако я не могу сделать запрос PUT для той же службы, используя следующую структуру HttpClient

let url = this.base_url + 'api/ContactGroup/Put';
// Try the new HttpClient object
this.httpClient.put(url, body,
  {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
    withCredentials: true
  })
  .subscribe(
    response=>{
      let temp:number;
      temp = parseInt(response.toString(),10);
      if(!isNaN(temp)){
        this.groupID = temp;          
      }
    }
  );

Этот запрос PUT отлично работает, когдаЯ подключаюсь с того же сервера (т. Е. CORS не применяется), но происходит сбой с ошибкой предварительной проверки при попытке подключения с любого другого сервера.

Не удалось загрузить http://***/api/ContactGroup/Put: Ответ на запрос предварительной проверки не проходит контроль доступаcheck: нет заголовка 'Access-Control-Allow-Origin'присутствует на запрашиваемом ресурсе.Происхождение 'http://localhost:2452' поэтому не разрешено.

Как я объяснил выше, у меня есть определенные заголовки Access-Control-Allow-Origin, и я считаю, что я включил PUT и OPTIONS в качестве поддерживаемых методов.

Я думаю, что проблема связана с тем, что запрос PUT не отправляет учетные данные.У запросов GET есть элемент учетных данных в заголовке запроса, но PUT нет, хотя я использую опцию withCredentials.

Вот как выглядят заголовки для запроса PUT:

Request URL: http://reitgc01/REITServicesQA/api/ContactGroup/Put
Request Method: OPTIONS
Status Code: 401 Unauthorized
Remote Address: 10.100.101.46:80
Referrer Policy: no-referrer-when-downgrade
Content-Type: text/html
Provisional headers are shown
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: PUT
DNT: 1
Origin: http://localhost:2452
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36

Я потратил несколько дней на эту проблему, и любая помощь будет принята с благодарностью.

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Вот как я решаю этот точный пример.Я использую Angular 5 с включенной WebApi и аутентификацией Windows.Материал CORS подробно описан здесь

TL; DR;этой статьи вам нужны как анонимные (потому что заголовки CORS не несут аутентификационные данные), так и аутентификации Windows (чтобы вы могли защитить свое приложение).

Итак, в моем проекте webapi у меня есть класс CorsConfig, которыйустанавливает мой CORS

public static class CorsConfig
{
    public static void Register(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute(ConfigurationManager.AppSettings["CorsClientUrl"], "*", "*")
        {
            SupportsCredentials = true
        };

        config.EnableCors(cors);
    }
}

, зарегистрируйте его в HttpConfiguration

CorsConfig.Register (config);

В вашей веб-конфигурации под

<authorization>
  <allow verbs="OPTIONS" users="*" />
  <allow users="<your user list>" />
  <deny users="*" />
</authorization>

Это позволит пройти проверку ВАРИАНТОВ для всех пользователей.Для всех остальных глаголов я запрещаю всем и разрешаю только определенный список пользователей.Вы можете изменить это, чтобы использовать роли.

Со стороны Angular я создал перехватчик, который будет помещать учетные данные во все http-запросы.

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';


@Injectable()
export class WinAuthInterceptor implements HttpInterceptor {
  constructor() { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = req.clone({
      withCredentials: true
    });

    return next.handle(req);
  }
}

Зарегистрируйте это в файле app.module.ts в списке поставщиков

  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: WinAuthInterceptor, multi: true }
  ]
0 голосов
/ 26 октября 2018

Спасибо Sideshowbarker за то, что он указал мне правильное направление.Я, наконец, выяснил фундаментальную проблему, с которой я боролся, и реализовал решение.

Основная проблема заключается в том, что наш сайт является внутренним сайтом, который использует встроенную защиту для соединений, и это создает несовместимость с поддержкой CORS и CORS.в IIS.Внешний интерфейс представляет собой интерфейс Angular 6 с веб-службой ASP.NET.Чтобы использовать встроенную безопасность, вы должны отключить анонимную аутентификацию.Кроме того, чтобы заставить аутентификацию работать, вы должны передать {withCredentials: true} с вашими запросами.Это прекрасно работает для запросов GET, потому что спецификация CORS не требует предварительных опций для этих запросов, а заголовок запроса GET будет иметь требуемый токен учетных данных для правильного разрешения.

Проблема вступает в игру сPUT запросы.CORS требует предварительный запрос OPTIONS для PUT, но запрос OPTIONS не включает учетные данные, даже если вы установили флаг withCredentials: true.Это означает, что вы должны включить анонимную аутентификацию, которая нарушит вашу встроенную безопасность.Немного подвох 22. Я не смог найти способ включить анонимный вход только для ОПЦИЙ.Я нашел пару постов StackOverflow, в которых утверждалось, что вы можете сделать это в IIS, но я не смог заставить это работать.Я также нашел сообщения от MS, в которых говорилось, что IIS не поддерживает эту функцию.Это означает, что вы должны написать собственный обработчик для запроса OPTIONS и сгенерировать правильный ответ, чтобы удовлетворить браузер.Есть несколько сообщений на эту тему, но лучше всего я нашел ответ Stu на эту тему 401 ответ на запрос CORS в IIS с включенной аутентификацией Windows

С незначительным улучшением, которое я смог получитьэто работать на меня.Мне нужна поддержка нескольких доменов в моей конфигурации системы, и я все еще получаю сообщение об ошибке, если я помещаю несколько URL-адресов в поле источника.Я использовал поле Origin в заголовке Request.Headers, чтобы получить текущее происхождение, и отправил это значение обратно в свой построенный заголовок ответа, как показано в приведенном ниже коде:

EDIT: Поработав некоторое время с этим, я понял, чтооригинальное решение имело недостаток.Мой оригинальный код явно не проверял источник, чтобы убедиться, что это разрешено.Как я уже говорил ранее, это было нормально для операций GET, потому что GET все равно потерпел бы неудачу (и вызов OPTIONS в любом случае необязателен для GET), но для PUT клиент передал бы вызов OPTIONS и сделал бы запрос PUT и получил бы ошибку в ответе.Тем не менее, сервер все равно будет выполнять запрос PUT, так как CORS принудительно выполняется на стороне клиента.Это приведет к вставке или обновлению данных, когда это не должно происходить.Решение этой проблемы заключается в явной проверке обнаруженного источника по списку источников CORS, который хранится в web.config, и возвращает статус 403 и пустое значение источника, если запрашивающий источник недействителен.Обновленный код указан ниже

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        // Declare local variables to store the CORS settings loaded from the web.config
        String origins;
        String headers;
        String methods;

        // load the CORS settings from the webConfig file
        origins = ConfigurationManager.AppSettings["corsOrigin"];
        headers = ConfigurationManager.AppSettings["corsHeader"];
        methods = ConfigurationManager.AppSettings["corsMethod"];


        if (Request.HttpMethod == "OPTIONS")
        {
            // Get the Origin from the Request header. This should be present for any cross domain requests
            IEnumerable<string> headerValues = Request.Headers.GetValues("Origin");
            var id = headerValues.FirstOrDefault();

            String httpOrigin = null;
            // Assign the origin value to the httpOrigin string.
            foreach (var origin in headerValues)
            {
                httpOrigin = origin;
            }

            // Construct the Access Control headers using the derived origin value and the desired content.
            if (httpOrigin == null) httpOrigin = "*";

            // Check to see if the origin is included in the CORS origin list
            if (origins.IndexOf(httpOrigin) < 0)
            {
                // This has failed the CORS check so send a blank origin list and a 403 status (forbidden)
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "");
                HttpContext.Current.Response.StatusCode = 403;
            }
            else
            {
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", httpOrigin);
                HttpContext.Current.Response.StatusCode = 200;
            }

            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With,content-type, Content-Type, Accept, X-Token");
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");


            var httpApplication = sender as HttpApplication;
            httpApplication.CompleteRequest();
        }

    }

Этот код был добавлен в метод Application_BeginRequest в файле global.asax.cs.Кроме того, подпись этого метода была изменена, чтобы включить объект отправителя, как показано здесь: protected void Application_BeginRequest (отправитель объекта, EventArgs e) {Это теперь будет генерировать ответ на запрос браузера OPTIONS, который удовлетворяет требованию CORS, и последующий GET будет по-прежнемупройти валидацию также, поэтому я не верю, что есть какое-либо негативное влияние на безопасность.

Последний элемент состоит в том, чтобы создать ваши GET и PUT, чтобы они включали информацию о полномочиях.Примеры запросов приведены ниже: let url = environment.base_url + 'api / SubDoc / GetDocument? DocID =' + docID;

this.httpClient.get(url,
  {
    withCredentials: true
  })
  .subscribe(
    response => {
      console.log(response);
      newData = response;
      this.currentDocument = newData;
    }
  );

и

body = { "GroupID": groupID, "ContactID": contact.ContactID, "IsActive": isActive }

let url = this.base_url + 'api/ContactGroup/Put';
this.httpClient.put(url, body,
  {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
    withCredentials: true
  })
  .subscribe(
    response=>{
      let temp:number;
      temp = parseInt(response.toString(),10);
      if(!isNaN(temp)){
        this.groupID = temp;          
      }
    }
  );

Надеемся, это сохраниткто-то еще пережил дни боли и разочарования, пытаясь заставить CORS работать с Angular 6, ASP.NET и интегрированной безопасностью

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...