I am trying to make a secure asp.net web api. For that I have followed the below link
So now each and every api request needs a token which I am supplying in the request header as below for example
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();
}
}
}
}
So I am now able to make each and every api request, check for a token in the header. But how do I accomplish authorization, I mean how can I not allow this token not access some actions in the same controller.I just need an idea.Hope I explained well enough.
Edit:
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();
}
}**
}
I am making a request to the "api" controller, inside above controller, using webrequest(I will change it later to HttpClient). In the code between ** ** above I am getting 404 page not found for myWebRequest.GetResponse()
Below is my api controller
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;
}
}
}
Now in the delegating handler I have the following code
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);
}
The 404 error doesnt rise if i remove the Authorize attribute from the api controller, and then I am able to access it.
Update(I believe solution too):
this is how the issue got solved
I have changed the TestController method as below suggested by Darin Dimitrov
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);
}
}
The issue was I didnt know how to make a call to the api, thanks to Darin for his great support(he was very quick too).
Thanks
You register the handler in Global.asax
:
GlobalConfiguration
.Configuration
.MessageHandlers
.Add(new TokenValidationHandler());
and then decorate controllers/actions that require authorization with the [Authorize]
attribute:
public class MyController : ApiController
{
[Authorize]
public string Get(string id)
{
...
}
}
For role based authorization you may could take a look at the following example: https://stackoverflow.com/a/11536349/29407
It uses basic authentication over SSL and relies on the built-in membership and role providers.
UPDATE:
According to the numerous comments left I get the impression that my answer was not clear enough. Let me elaborate.
Define a model:
public class Product { public int Id { get; set; } public string Name { get; set; } }
Define an 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) { } }
Define a 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") { } } }
Define a 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); } }
Define a test controller:
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); } } }
Test:
* /tests/getproducts => success * /tests/postproduct => 401
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.