2012-08-16 76 views
9

我想製作一個安全的asp.net web api。對於我跟隨下面的鏈接如何爲asp.net mvc 4 web api執行基於角色的授權

MessageHandler for token

所以現在每個API請求需要我在例如下面

public class TestController : Controller 
{ 

    public string GetProducts() 
    { 
     Uri myUri = new Uri("http://localhost:420420/api/products"); 
     WebRequest myWebRequest = WebRequest.Create(myUri); 

     myWebRequest.Method = "GET"; 
     myWebRequest.ContentType = "application/json"; 
     myWebRequest.Headers.Add("Authorization-Token", RSAClass.accessToken); 

     using (WebResponse response = myWebRequest.GetResponse()) 
     { 
      using (var responseStream = response.GetResponseStream()) 
      { 
       var reader = new StreamReader(responseStream); 
       return reader.ReadToEnd(); 
      } 
     } 
    }  
    } 

所以我現在的請求頭我提供一個令牌能夠做出每一個API請求,檢查標題中的令牌。但我該如何完成授權,我的意思是我怎麼能不讓這個令牌不能訪問同一個控制器中的某些動作。我只需要一個想法。希望我解釋得很好。

編輯:

public class TestController : Controller 
{ 
    public string GetProducts() 
    { 
     Uri myUri = new Uri("http://localhost:420420/api/products");   

     WebRequest myWebRequest = WebRequest.Create(myUri); 

     myWebRequest.Method = "GET"; 
     myWebRequest.ContentType = "application/json"; 
     myWebRequest.Headers.Add("Authorization-Token", RSAClass.accessToken); 

     **using (WebResponse response = myWebRequest.GetResponse()) 
     { 
      using (var responseStream = response.GetResponseStream()) 
      { 
       var reader = new StreamReader(responseStream); 
       return reader.ReadToEnd(); 
      } 
     }** 
} 

我正在向 「API」 控制器的請求,上述內部控制器使用的WebRequest(I稍後將改變它的HttpClient)。在這兩者之間**上面我收到404頁未找到myWebRequest.GetResponse()

下面是我的API控制器

public class ProductsController : ApiController 
{ 

    TestModelContainer testModel = new TestModelContainer(); 

    [Authorize(Roles="Users")] 
    public IEnumerable<Products> GetProducts() 
    { 
     IEnumerable<Products> products = (from prods in testModel.Products 
         select prods); 
     return products;   
    }   
} 
} 

現在在委託處理我有代碼以下代碼

public class TokenValidationHandler : DelegatingHandler 
{ 
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 
    CancellationToken cancellationToken) 
    { 
     TestModelContainer testModel = new TestModelContainer(); 

     var token = ""; 
     try 
     { 

      if (request.Headers.Contains("Authorization-Token")) 
      { 

       token = request.Headers.GetValues("Authorization-Token").FirstOrDefault(); 

       if (String.IsNullOrEmpty(token)) 
       { 
        return Task<HttpResponseMessage>.Factory.StartNew(() => 
        { 
         return new HttpResponseMessage(HttpStatusCode.BadRequest) 
         { 
          Content = new StringContent("Missing Authorization-Token") 
         }; 
        }); 
       } 
      } 
      else 
      { 
       return Task<HttpResponseMessage>.Factory.StartNew(() => 
       { 
        return new HttpResponseMessage(HttpStatusCode.BadRequest) 
        { 
         Content = new StringContent("You need to include Authorization-Token " + 
         "header in your request") 
        }; 
       }); 
      } 


      var decryptedToken = RSAClass.Decrypt(token); 
      var foundUser = (from user in testModel.Users 
           where user.Name == decryptedToken 
           select user).Any();    

      if (!foundUser) 
       return Task<HttpResponseMessage>.Factory.StartNew(() => 
       { 
        return new HttpResponseMessage(HttpStatusCode.Forbidden) 
        { 
         Content = new StringContent("Unauthorized User") 
        }; 
       }); 

     var identity = new GenericIdentity(decryptedToken); 
       string[] roles = new string[] { "Users", "Testers" }; 

       var principal = new GenericPrincipal(identity, roles); 
       Thread.CurrentPrincipal = principal; 
     } 
     catch (Exception ex) 
     { 
      return Task<HttpResponseMessage>.Factory.StartNew(() => 
      { 
       return new HttpResponseMessage(HttpStatusCode.InternalServerError) 
       { 
        Content = new StringContent("Error encountered while attempting to process authorization token") 
       }; 
      }); 
     } 
     return base.SendAsync(request, cancellationToken); 
    } 

如果我從api控制器中刪除授​​權屬性,然後我能夠訪問它,404錯誤不會增加。

更新(我相信解決方案太):

這是問題是怎麼通過達林季米特洛夫

public class TestsController : Controller 
{ 
    public ActionResult GetProducts() 
    { 
     var productsUrl = Url.RouteUrl("DefaultApi", new { httproute = "", controller = "products" }, "http"); 
     using (var client = new HttpClient()) 
     { 
      client.DefaultRequestHeaders.Add("Authorization-Token", RSAClass.accessToken); 

     var products = client 
       .GetAsync(productsUrl) 
       .Result; 

      if (products.StatusCode == HttpStatusCode.Unauthorized) 
      { 
       return Content("Sorry you are not authorized to perform this operation"); 
      } 

      var prods = products.Content 
       .ReadAsAsync<IEnumerable<Products>>() 
       .Result; 

      return Json(prods, JsonRequestBehavior.AllowGet); 
     } 
    } 

解決

我已經改變了的TestController方法如下建議問題是我不知道如何打電話給api,感謝達林他的大力支持(他也很快)。

感謝

回答

26

您在Global.asax註冊的處理程序:

GlobalConfiguration 
    .Configuration 
    .MessageHandlers 
    .Add(new TokenValidationHandler()); 

,然後裝飾控制器/需要與[Authorize]屬性的授權行動:

public class MyController : ApiController 
{ 
    [Authorize] 
    public string Get(string id) 
    { 
     ...   
    } 
} 

對於基於角色的授權你可以看看以下示例:https://stackoverflow.com/a/11536349/29407

它使用基於SSL的基本身份驗證,並依賴於內置的成員資格和角色提供程序。


UPDATE:

根據衆多的評論離開我得到的印象是我的回答不夠清晰。讓我詳細說明一下。

  1. 創建一個使用空模板創建新的ASP.NET MVC 4項目
  2. 定義模型:

    public class Product 
    { 
        public int Id { get; set; } 
        public string Name { get; set; } 
    } 
    
  3. 定義一個ApiController:

    public class ProductsController : ApiController 
    { 
        // GET /api/products => only users having the Users role can call this 
        [Authorize(Roles = "Users")] 
        public HttpResponseMessage Get() 
        { 
         var products = Enumerable.Range(1, 5).Select(x => new Product 
         { 
          Id = x, 
          Name = "product " + x 
         }); 
         return Request.CreateResponse(HttpStatusCode.OK, products); 
        } 
    
        // GET /api/products => only users having the Admin role can call this 
        [Authorize(Roles = "Admin")] 
        public void Post(Product product) 
        { 
        } 
    } 
    
  4. 定義RSAHelper

    public class RSAClass 
    { 
        private static string _privateKey = "<RSAKeyValue><Modulus>poQS/c9tLkgg84xYZpnUBHP6fy24D6XmzhQ8yCOG317hfUNhRt6Z9N4oTn+QcOTh/DAnul4Q901GrHbPrMB8tl1LtbpKbvGftPhyR7OLQVnWC1Oz10t2tHEo7mqyPyAVuYsq8Q1E3YNTh2V6+PRvMiAWGUHGyyG7fKjt/R9W+RE=</Modulus><Exponent>AQAB</Exponent><P>4G09wYejA4iLakpAcjXbE/zV9tXTNsYqVIWeXF4hzwMmwmin7ru/WQzXu2DdapXXOJIKqrkfzXlcPwCsW5b9rQ==</P><Q>vfEq13Et+cP4eGgsR+crDQH0Mi+G6UW5ACfuDs/zam1o+CE70pLgeWawfqW4jRN30/VHDnTF9DZuotH6zihNdQ==</Q><DP>JoZaHYidERQ1am+IlJJuIwY57H9UHIjz50JwpsZ540FVO/YfLboI5M5xkfbUy2EhatKXBit1LB5zGVWSQL6wmQ==</DP><DQ>Gxk7KX2GN6oT2unR13hNlg9/TWGmd8VwvWr09bwJWFe/sBbduA8oY2mZKJhwGgB7CgxmVNOoIk1Zv3UBuUPauQ==</DQ><InverseQ>ZwJpSUZ09lCfiCF3ILB6F1q+6NC5hFH0O4924X9B4LZ8G4PRuudBMu1Yg0WNROUqVi3zfihKvzHnquHshSL56A==</InverseQ><D>pPQNRDVpeQGm8t1C7VDRwR+LNNV7krTMMbXGiJT5FOoPAmHvSZ9WcEZrM2gXFF8IpySlFm/86p84tbx0+jMs1niU52VsTscsamGbTzbsxeoHAt1fQUvzYveOGoRezotXblboVB2971r6avMHNtAk0FAdjvh4TjGZJCGTqNHD0mE=</D></RSAKeyValue>"; 
        private static string _publicKey = "<RSAKeyValue><Modulus>poQS/c9tLkgg84xYZpnUBHP6fy24D6XmzhQ8yCOG317hfUNhRt6Z9N4oTn+QcOTh/DAnul4Q901GrHbPrMB8tl1LtbpKbvGftPhyR7OLQVnWC1Oz10t2tHEo7mqyPyAVuYsq8Q1E3YNTh2V6+PRvMiAWGUHGyyG7fKjt/R9W+RE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>"; 
        private static UnicodeEncoding _encoder = new UnicodeEncoding(); 
    
        public static string Decrypt(string data) 
        { 
         try 
         { 
          var rsa = new RSACryptoServiceProvider(); 
          var dataArray = data.Split(new char[] { ',' }); 
    
          byte[] dataByte = new byte[dataArray.Length]; 
          for (int i = 0; i < dataArray.Length; i++) 
          { 
           dataByte[i] = Convert.ToByte(dataArray[i]); 
          } 
    
          rsa.FromXmlString(_privateKey); 
          var decryptedByte = rsa.Decrypt(dataByte, false); 
          return _encoder.GetString(decryptedByte); 
         } 
         catch (Exception) 
         { 
          throw new RSAException(); 
         } 
        } 
    
        public static string Encrypt(string data) 
        { 
         try 
         { 
          var rsa = new RSACryptoServiceProvider(); 
          rsa.FromXmlString(_publicKey); 
          var dataToEncrypt = _encoder.GetBytes(data); 
          var encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray(); 
          var length = encryptedByteArray.Count(); 
          var item = 0; 
          var sb = new StringBuilder(); 
          foreach (var x in encryptedByteArray) 
          { 
           item++; 
           sb.Append(x); 
    
           if (item < length) 
            sb.Append(","); 
          } 
    
          return sb.ToString(); 
    
         } 
         catch (Exception ex) 
         { 
          throw new RSAException(); 
         } 
        } 
    
        public class RSAException : Exception 
        { 
         public RSAException() : base("RSA Encryption Error") { } 
        } 
    } 
    
  5. 定義TokenValidationHandler

    public class TokenValidationHandler : DelegatingHandler 
    { 
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
        { 
         try 
         { 
          if (!request.Headers.Contains("Authorization-Token")) 
          { 
           return Task<HttpResponseMessage>.Factory.StartNew(() => 
           { 
            return new HttpResponseMessage(HttpStatusCode.BadRequest) 
            { 
             Content = new StringContent("You need to include Authorization-Token header in your request") 
            }; 
           }); 
          } 
    
          var token = request.Headers.GetValues("Authorization-Token").FirstOrDefault(); 
          if (string.IsNullOrEmpty(token)) 
          { 
           return Task<HttpResponseMessage>.Factory.StartNew(() => 
           { 
            return new HttpResponseMessage(HttpStatusCode.BadRequest) 
            { 
             Content = new StringContent("Missing Authorization-Token") 
            }; 
           }); 
          } 
    
          var decryptedToken = RSAClass.Decrypt(token); 
    
          // TODO: do your query to find the user 
          var user = decryptedToken; 
    
          var identity = new GenericIdentity(decryptedToken); 
          string[] roles = new[] { "Users", "Testers" }; 
    
          var principal = new GenericPrincipal(identity, roles); 
          Thread.CurrentPrincipal = principal; 
         } 
         catch 
         { 
          return Task<HttpResponseMessage>.Factory.StartNew(() => 
          { 
           return new HttpResponseMessage(HttpStatusCode.InternalServerError) 
           { 
            Content = new StringContent("Error encountered while attempting to process authorization token") 
           }; 
          }); 
         } 
    
         return base.SendAsync(request, cancellationToken); 
        } 
    } 
    
  6. 定義測試控制器:

    public class TestsController : Controller 
    { 
        public ActionResult GetProducts() 
        { 
         var productsUrl = Url.RouteUrl("DefaultApi", new { httproute = "", controller = "products" }, "http"); 
         using (var client = new HttpClient()) 
         { 
          var token = RSAClass.Encrypt("john"); 
          client.DefaultRequestHeaders.Add("Authorization-Token", token); 
    
          var products = client 
           .GetAsync(productsUrl) 
           .Result 
           .Content 
           .ReadAsAsync<IEnumerable<Product>>() 
           .Result; 
    
          return Json(products, JsonRequestBehavior.AllowGet); 
         } 
        } 
    
        public ActionResult PostProduct() 
        { 
         var productsUrl = Url.RouteUrl("DefaultApi", new { httproute = "", controller = "products" }, "http"); 
         using (var client = new HttpClient()) 
         { 
          var token = RSAClass.Encrypt("john"); 
          client.DefaultRequestHeaders.Add("Authorization-Token", token); 
    
          var product = new Product 
          { 
           Id = 1, 
           Name = "test product" 
          }; 
    
          var result = client 
           .PostAsync<Product>(productsUrl, product, new JsonMediaTypeFormatter()) 
           .Result; 
          if (result.StatusCode == HttpStatusCode.Unauthorized) 
          { 
           return Content("Sorry you are not authorized to perform this operation"); 
          } 
    
          return Json(true, JsonRequestBehavior.AllowGet); 
         } 
        } 
    } 
    
  7. 測試:

    * /tests/getproducts => success 
    * /tests/postproduct => 401 
    
+0

我已經註冊了TokenValidation在global.asax.cs中,但我的問題如何使一個經過身份驗證的令牌不能訪問同一控制器中的其他一些操作...?我的意思是我怎麼做到[授權(角色=「管理員」)與在「API控制器」將產生的令牌 – CrazyNooB 2012-08-16 12:35:58

+0

你必須寫一個不同的'TokenValidationHandler'。本文中介紹的僅在令牌內存儲用戶名,根本沒有角色。您可以在結帳我出一個自定義的委託處理程序使用基本身份驗證方案,並依靠內置的成員資格和角色提供以下答案:http://stackoverflow.com/a/11536349/29407 – 2012-08-16 12:37:45

+0

沒錯,這就是我問。你能給出一小段代碼如何使用令牌進行基於角色的授權..? – CrazyNooB 2012-08-16 12:40:59