使用OAuth、Identity建立WebApi認證介面供用戶端調用

來源:互聯網
上載者:User

標籤:

前言

      現在的web app基本上都是前後端分離,之前接觸的大部分應用情境最終產品都是部署在同一個網站下,那麼隨著WebApi(Restful api)的發展前後端實現的完全分離,前端不在後端架構的頁面基礎上開發,也就告別傳統上的Session判斷用戶端登陸使用者的情況。OAuth發行很久,Asp.Net  Identity也發布很久。看了幾篇朋友寫的部落格才把這幾個sample寫完,也解決了之前我對前後端完全分離產生的一些疑惑。

 

OAuth2.0的4種角色
  • resource owner資源所有者:比如twitter使用者,他在twitter的資料就是資源,他自己就是這些資源的所有者
  • resource server資原始伺服器:儲存資源的伺服器,別人要訪問受限制的資源就要出示 Access Token(訪問另牌)
  • client用戶端:一個經過授權後,可以代表資源所有者訪問資原始伺服器上受限制資源的一方。比如 開發人員開發的應用
  • authorization server授權伺服器:對 資源所有者進行認證,認證通過後,向 用戶端發放 Access Token(訪問另牌
OAuth2.0取得Access Token的4種方式
  • 授權碼模式(authorization code)
  • 簡化模式(implicit)
  • Cipher 模式(resource owner password credentials)
  • 用戶端模式(client credentials)
使用Owin實現Cipher 模式(OAuth2.0Cipher 模式)

1、使用VS2015建立一個Empty WebApi項目。

2、使用Nuget匯入核心命名空間。

    Install-Package Microsoft.AspNet.WebApi.Owin

    Install-Package Microsoft.Owin.Host.SystemWeb
3、添加Owin入口類     Startup類上增加了OwinStartup屬性,代表這個類作為Owin的入口。
[assembly:OwinStartup(typeof(AspNet_Identity_Demo.Startup))]namespace AspNet_Identity_Demo{    public class Startup    {        public void Configuration(IAppBuilder app)        {            HttpConfiguration config = new HttpConfiguration();              WebApiConfig.Register(config);            app.UseWebApi(config);        }    }}

  

4、修改WebApiConfig。修改最後兩句代碼,主要以CamelCase命名法序列化webApi的返回結果。

namespace AspNet_Identity_Demo{    public static class WebApiConfig    {        public static void Register(HttpConfiguration config)        {            // Web API 配置和服務            // Web API 路由            config.MapHttpAttributeRoutes();            config.Routes.MapHttpRoute(                name: "DefaultApi",                routeTemplate: "api/{controller}/{id}",                defaults: new { id = RouteParameter.Optional }            );            //用json的方式返回webapi介面傳回值            var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();            jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();        }    }}

  

5、刪除Global.asax。添加Startup類後暫時用不到這個類。

6、添加Asp.Net Identity。先添加Identity類庫。

    Install-Package Microsoft.AspNet.Identity.Owin

    Install-Package Microsoft.AspNet.Identity.EntityFramework
    第一個包提供Asp.Net Identity Owin支援,第二個則提供了基於EF SQL Server的Owin實現。這裡要提下Microsoft.AspNet.Identity.Core包,這裡主要是Asp.net Identity實現的相關介面,比如IUser、IRole、IPasswordHasher、IUserStore<TUser>、IUseRoleStore<TUser>、IRoleStore<TUser>、IClamisIdentityFactory<TUser>、UserManager<TUser>、IdentiyResult。    在第二個包中我們會首先看到IdentityDbContext<TUser>、IdentityUser、IdentityRole、UserStore。你想基於自己的使用者系統擴充建立使用者類繼承IUser或者IdentityUser. 如果想換其他資料庫則自訂DbContext。  7、建立AuthContext。 
namespace AspNet_Identity_Demo.Models{    public class AuthContext:IdentityDbContext<IdentityUser>    {        public AuthContext() : base("AuthContext")        { }    }}

 Web.config中增加connectionString

<add name="AuthContext" connectionString="Data Source=.;User Id=sa;password=111111;Initial Catalog=AspNet_Identity;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />

  

8、在Models檔案夾中建立UserModel.cs

public class UserModel    {        [Required]        [Display(Name ="User Name")]        public string UserName { get; set; }        [Required]        [DataType(DataType.Password)]        [StringLength(100,ErrorMessage ="The {0} must be at least {2} characters long",MinimumLength =6)]        public string Password { get; set; }        [Required]        [DataType(DataType.Password)]        [Compare("Password",ErrorMessage ="The password and confirmpassword are not matched...")]        public string ConfirmPassword { get; set; }    }

 

9、添加Asp.Net Identity 倉儲支援類。

    這裡用到了策略模式,把你實現的UserStore.cs作為參數傳進UserManager建構函式中。

namespace AspNet_Identity_Demo.Models{    public class AuthRepository : IDisposable    {        private AuthContext _ctx;        private UserManager<IdentityUser> _userManager;        public AuthRepository()        {            _ctx = new AuthContext();            _userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(_ctx));        }        public async Task<IdentityResult> Register(UserModel model)        {            IdentityUser user = new IdentityUser()            {                UserName = model.UserName            };            IdentityResult result = await _userManager.CreateAsync(user,model.Password);            return result;        }        public async Task<IdentityUser> FindUser(UserModel model)        {            IdentityUser user = await _userManager.FindAsync(model.UserName, model.Password);            return user;        }        public async Task<IdentityUser> FindUserByName(string username)        {            IdentityUser user = await _userManager.FindByNameAsync(username);            return user;        }        public void Dispose()        {            _ctx.Dispose();            _userManager.Dispose();        }    }}

  

10、添加AccountController.cs

       給Controller添加webapi訪問首碼,我的是apix,訪問時也就是http://localhost:8083/apix/account/register。

namespace AspNet_Identity_Demo.Controllers{    [RoutePrefix("apix/Account")]    public class AccountController : ApiController    {        private AuthRepository _authRepo;        public AccountController()        {            _authRepo = new AuthRepository();        }        [AllowAnonymous]        [Route("Register")]        public async Task<IHttpActionResult> Register(UserModel model)        {            if (!ModelState.IsValid)            {                return BadRequest(ModelState);            }            IdentityResult result = await _authRepo.Register(model);            IHttpActionResult errorResult = GetError(result);            if (errorResult != null)            {                return errorResult;            }            return Ok();        }        private IHttpActionResult GetError(IdentityResult result)        {            if (result == null)            {                return InternalServerError();            }            if (!result.Succeeded)            {                foreach (string err in result.Errors)                {                    ModelState.AddModelError("", err);                }                if (ModelState.IsValid)                {                    return BadRequest();                }                return BadRequest(ModelState);            }            return null;        }    }}

   OK,到了這一步就可以在你的視線之上註冊使用者了,使用Postman調用介面並調用介面http://localhost:8080/apix/account/register。post方式調用。參數傳UserName、Password。 調用成功返回介面返回200.開啟你的SQL Server。調用成功的話資料庫用到的幾張表都會產生。使用者表是dbo.AspNetUsers.

 

11、添加一個資料訪問controller,OrdersController。

namespace AspNet_Identity_Demo.Controllers{    [Authorize]    [RoutePrefix("apix/orders")]    public class OrdersController : ApiController    {        [Route]        public IHttpActionResult Get()        {            return Ok(Order.CreateOrders());        }    }    public class Order    {        public int OrderID { get; set; }        public string CustomerName { get; set; }        public string ShipperCity { get; set; }        public Boolean IsShipped { get; set; }        public static List<Order> CreateOrders()        {            List<Order> OrderList = new List<Order>            {                new Order {OrderID = 10248, CustomerName = "Taiseer Joudeh", ShipperCity = "Amman", IsShipped = true },                new Order {OrderID = 10249, CustomerName = "Ahmad Hasan", ShipperCity = "Dubai", IsShipped = false},                new Order {OrderID = 10250,CustomerName = "Tamer Yaser", ShipperCity = "Jeddah", IsShipped = false },                new Order {OrderID = 10251,CustomerName = "Lina Majed", ShipperCity = "Abu Dhabi", IsShipped = false},                new Order {OrderID = 10252,CustomerName = "Yasmeen Rami", ShipperCity = "Kuwait", IsShipped = true}            };            return OrderList;        }    }}

 

12、添加OAuth Bearer Token支援類庫 Install-Package Microsoft.Owin.Security.OAuth

13、回到Startup。添加建立token方法,主要涉及到了兩個類SimpleAuthorizationServerProvider、OAuthAuthorizationServerOptions。

[assembly:OwinStartup(typeof(AspNet_Identity_Demo.Startup))]namespace AspNet_Identity_Demo{    public class Startup    {        public void Configuration(IAppBuilder app)        {            HttpConfiguration config = new HttpConfiguration();            ConfigAuth(app);            WebApiConfig.Register(config);            app.UseCors(CorsOptions.AllowAll);            app.UseWebApi(config);        }        public void ConfigAuth(IAppBuilder app)        {            OAuthAuthorizationServerOptions option = new OAuthAuthorizationServerOptions()            {                AllowInsecureHttp=true,                TokenEndpointPath=new PathString("/token"),                AccessTokenExpireTimeSpan=TimeSpan.FromDays(1),                Provider=new SimpleAuthorizationServerProvider()            };            app.UseOAuthAuthorizationServer(option);            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());        }    }    public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider    {        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)        {            context.Validated();        }        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)        {            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });            using (AuthRepository _repo = new AuthRepository())            {                IdentityUser user =await _repo.FindUser(                    new UserModel() { UserName=context.UserName,Password=context.Password});                if (user == null)                {                    context.SetError("invalid_grant", "The username or password is incorrect");                    return;                }            }            var identity = new ClaimsIdentity(context.Options.AuthenticationType);            identity.AddClaim(new Claim("sub", context.UserName));            identity.AddClaim(new Claim("role", "user"));            context.Validated(identity);        }    }}

  訪問http://localhost:8083/token  http介面產生token。到期時間24小時。SimpleAuthorizationServerProvider 在該類中實現使用者驗證和口令產生。 注意這裡的ClamisIdentity。該類在命名空間:System.Security.Claims。 產生token主要是context.Validated(identity);這句代碼。

      OK,現在可以註冊使用者,也可以產生token了。那麼現在有個問題來了,前後端完全分離後,那麼肯定要實現跨域訪問(CORS)。所以你看到重寫GrantResourceOwnerCredentials第一句就是添加Access-Control-Allow-Origin支援。

13、添加Asp.Net WebApi Install-Package Microsoft.Owin.Cors。在Startup.cs Configuration方法中添加app.UseCors(CorsOptions.AllowAll);

14、產生用戶端token。

      

  

15、拿到token後,訪問資料介面。注意參數Authorization值有首碼Bearer。

    

總結

      總的來說Owin和Identity的設計還是有點複雜的,約定的東西多一些。相比微軟早起的Membership則要優雅很多,原理和實現背後的細節還要多多挖掘,才能體會到其中個的魅力。比如ClamisIdentity、 UserManager、UserStore。

 

參考資料

http://www.cnblogs.com/richieyang/p/4918819.html

http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

http://www.haomou.net/2014/08/13/2014_bare_token/

http://www.cnblogs.com/pengyingh/articles/2377968.html

http://www.cnblogs.com/keepfool/p/5665953.html

使用OAuth、Identity建立WebApi認證介面供用戶端調用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.