• Asp.net Web Api 2 FORM Authentication Demo


    最近看了一点 web api 2方面的书,对认证都是简单介绍了下,所以我在这里做个简单Demo,本文主要是FORM Authentication,顺带把基本认证也讲了。

    Demo

    一、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>
    View Code

       (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);
                 }
             }
    
        }
    }
    View Code

        (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.");
                }
            }
    
        }
    View Code

        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>密&nbsp;&nbsp;码</label><input id="password" type="password" />
        <br />
        <button onclick="login()">登陆</button>
    </body>
    </html>
    View Code

      编译网站后刷新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;
                }
            }
        }
    View Code

    (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中了,不再赘述。

    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

    inside-asp-ney-web-api-2.pdf

    好了到此结束。

  • 相关阅读:
    Pycharm 新建工程后修改解析器为python3 的方法
    HttpRunner2.X开源接口测试框架学习(七):跳过用例以及重复执行用例
    python习题(一)
    HttpRunner2.X开源接口测试框架学习(九):用例分层机制实战应用
    C# 中的常用正则表达式总结
    缩小SQL Server数据库日志文件
    DataBinder.Eval用法范例
    关于GridView中自定义分页、单选、多选、排序、自增列的简单应用
    退出一个页面时自动清空session变量
    C#代码与javaScript函数的相互调用
  • 原文地址:https://www.cnblogs.com/zhangzhi19861216/p/4240860.html
Copyright © 2020-2023  润新知