基於OWIN WebAPI 使用OAuth授權服務【客戶端驗證授權(Resource Owner Password Credentials Grant)】


適用范圍

前面介紹了Client Credentials Grant ,只適合客戶端的模式來使用,不涉及用戶相關。而Resource Owner Password Credentials Grant模式中,用戶向客戶端提供自己的用戶名和密碼。客戶端使用這些信息,向"服務商提供商"索要授權。

在這種模式中,用戶必須把自己的密碼給客戶端,但是客戶端不得儲存密碼。這通常用在用戶對客戶端高度信任的情況下,比如客戶端是操作系統的一部分,或者由一個著名公司出品。而認證服務器只有在其他授權模式無法執行的情況下,才能考慮使用這種模式。這種模式會直接將用戶密碼暴露給應用程序,因此應謹慎使用。一般說來,只有信任度極高的客戶才應授權使用該模式,如官方移動應用、操作系統或高權限程序。

Resource Owner Password Credentials Grant:http://tools.ietf.org/html/rfc6749#section-4.3

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          v
          |    Resource Owner
         (A) Password Credentials
          |
          v
     +---------+                                  +---------------+
     |         |>--(B)---- Resource Owner ------->|               |
     |         |         Password Credentials     | Authorization |
     | Client  |                                  |     Server    |
     |         |<--(C)---- Access Token ---------<|               |
     |         |    (w/ Optional Refresh Token)   |               |
     +---------+                                  +---------------+

            Figure 5: Resource Owner Password Credentials Flow

   The flow illustrated in Figure 5 includes the following steps:

   (A)  The resource owner provides the client with its username and
        password.

   (B)  The client requests an access token from the authorization
        server's token endpoint by including the credentials received
        from the resource owner.  When making the request, the client
        authenticates with the authorization server.

   (C)  The authorization server authenticates the client and validates
        the resource owner credentials, and if valid, issues an access
        token.

基本流程

Resource Owner Password

A. 向用戶索要認證信息

首先,我們必須得讓用戶將認證信息提供給應用程序。對於應用方來說,如果用戶處於不可信的網絡中時,除了需要輸入用戶名和密碼外,還需要用戶提供一個安全令牌作為用戶的第三個輸入。

B. 交換訪問令牌

這里的訪問令牌交換過程與授權碼類型的驗證授權(authorization code)很相似。我們要做的就是向認證服務器提交一個POST請求並在其中提供相應的認證和客戶信息。

所需的POST參數:
grant_type 該模式下為"password"
scope 業務訪問控制范圍,是一個可選參數
client_id 應用注冊時獲得的客戶id
client_secret 應用注冊時獲得的客戶密鑰
username 用戶的用戶名
password 用戶的密碼

POST https://xxx.com/token HTTP/1.1
Content-type:application/x-www-form-urlencoded
Authorization Basic Base64(clientId:clientSecret)

username=irving&password=123456&grant_type=password

C. 刷新Token

1).accesstoken 是有過期時間的,到了過期時間這個 access token 就失效,需要刷新。
2).如果accesstoken會關聯一定的用戶權限,如果用戶授權更改了,這個accesstoken需要被刷新以關聯新的權限。
3).為什么要專門用一個 token 去更新 accesstoken 呢?如果沒有 refreshtoken,也可以刷新 accesstoken,但每次刷新都要用戶輸入登錄用戶名與密碼,客戶端直接用 refreshtoken 去更新 accesstoken,無需用戶進行額外的操作。

POST http://localhost:19923/token
Content-Type: Application/x-www-form-urlencoded Authorization Basic Base64(clientId:clientSecret) username=irving&password=123456&grant_type=refresh_token

備注:

有了前面相關token,服務調用也很簡單

GET https://xxx.com/api/v1/account/profile HTTP/1.1
Content-type:application/x-www-form-urlencoded
Authorization Authorization: Bearer {THE TOKEN}

服務實現

基於ASP.NET WEBAPI Microsoft.Owin.Security.OAuth實現

表設計

CREATE TABLE [dbo].[clients] (
    [Id]          INT       IDENTITY (1, 1) NOT NULL,
    [client_name] VARCHAR(200) NOT NULL, 
    [client_id]     VARCHAR (200) NOT NULL,
    [client_secret] VARCHAR (500) NOT NULL,
    [client_description] NCHAR(10) NULL, 
    PRIMARY KEY CLUSTERED ([Id] ASC)
);

CREATE TABLE [dbo].[users] (
    [Id]       INT           IDENTITY (1, 1) NOT NULL,
    [username] VARCHAR (100) NOT NULL,
    [pwd]      VARCHAR (500) NULL,
    [mobile]   VARCHAR (15)  NULL,
    [birthday] DATETIME      NULL,
    [sex]      INT           NULL,
    [age]      INT           NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);

CREATE TABLE [dbo].[tokens] (
    [Id]           INT           IDENTITY (1, 1) NOT NULL,
    [clientId]     VARCHAR (200) NULL,
    [userName]     VARCHAR (100) NULL,
    [accessToken]  VARCHAR (300) NULL,
    [refreshToken] VARCHAR (300) NULL,
    [issuedUtc]    DATETIME      NULL,
    [expiresUtc]   DATETIME      NULL,
    [ipAddress]    VARCHAR (50)  NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);

倉庫設計 
public class AccountRepository : IAccountRepository
    {
        /// <summary>
        /// 驗證用戶名密碼
        /// </summary>
        /// <param name="userName">用戶名</param>
        /// <param name="pwd">密碼</param>
        /// <returns></returns>
        public async Task<bool> ValidateUserNameAuthorizationPwdAsync(string userName, string pwd)
        {
            const string cmdText = @"SELECT COUNT(*) FROM [dbo].[users] WHERE username=@username AND pwd=@pwd";
            try
            {
                return await new SqlConnection(DbSetting.App).ExecuteScalarAsync<int>(cmdText, new { userName = userName, pwd = pwd }) != 0;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
    }
public class ClientAuthorizationRepository : IClientAuthorizationRepository
    {
        /// <summary>
        /// 生成OAuth2 clientSecret
        /// </summary>
        /// <returns></returns>
        public async Task<string> GenerateOAuthClientSecretAsync(string client_id = "")
        {
            return await Task.Run(() =>
            {
                var cryptoRandomDataGenerator = new RNGCryptoServiceProvider();
                byte[] buffer = Guid.NewGuid().ToByteArray();
                if (client_id.IsNotNullOrEmpty())
                {
                    buffer = client_id.ToByteArray();
                }
                cryptoRandomDataGenerator.GetBytes(buffer);
                return Convert.ToBase64String(buffer).TrimEnd('=').Replace('+', '-').Replace('/', '_');
            });
        }

        /// <summary>
        /// 驗證客戶端[Authorization Basic Base64(clientId:clientSecret)]
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="clientSecret"></param>
        /// <returns></returns>
        public async Task<bool> ValidateClientAuthorizationSecretAsync(string client_id, string client_secret)
        {
            //!!! http://stackoverflow.com/questions/23652166/how-to-generate-oauth-2-client-id-and-secret
            const string cmdText = @"SELECT COUNT(*) FROM [dbo].[clients] WHERE client_id=@clientId AND client_secret=@clientSecret";
            try
            {
                return await new SqlConnection(DbSetting.App).ExecuteScalarAsync<int>(cmdText, new { clientId = client_id, clientSecret = client_secret }) != 0;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        /// <summary>
        /// 保持票據
        /// </summary>
        /// <param name="token">票據</param>
        /// <returns></returns>
        public async Task<bool> SaveTokenAsync(Token token)
        {
            const string cmdText = @"INSERT INTO Tokens(access_token,token_type, expires_in ,refresh_token ,userName, clientId ,issuedUtc ,expiresUtc) VALUES(@access_token,@token_type, @expires_in ,@refresh_token ,@userName, @clientId ,@issuedUtc ,@expiresUtc)";
            try
            {
                // var data = await new SqlConnection(DbSetting.App).InsertAsync(token);
                return await new SqlConnection(DbSetting.App).ExecuteAsync(cmdText, new
                {
                    access_token = token.Access_token,
                    token_type = token.Token_type,
                    expires_in = token.Expires_in,
                    refresh_token = token.Refresh_token,
                    userName = token.UserName,
                    clientId = token.ClientId,
                    issuedUtc = token.IssuedUtc,
                    expiresUtc = token.ExpiresUtc
                }) != 0;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
    }

OWIN WEBAPI

        /// <summary>
        /// IOS App OAuth2 Credential Grant Password Service
        /// </summary>
        /// <param name="app"></param>
        public void ConfigureAuth(IAppBuilder app)
        {
            app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
            {
                // AccessTokenProvider=
                // /token  api/v1/account/signin
                TokenEndpointPath = new PathString("/token"),
                //Provider = new ClientApplicationOAuthProvider(),
                //Provider = new PasswordAuthorizationServerProvider(),
                //Provider = DependencyInjectionConfig.container.Resolve<PasswordAuthorizationServerProvider>(),
                //Provider = DependencyResolver.Current.GetService<PasswordAuthorizationServerProvider>(),
                Provider = GlobalConfiguration.Configuration.DependencyResolver.GetRootLifetimeScope().Resolve<PasswordAuthorizationServerProvider>(),
                RefreshTokenProvider = GlobalConfiguration.Configuration.DependencyResolver.GetRootLifetimeScope().Resolve<RefreshAuthenticationTokenProvider>(),
                AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
                AuthenticationMode = AuthenticationMode.Active,
                //HTTPS is allowed only AllowInsecureHttp = false
#if DEBUG
                AllowInsecureHttp = true,
#endif
            });

PasswordAuthorizationServerProvider[Password Grant 授權服務]

    /// <summary>
    /// Resource Owner Password Credentials Grant 授權
    /// </summary>
    public class PasswordAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        /// <summary>
        /// Password Grant 授權服務
        /// </summary>
        private readonly IClientAuthorizationService _clientAuthorizationService;

        /// <summary>
        ///賬戶服務
        /// </summary>
        private readonly IAccountService _accountService;

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="clientAuthorizationService">Password Grant 授權服務</param>
        /// <param name="accountService">用戶服務</param>
        public PasswordAuthorizationServerProvider(IClientAuthorizationService clientAuthorizationService, IAccountService accountService)
        {
            _clientAuthorizationService = clientAuthorizationService;
            _accountService = accountService;
        }

        /// <summary>
        /// 驗證客戶端 [Authorization Basic Base64(clientId:clientSecret)|Authorization: Basic 5zsd8ewF0MqapsWmDwFmQmeF0Mf2gJkW]
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            //var generateClientSecret = await _clientAuthorizationService.GenerateOAuthClientSecretAsync();
            
            //validate client credentials should be stored securely (salted, hashed, iterated)
            string clientId;
            string clientSecret;
            context.TryGetBasicCredentials(out clientId, out clientSecret);
            var clientValid = await _clientAuthorizationService.ValidateClientAuthorizationSecretAsync(clientId, clientSecret);
            if (!clientValid)
            {
                //context.Rejected();
                context.SetError(AbpConstants.InvalidClient, AbpConstants.InvalidClientErrorDescription);
                return;
            }
            //need to make the client_id available for later security checks
            context.OwinContext.Set<string>("as:client_id", clientId);
            //context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", _clientAuthorizationProviderService.RefreshTokenLifeTime.ToString());
            context.Validated(clientId);
        }

        /// <summary>
        ///  驗證用戶名與密碼 [Resource Owner Password Credentials Grant[username與password]|grant_type=password&username=irving&password=654321]
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
            //validate user credentials (驗證用戶名與密碼)  should be stored securely (salted, hashed, iterated) 
            var userValid = await _accountService.ValidateUserNameAuthorizationPwdAsync(context.UserName, context.Password);
            if (!userValid)
            {
                //context.Rejected();
                context.SetError(AbpConstants.AccessDenied, AbpConstants.AccessDeniedErrorDescription);
                return;
            }
            var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
            claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
            var ticket = new AuthenticationTicket(claimsIdentity, new AuthenticationProperties());
            context.Validated(ticket);
            /*
            //create identity
            var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
            claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
            claimsIdentity.AddClaim(new Claim("sub", context.UserName));
            claimsIdentity.AddClaim(new Claim("role", "user"));
            // create metadata to pass on to refresh token provider
            var props = new AuthenticationProperties(new Dictionary<string, string>
                            {
                                {"as:client_id", context.ClientId }
                            });
            var ticket = new AuthenticationTicket(claimsIdentity, props);
            context.Validated(ticket);
            */
        }

        /// <summary>
        /// 驗證持有 refresh token 的客戶端
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
        {
            if (context.Ticket == null || context.Ticket.Identity == null)
            {
                context.Rejected();
                return base.GrantRefreshToken(context);
            }
            var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
            var currentClient = context.OwinContext.Get<string>("as:client_id");
            // enforce client binding of refresh token
            if (originalClient != currentClient)
            {
                context.Rejected();
                return base.GrantRefreshToken(context);
            }
            // chance to change authentication ticket for refresh token requests
            var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
            var props = new AuthenticationProperties(new Dictionary<string, string>
                {
                    { "as:client_id", context.ClientId }
                });
            var newTicket = new AuthenticationTicket(claimsIdentity, props);
            context.Validated(newTicket);
            return base.GrantRefreshToken(context);
        }
    }

RefreshAuthenticationTokenProvider[刷新Token]

/// <summary>
    /// 刷新Token
    /// </summary>
    public class RefreshAuthenticationTokenProvider : AuthenticationTokenProvider
    {
        private readonly ConcurrentDictionary<string, string> _authenticationCodes = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);

        /// <summary>
        /// Password Grant 授權服務
        /// </summary>
        private readonly IClientAuthorizationService _clientAuthorizationService;

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="clientAuthorizationService">Password Grant 授權服務</param>
        public RefreshAuthenticationTokenProvider(IClientAuthorizationService clientAuthorizationService)
        {
            _clientAuthorizationService = clientAuthorizationService;
        }

        /// <summary>
        /// 創建refreshToken
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task CreateAsync(AuthenticationTokenCreateContext context)
        {
            if (string.IsNullOrEmpty(context.Ticket.Identity.Name)) return;
            var clietId = context.OwinContext.Get<string>("as:client_id");
            if (string.IsNullOrEmpty(clietId)) return;
            var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
            //if (string.IsNullOrEmpty(refreshTokenLifeTime)) return;

            string ip = context.Request.RemoteIpAddress;
            int? port = context.Request.RemotePort;
            var token = new Token()
            {
                ClientId = clietId,
                UserName = context.Ticket.Identity.Name,
                IssuedUtc = DateTime.UtcNow,
                ExpiresUtc = DateTime.UtcNow.AddSeconds(Convert.ToDouble(refreshTokenLifeTime)),
            };
            context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
            context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
            token.Access_token = context.SerializeTicket();
            token.Refresh_token = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).TrimEnd('=').Replace('+', '-').Replace('/', '_');
            if (await _clientAuthorizationService.SaveTokenAsync(token))
            {
                context.SetToken(token.Refresh_token);
            }
            /*
            // maybe only create a handle the first time, then re-use for same client
            // copy properties and set the desired lifetime of refresh token
            var tokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
            {
                IssuedUtc = context.Ticket.Properties.IssuedUtc,
                ExpiresUtc = context.Ticket.Properties.ExpiresUtc
            };
            var token = context.SerializeTicket();
            var refreshTicket = new AuthenticationTicket(context.Ticket.Identity, tokenProperties);
            _refreshTokens.TryAdd(token, refreshTicket);
            // consider storing only the hash of the handle
            context.SetToken(token);
            */
        }

        /// <summary>
        /// 刷新refreshToken[刷新access token時,refresh token也會重新生成]
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task ReceiveAsync(AuthenticationTokenReceiveContext context)
        {
            string token = context.Token;
            string value;
            if (_authenticationCodes.TryRemove(context.Token, out value))
            {
                context.DeserializeTicket(value);
            }
            return base.ReceiveAsync(context);
        }

DI[DependencyInjectionConfig]

 //注冊 Password Grant 授權服務
  builder.RegisterType<PasswordAuthorizationServerProvider>().AsSelf().SingleInstance();
  builder.RegisterType<RefreshAuthenticationTokenProvider>().AsSelf().SingleInstance();

啟用驗證不記名授權[WebApiConfig]

    // Configure Web API to use only bearer token authentication.
    config.SuppressDefaultHostAuthentication();
    config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

資源服務[AccountController]

    /// <summary>
    /// 賬戶控制器
    /// </summary>
    [RoutePrefix("api/v1/account")]
    public class AccountController : ApiController
    {
        /// <summary>
        /// 用戶登錄
        /// </summary>
        /// <returns></returns>
        [Authorize]
        [Route("signin")]
        public async Task<IHttpActionResult> SignInAsync(LoginViewModel lg)
        {
            return Ok(new { IsError = true, Msg = string.Empty, Data = string.Empty });
        }

        /// <summary>
        /// 用戶信息
        /// </summary>
        /// <returns></returns>
        [Authorize]
        [Route("info")]
        public async Task<IHttpActionResult> InfoAsync()
        {
            return Ok(new { IsError = true, Msg = "irving", Data = string.Empty });
        }


        [Authorize]
        [HttpGet]
        [Route("api/account/profile")]
        public HttpResponseMessage Profile()
        {
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new ObjectContent<object>(new
                {
                    UserName = User.Identity.Name
                }, Configuration.Formatters.JsonFormatter)
            };
        }

客戶端

獲得票據

服務端[/token]獲取token需要三個參數+ Basic Authorzation BASE64(client_id+client_secret)

POST https://domain.com/token HTTP/1.1
Content-type:application/json;charset=UTF-8
Authorization: Basic Base64(clientId:clientSecret)
username=irving&password=123456&grant_type=password

    {
        "access_token": "uqoRNxKiC5tdNkD-Q8z2RRW98O8j2ZXf2RsQLd0x0IpaZJexQBeH67mRHFqyU4bwRd5nZLdOg-akEOwQRJaDBGkAqpi0tTvsqHU7EsEdNuS6SNugVQX6FTVSBmvVSJhuXmQeZBBiUl-20ZgxLFF4gpKD5HiNIuOG0ZPHAt-dNXV22e3i4hdhL-KoNMAf6xIF0Rx-18syravzRTPMtCoIcA",
        "token_type": "bearer",
        "expires_in": 7199,
        "refresh_token": "h6Bwe_hNljTubhqhmXCK8A"
    }

image

刷新票據

POST https://domain.com/token HTTP/1.1
Content-type:application/json;charset=UTF-8
Authorization: Basic Base64(clientId:clientSecret)
grant_type=refresh_token&refresh_token=h6Bwe_hNljTubhqhmXCK8A

    {
        "access_token": "rw_W_xic8xNlGd1kW06QiDLfXibmMPzUFAlSQx0jZ6KUcLq7bxMBBnI8ttITuhp1exus2wLOOgJ-bOzXz4y11fvbm9Do1rUiwYNvsbBFBsMnut2PYsC_6mBlFkUCYTaEZVhEwtopP_9cAVmC4G-UonQTsQ943ejtiLLc6nYQqVQvYe_0tndRxz2uBuLdc_KNcavs8AVq5QlAjCmozvZC1g",
        "token_type": "bearer",
        "expires_in": 7199,
        "refresh_token": "enGVqAraNTgv8EpjCxszMA"
    }

image

請求資源

設置HTTP頭 Authorization: Bearer {THE TOKEN}

POST https://domain.com//api/v1/account/info HTTP/1.1
Content-type:application/json;charset=UTF-8
Authorization: Bearer rw_W_xic8xNlGd1kW06QiDLfXibmMPzUFAlSQx0jZ6KUcLq7bxMBBnI8ttITuhp1exus2wLOOgJ-bOzXz4y11fvbm9Do1rUiwYNvsbBFBsMnut2PYsC_6mBlFkUCYTaEZVhEwtopP_9cAVmC4G-UonQTsQ943ejtiLLc6nYQqVQvYe_0tndRxz2uBuLdc_KNcavs8AVq5QlAjCmozvZC1g
    {
        "IsError": true,
        "Msg": "irving",
        "Data": ""
    }

image

Code Test

基礎代碼Test

   [RoutePrefix("api/v1/oauth")] public class OAuthController : ApiController { /// <summary>
        /// 獲取token /// </summary>
        /// <returns></returns>
        [Route("token")] public async Task<IHttpActionResult> GetTokenAsync() { //獲得token
            var dict = new SortedDictionary<string, string>(); dict.Add("grant_type", "password"); dict.Add("username", "irving"); dict.Add("password", "123456"); //var data = await (@"http://" + Request.RequestUri.Authority + @"/token").WithHeader("Authorization", "Basic " + Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("irving" + ":" + "123456"))).PostUrlEncodedAsync(dict).ReceiveJson<PToken>(); //var order = await (@"http://" + Request.RequestUri.Authority + @"/api/v1/oauth/order").WithHeader("Authorization", "Bearer " + data.access_token).GetAsync().ReceiveString();
            var data = await (@"http://" + Request.RequestUri.Authority + @"/token").WithBasicAuth("irving", "123456").PostUrlEncodedAsync(dict).ReceiveJson<PToken>(); var order = await (@"http://" + Request.RequestUri.Authority + @"/api/v1/oauth/order").WithOAuthBearerToken(data.access_token).GetAsync().ReceiveString(); return Ok(new { IsError = true, Msg = order, Data = data }); } /// <summary>
        /// 獲得訂單信息 /// </summary>
        /// <returns></returns>
 [Authorize] [Route("order")] public async Task<IHttpActionResult> GetOrderAsync() { string username = User.Identity.Name; var authentication = HttpContext.Current.GetOwinContext().Authentication; var ticket = authentication.AuthenticateAsync("Bearer").Result; return Ok(new { IsError = true, Msg = string.Empty, Data = Thread.CurrentPrincipal.Identity.Name + " It's about news !!! token expires: " + ticket.Properties.Dictionary.ToJson() }); } } public class PToken { public string access_token { get; set; } public string refresh_token { get; set; } public string token_type { get; set; } public string expires_in { get; set; } }

GitHub

https://github.com/zhouyongtao/App.WebAPI

Share PDF

分享一個PDF Securing ASP.NET Web APIs

http://sddconf.com/brands/sdd/library/Securing_ASPdotNET_web_APIs.pdf

Refer:

OpenID Connect Provider and OAuth2 Authorization Server Framework

https://github.com/IdentityServer/IdentityServer3

Chapter 16. The OAuth 2.0 Authorization Framework

http://chimera.labs.oreilly.com/books/1234000001708/ch16.html

Token Based Authentication using ASP.NET Web API 2, Owin, and Identity

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

Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin

http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/

Creating an OAuth password grant type token endpoint

http://www.hackered.co.uk/articles/asp-net-mvc-creating-an-oauth-password-grant-type-token-endpoint

Token Based Authentication in Web API 2

http://www.c-sharpcorner.com/UploadFile/736ca4/token-based-authentication-in-web-api-2/

Adding Refresh Tokens to a Web API v2 Authorization Server

http://leastprivilege.com/2013/11/15/adding-refresh-tokens-to-a-web-api-v2-authorization-server/

RESTful API With Node.js + MongoDB

http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb

Beer Locker: Building a RESTful API With Node - OAuth2 Server

http://scottksmith.com/blog/2014/07/02/beer-locker-building-a-restful-api-with-node-oauth2-server

http://scottksmith.com/blog/2014/05/29/beer-locker-building-a-restful-api-with-node-passport/

http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server

http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api

How to implement oauth2 server in ASP.NET MVC 5 and WEB API 2

http://stackoverflow.com/questions/26755573/how-to-implement-oauth2-server-in-asp-net-mvc-5-and-web-api-2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM