2015-06-18 127 views
1

解决!!! - 查看上次编辑。C#Web API方法返回403 Forbidden

在我的MVC应用程序中,我使用HMAC身份验证滤波器向Web API服务发出调用。我的Get(GetMultipleItemsRequest)有效,但我的Post没有。如果我关闭HMAC认证过滤,所有这些过滤器都可以工作。我不确定为什么POSTS不起作用,但GETs可以。

我做的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; 
    } 

    //... 

} 

我的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不为岗位工作?

编辑:
当SecretAuthenticationFilter尝试比较发送的HMAC与它认为HMAC应该是他们不匹配。原因是内容的MD5Hash与接收内容的MD5Hash不匹配。 RestClient使用JavaScriptSerializer.Serialized内容版本对内容进行散列处理,但是PostRequest将对象作为JsonMediaTypeFormatted传递。

这两种类型的格式不一样。例如,JavaScriptSerializer给在美国的历史可以是这样的: \ “EnteredDate \”:\ “\ /日期(1434642998639)\/\”

所传递的内容有日期如下: \ “EnteredDate \”:\ “2015-06-18T11:56:38.6390407-04:00 \”

我想我需要哈希使用传递的相同数据,因此另一端的过滤器可以正确地确认它。思考?

编辑: 找到答案,我需要使用此线改变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格式)。