2015-06-18 6 views
1

Решено !!! - См. Последнее редактирование.C# API API возвращает 403 Запрещено

В моем приложении MVC я звоню в службу веб-API с помощью HFAC Authentication Filterign. Мой Get (GetMultipleItemsRequest) работает, но мой пост не работает. Если я отключу фильтрацию подлинности HMAC, все они будут работать. Я не уверен, почему POSTS не работают, но GET.

Я делаю вызов GET из моего кода, как это (это работает):

var productsClient = new RestClient<Role>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"], 
       "xxxxxxxxxxxxxxx", true); 

var getManyResult = productsClient.GetMultipleItemsRequest("api/Role").Result; 

Я делаю вызов POST из моего кода, как это (это работает только тогда, когда я выключаю HMAC):

private RestClient<Profile> profileClient = new RestClient<Profile>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"], 
     "xxxxxxxxxxxxxxx", true); 

[HttpPost] 
public ActionResult ProfileImport(IEnumerable<HttpPostedFileBase> files) 
{ 
    //... 
    var postResult = profileClient.PostRequest("api/Profile", newProfile).Result; 
} 

Моего RestClient строит так:

public class RestClient<T> where T : class 
{ 
    //... 

    private void SetupClient(HttpClient client, string methodName, string apiUrl, T content = null) 
    { 
     const string secretTokenName = "SecretToken"; 

     client.BaseAddress = new Uri(_baseAddress); 
     client.DefaultRequestHeaders.Accept.Clear(); 
     client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 

     if (_hmacSecret) 
     { 
      client.DefaultRequestHeaders.Date = DateTime.UtcNow; 

      var datePart = client.DefaultRequestHeaders.Date.Value.UtcDateTime.ToString(CultureInfo.InvariantCulture); 
      var fullUri = _baseAddress + apiUrl; 
      var contentMD5 = ""; 

      if (content != null) 
      { 
       var json = new JavaScriptSerializer().Serialize(content); 
       contentMD5 = Hashing.GetHashMD5OfString(json); // <--- Javascript serialized version is hashed 
      } 

      var messageRepresentation = 
       methodName + "\n" + 
       contentMD5 + "\n" + 
       datePart + "\n" + 
       fullUri; 

      var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName]; 
      var hmac = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue); 

      client.DefaultRequestHeaders.Add(secretTokenName, hmac); 
     } 
     else if (!string.IsNullOrWhiteSpace(_sharedSecretName)) 
     { 
      var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName]; 
      client.DefaultRequestHeaders.Add(secretTokenName, sharedSecretValue); 
     } 
    } 

    public async Task<T[]> GetMultipleItemsRequest(string apiUrl) 
    { 
     T[] result = null; 

     try 
     {    
      using (var client = new HttpClient()) 
      { 
       SetupClient(client, "GET", apiUrl); 

       var response = await client.GetAsync(apiUrl).ConfigureAwait(false); 

       response.EnsureSuccessStatusCode(); 

       await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) => 
       { 
        if (x.IsFaulted) 
         throw x.Exception; 

        result = JsonConvert.DeserializeObject<T[]>(x.Result); 
       }); 
      } 
     } 
     catch (HttpRequestException exception) 
     { 
      if (exception.Message.Contains("401 (Unauthorized)")) 
      { 

      } 
      else if (exception.Message.Contains("403 (Forbidden)")) 
      { 

      } 
     } 
     catch (Exception) 
     { 
     } 

     return result; 
    } 

    public async Task<T> PostRequest(string apiUrl, T postObject) 
    { 
     T result = null; 
     try 
     {    
      using (var client = new HttpClient()) 
      { 
       SetupClient(client, "POST", apiUrl, postObject); 

       var response = await client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter()).ConfigureAwait(false); //<--- not javascript formatted 

       response.EnsureSuccessStatusCode(); 

       await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) => 
       { 
        if (x.IsFaulted) 
         throw x.Exception; 

        result = JsonConvert.DeserializeObject<T>(x.Result); 

       }); 
      } 
     } 
     catch (HttpRequestException exception) 
     { 
      if (exception.Message.Contains("401 (Unauthorized)")) 
      { 

      } 
      else if (exception.Message.Contains("403 (Forbidden)")) 
      { 

      } 
     } 
     catch (Exception) 
     { 
     } 

     return result; 
    } 

    //... 

} 

My Web API контроллер определяются следующим образом:

[SecretAuthenticationFilter(SharedSecretName = "xxxxxxxxxxxxxxx", HmacSecret = true)]  
public class ProfileController : ApiController 
{ 

    [HttpPost] 
    [ResponseType(typeof(Profile))] 
    public IHttpActionResult PostProfile(Profile Profile) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 
     GuidValue = Guid.NewGuid(); 

     Resource res = new Resource(); 
     res.ResourceId = GuidValue; 
     var data23 = Resourceservices.Insert(res); 

     Profile.ProfileId = data23.ResourceId; 
     _profileservices.Insert(Profile); 

     return CreatedAtRoute("DefaultApi", new { id = Profile.ProfileId }, Profile); 
    } 

} 

Вот некоторые из того, что делает SecretAuthenticationFilter:

//now try to read the content as string 
string content = actionContext.Request.Content.ReadAsStringAsync().Result; 
var contentMD5 = content == "" ? "" : Hashing.GetHashMD5OfString(content); //<-- Hashing the non-JavaScriptSerialized 
var datePart = ""; 
var requestDate = DateTime.Now.AddDays(-2); 
if (actionContext.Request.Headers.Date != null) 
{ 
    requestDate = actionContext.Request.Headers.Date.Value.UtcDateTime; 
    datePart = requestDate.ToString(CultureInfo.InvariantCulture); 
} 
var methodName = actionContext.Request.Method.Method; 
var fullUri = actionContext.Request.RequestUri.ToString(); 

var messageRepresentation = 
    methodName + "\n" + 
    contentMD5 + "\n" + 
    datePart + "\n" + 
    fullUri; 

var expectedValue = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue); 

// Are the hmacs the same, and have we received it within +/- 5 mins (sending and 
// receiving servers may not have exactly the same time) 
if (messageSecretValue == expectedValue 
    && requestDate > DateTime.UtcNow.AddMinutes(-5) 
    && requestDate < DateTime.UtcNow.AddMinutes(5)) 
    goodRequest = true; 

Любая идея, почему HMAC не работает для POST?

EDIT:
Когда SecretAuthenticationFilter пытается сравнить отправленное HMAC, с тем, что, по его мнению, HMAC должно быть, они не совпадают. Причина в том, что MD5Hash содержимого не соответствует MD5Hash полученного контента. RestClient хэширует содержимое с помощью JavaScriptSerializer.Serialized версии содержимого, но затем PostRequest передает объект как JsonMediaTypeFormatted.

Эти два типа не отформатированы одинаково. Например, JavaScriptSerializer дают нам мы датирует так: \ "EnteredDate \": \ "\/Date (1434642998639) \/\"

Передаваемый контент имеет даты, как это: \ "EnteredDate \": \ "2015-06-18T11: 56: 38.6390407-04: 00 \"

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

EDIT: Найден ответ, мне нужно, чтобы изменить код SetupClient с помощью этой строки:

var json = new JavaScriptSerializer().Serialize(content); 
contentMD5 = Hashing.GetHashMD5OfString(json); 

Для использования этого:

var json = JsonConvert.SerializeObject(content); 
contentMD5 = Hashing.GetHashMD5OfString(json); 

Теперь отправленное содержание (форматированный с помощью JSON) будет соответствовать хешированному контенту.

Я не был тем человеком, который написал этот код изначально.:)

+0

Проверьте, установлен ли модуль WebDav. Если это так, удалите его и повторите попытку. – Alioza

ответ

0

Найден ответ, мне нужно, чтобы изменить код SetupClient с помощью этой строки:

var json = new JavaScriptSerializer().Serialize(content); 
contentMD5 = Hashing.GetHashMD5OfString(json); 

Для использования этого:

var json = JsonConvert.SerializeObject(content); 
contentMD5 = Hashing.GetHashMD5OfString(json); 

Теперь содержимое, используемое для хэш будет отформатирован как JSON и будет соответствовать отправленному контенту (который также форматируется через JSON).

Смежные вопросы