最近看了一点 web api 2方面的书,对认证都是简单介绍了下,所以我在这里做个简单Demo,本文主要是FORM Authentication,顺带把基本认证也讲了。
一、FORM Authentication
1、新建asp.net 空项目->Web API,如下图所示:
2、先创建一个简单无认证示例:
(1)、Models文件夹下新建Product类,
/// <summary> /// 产品 /// </summary> public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } }
(2)、Controllers文件夹下新建ProductsController类,
public class ProductsController : ApiController { Product[] products = new Product[] { new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 }, new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M }, new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M } }; public IEnumerable<Product> GetAll() { return products; } public IHttpActionResult Get(int id) { var product = products.FirstOrDefault((p) => p.Id == id); if (product == null) { return NotFound(); } return Ok(product); } }
(3)、创建index.html页面,前端脚本如下,
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Web API2 Studt</title> </head> <body> <div> <h2>All Products</h2> <ul id="products"></ul> </div> <div> <h2>Search by ID</h2> <input type="text" id="prodId" size="5" /> <input type="button" value="Search" onclick="find();" /> <p id="product" /> </div> <script src="JS/jquery-2.0.3.min.js"></script> <script> var uri = 'api/products'; $(document).ready(function () { // Send an AJAX request $.getJSON(uri) .done(function (data) { // On success, 'data' contains a list of products. $.each(data, function (key, item) { // Add a list item for the product. $('<li>', { text: formatItem(item) }).appendTo($('#products')); }); }).fail(function (jqXHR, textStatus, err) { if (err == 'Forbidden') {self.location = 'login.html';} }); }); function formatItem(item) { return item.Name + ': $' + item.Price; } function find() { var id = $('#prodId').val(); $.getJSON(uri + '/' + id) .done(function (data) { $('#product').text(formatItem(data)); }) .fail(function (jqXHR, textStatus, err) { $('#product').text('Error: ' + err); }); } </script> </body> </html>
(4) index.html设为起始页,启动后访问成功,
3、添加FORM Authentication认证,
(1)、Controllers文件夹下新建FormAuth类,
using System; using System.Net; using System.Net.Http; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; using System.Web.Security; namespace WebApi2Demo.Controllers { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class FormAuth : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { try { if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0) { base.OnActionExecuting(actionContext); return; } var cookie = actionContext.Request.Headers.GetCookies(); if (cookie == null || cookie.Count < 1) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden); return; } FormsAuthenticationTicket ticket = null; foreach (var perCookie in cookie[0].Cookies) { if (perCookie.Name == FormsAuthentication.FormsCookieName) { ticket = FormsAuthentication.Decrypt(perCookie.Value); break; } } if (ticket == null) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden); return; } // TODO: 添加其它验证方法 base.OnActionExecuting(actionContext); } catch { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden); } } } }
(2)、将FormAuth特性添加到ProductsController类上面,如下图:
(3)、编译网站后,刷新index.html页面,跳转到了login.html(需要新建)页面,
看看前端js,
如果错误提示“Forbidden”,则跳转到login.html页面,这正是FormAuth中响应的HttpStatusCode(Forbidden = 403),
用Fiddler2看看,
(4)、添加登陆认证,
1)、 新建LogOn类,
public class LogOn { public string Username { get; set; } public string Password { get; set; } }
2)、新建登陆认证类,
public class AccountController : ApiController { [HttpPost] public HttpResponseMessage Post(LogOn model) { string password; if (model.Username == "zzhi") { if (model.Password == "12345") { FormsAuthentication.SetAuthCookie(model.Username, false); return Request.CreateResponse(HttpStatusCode.OK, "Success"); } return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid username or password."); } else { return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid username or password."); } } }
3)、login.html 脚本如下,
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>登陆</title> <script src="JS/jquery-2.0.3.min.js"></script> <script> var uri = 'api/Account'; function login() { var username = $("#username").val(); var password = $("#password").val(); $.post(uri, { Username: username, Password: password }) .success(function (result) { alert(result); window.location.href = "index.html"; }) .fail(function (XMLHttpRequest, textStatus, err) { alert(XMLHttpRequest.status); alert(XMLHttpRequest.text); alert(err); this.reload(); }); } </script> </head> <body> <label>用户名</label><input id="username" type="text" /> <br /> <label>密 码</label><input id="password" type="password" /> <br /> <button onclick="login()">登陆</button> </body> </html>
编译网站后刷新login.html,输入用户名:zzhi,密码:12345,提示登陆成功,然后跳转到index.html页面。
TEST:关闭网页,再次启动网站,直接进入index.html页面并请求数据成功,fiddler2看看,
(5)、控制台访问API 2,代码如下:
internal class Program { private static void Main(string[] args) { Process(); Console.Read(); } private static async void Process() { string token = GetSecurityToken("zzhi", "12345", "http://localhost:45690/api/Account", ".ASPXAUTH"); string address = "http://localhost:45690/api/products"; if (!string.IsNullOrEmpty(token)) { HttpClientHandler handler = new HttpClientHandler {CookieContainer = new CookieContainer()}; handler.CookieContainer.Add(new Uri(address), new Cookie(".ASPXAUTH", token)); using (HttpClient httpClient = new HttpClient(handler)) { HttpResponseMessage response = httpClient.GetAsync(address).Result; IEnumerable<Product> Products = await response.Content.ReadAsAsync<IEnumerable<Product>>(); foreach (Product c in Products) { Console.WriteLine(c.Name); } } } } private static string GetSecurityToken(string userName, string password, string url, string cookieName) { using (HttpClient httpClient = new HttpClient()) { Dictionary<string, string> credential = new Dictionary<string, string>(); credential.Add("Username", userName); credential.Add("Password", password); HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(credential)).Result; IEnumerable<string> cookies; if (response.Headers.TryGetValues("Set-Cookie", out cookies)) { string token = cookies.FirstOrDefault(value => value.StartsWith(cookieName)); if (null == token) { return null; } return token.Split(';')[0].Substring(cookieName.Length + 1); } return null; } } }
(6)、跨域访问
1)、在web项目中(我的web项目名字:WebApi2Demo)通过Nuget添加 web api 2 corss-Origin 引用,如下图:
2)、WebApiConfig.cs 配置如下:
var cors = new EnableCorsAttribute("*", "*", "*");//跨域访问 config.EnableCors(cors); config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
3)、ProductsController.cs 去掉[FormAuth]特性。
ASP.net里面的Form认证,是通过在Cookie写入登陆信息,然后浏览器发送请求后服务端再去验证Cookie是否存在,从而达到认证用户的目的。但是我们现在涉及到一个跨域的问题,而Cookie是不能跨站共享的
。即使RESTful那边设置了cookie,也不会到当前请求的域下面。到了第二次请求的时候,还是得不到认证信息。
这里我做了个简单测试,如果你不注释[FormAuth]特性,跨域是请求不到数据的。
那么如何跨域认证呢,一种简单方式是基本认证。
可以通过Demo中的WebCorsTest项目进行测试。
二、基本认证,基本认证放在Demo中了,不再赘述。
相关链接:RESTful api跨域认证
Api 2 资料:
ASP.NET Web API 2 Recipes_ A Problem-Solution Approach.pdf
ASP.NET Web API 2_ Building a REST Service from Start to Finish.pdf
Pro ASP.NET Web API HTTP Web Services in ASP.NET.pdf
好了到此结束。