(转)基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】

适应范围

采用Client Credentials方式,即应用公钥、密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open API,并且需要开发者提前向开放平台申请,成功对接后方能使用。认证服务器不提供像用户数据这样的重要资源,仅仅是有限的只读资源或者一些开放的 API。例如使用了第三方的静态文件服务,如Google Storage或Amazon S3。这样,你的应用需要通过外部API调用并以应用本身而不是单个用户的身份来读取或修改这些资源。这样的场景就很适合使用客户端证书授权,通过此授权方式获取Access Token仅可访问平台授权类的接口。

比如获取App首页最新闻列表,由于这个数据与用户无关,所以不涉及用户登录与授权,但又不想任何人都可以调用这个WebAPI,这样场景就适用[例:比如微信公众平台授权]。

Client Credentials Grant:http://tools.ietf.org/html/rfc6749#section-4.4
     +---------+                                  +---------------+
     |         |                                  |               |
     |         |>--(A)- Client Authentication --->| Authorization |
     | Client  |                                  |     Server    |
     |         |<--(B)---- Access Token ---------<|               |
     |         |                                  |               |
     +---------+                                  +---------------+

                     Figure 6: Client Credentials Flow

基本流程

 

A.客户端提供用户名和密码交换令牌
B.认证服务器验证通过,发放令牌,后面根据这个令牌获取资源即可

服务实现

使用WEBAPI基于Microsoft.Owin.Security.OAuth实现,新建一个不启用身份验证空的WEBAPI项目

安装包

Install-Package Microsoft.AspNet.Identity.Owin

Install-Package Microsoft.Owin.Security.OAuth

Install-Package Microsoft.AspNet.WebApi.Owin

Install-Package Microsoft.AspNet.WebApi.WebHost

Install-Package Microsoft.Owin.Host.SystemWeb

OWIN WEBAPI

[assembly: OwinStartup(typeof(Startup))]
namespace OAuth2.App_Start
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }
    }
}
    public partial class Startup
    {
        public void ConfigureAuth(IAppBuilder app)
        {
            /*
              app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
             {
                 TokenEndpointPath = new PathString("/token"),
                 Provider = new ApplicationOAuthProvider(),
                 AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
                 AuthenticationMode = AuthenticationMode.Active,
                 //HTTPS is allowed only AllowInsecureHttp = false
                 AllowInsecureHttp = true
                 //ApplicationCanDisplayErrors = false
             });
             app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
             */
            app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/token"),
                Provider = new ApplicationOAuthProvider(),
                //RefreshTokenProvider = new ApplicationRefreshTokenProvider(),
                AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
                AuthenticationMode = AuthenticationMode.Active,
                //HTTPS is allowed only AllowInsecureHttp = false
                AllowInsecureHttp = true
                //ApplicationCanDisplayErrors = false
            });
        }
    }
    public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        /*
         private OAuth2ClientService _oauthClientService;
         public ApplicationOAuthProvider()
         {
             this.OAuth2ClientService = new OAuth2ClientService();
         }
         */

        /// <summary>
        /// 验证客户[client_id与client_secret验证]
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            //http://localhost:48339/token
            //grant_type=client_credentials&client_id=irving&client_secret=123456
            string client_id;
            string client_secret;
            context.TryGetFormCredentials(out client_id, out client_secret);
            if (client_id == "irving" && client_secret == "123456")
            {
                context.Validated(client_id);
            }
            else
            {
                //context.Response.StatusCode = Convert.ToInt32(HttpStatusCode.OK);
                context.SetError("invalid_client", "client is not valid");
            }
            return base.ValidateClientAuthentication(context);
        }

        /// <summary>
        /// 客户端授权[生成access token]
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
        {
            /*
                 var client = _oauthClientService.GetClient(context.ClientId);
                 oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName));
             */

            var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
            oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, "iphone"));
            var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties() { AllowRefresh = true });
            context.Validated(ticket);
            return base.GrantClientCredentials(context);
        }

        /// <summary>
        /// 刷新Token[刷新refresh_token]
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
        {
            //enforce client binding of refresh token
            if (context.Ticket == null || context.Ticket.Identity == null || !context.Ticket.Identity.IsAuthenticated)
            {
                context.SetError("invalid_grant", "Refresh token is not valid");
            }
            else
            {
                //Additional claim is needed to separate access token updating from authentication
                //requests in RefreshTokenProvider.CreateAsync() method
            }
            return base.GrantRefreshToken(context);
        }

        public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            if (context.ClientId == "irving")
            {
                var expectedRootUri = new Uri(context.Request.Uri, "/");
                if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                {
                    context.Validated();
                }
            }
            return Task.FromResult<object>(null);
        }
    }

资源服务

    /// <summary>
    ///客户端模式【Client Credentials Grant】
    ///http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api
    /// </summary>
    [RoutePrefix("api/v1/oauth2")]
    public class OAuth2Controller : ApiController
    {
        /// <summary>
        /// 获得资讯
        /// </summary>
        /// <returns></returns>
        [Authorize]
        [Route("news")]
        public async Task<IHttpActionResult> GetNewsAsync()
        {
            var authentication = HttpContext.Current.GetOwinContext().Authentication;
            var ticket = authentication.AuthenticateAsync("Bearer").Result;

            var claimsIdentity = User.Identity as ClaimsIdentity;
            var data = claimsIdentity.Claims.Where(c => c.Type == "urn:oauth:scope").ToList();
            var claims = ((ClaimsIdentity)Thread.CurrentPrincipal.Identity).Claims;
            return Ok(new { IsError = true, Msg = string.Empty, Data = Thread.CurrentPrincipal.Identity.Name + " It‘s about news !!! token expires: " + ticket.Properties.Dictionary.ToJson() });
        }
    }

启用授权验证[WebApiConfig]

在ASP.NET Web API中启用Token验证,需要加上[Authorize]标记,并且配置默认启用验证不记名授权方式

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

客户端

获得票据

服务端[/token]获取token需要三个参数

POST https://domain.com/token HTTP/1.1
Content-type:application/json;charset=UTF-8

grant_type=client_credentials&client_id=irving&client_secret=123456

{"access_token":"qghSowAcM9Ap7yIiyZ6i52VOk4NWBpgDDJZ6jf-PdAeP4roFMlGhKUV_Kg_ow0QgTXBKaPzIFBLzdc6evBUPVOaV8Op0wsrUwwKUjRluAPAQmw3MIm8MtmtC0Vfp7ZByuvvMy21NbpRBZcajQxzunJGPIqbdPMYs8e279T5UMmgpBVZJuC4N6d-mxk3DMN2-42cOxz-3k6J-7yXCVYroEh6txjZW03ws155LswIg0yw","token_type":"bearer","expires_in":7199}

请求资源

设置HTTP头 Authorization: Bearer {THE TOKEN}

GET http://localhost:48339/api/v1/oauth2/news HTTP/1.1

Authorization: Bearer qghSowAcM9Ap7yIiyZ6i52VOk4NWBpgDDJZ6jf-PdAeP4roFMlGhKUV_Kg_ow0QgTXBKaPzIFBLzdc6evBUPVOaV8Op0wsrUwwKUjRluAPAQmw3MIm8MtmtC0Vfp7ZByuvvMy21NbpRBZcajQxzunJGPIqbdPMYs8e279T5UMmgpBVZJuC4N6d-mxk3DMN2-42cOxz-3k6J-7yXCVYroEh6txjZW03ws155LswIg0yw

grant_type=client_credentials&client_id=irving&client_secret=123456

{"IsError":true,"Msg":"","Data":"iphone It‘s about news !!! token expires: {\".refresh\":\"True\",\".issued\":\"Mon, 29 Jun 2015 02:47:12 GMT\",\".expires\":\"Mon, 29 Jun 2015 04:47:12 GMT\"}"}

客户端测试

    /// <summary>
    ///客户端模式【Client Credentials Grant】
    ///http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api
    /// </summary>
    [RoutePrefix("api/v1/oauth2")]
    public class OAuth2Controller : ApiController
    {
        /// <summary>
        /// 获取token
        /// </summary>
        /// <returns></returns>
        [Route("token")]
        public async Task<IHttpActionResult> GetTokenAsync()
        {
            //获得token
            var dict = new SortedDictionary<string, string>();
            dict.Add("client_id", "irving");
            dict.Add("client_secret", "123456");
            dict.Add("grant_type", "client_credentials");
            var data = await (@"http://" + Request.RequestUri.Authority + @"/token").PostUrlEncodedAsync(dict).ReceiveJson<Token>();
            //根据token获得咨询信息 [Authorization: Bearer {THE TOKEN}]
            //var news = await (@"http://" + Request.RequestUri.Authority + @"/api/v1/oauth2/news").WithHeader("Authorization", "Bearer " + data.access_token).GetAsync().ReceiveString();
            var news = await (@"http://" + Request.RequestUri.Authority + @"/api/v1/oauth2/news").WithOAuthBearerToken(data.access_token).GetAsync().ReceiveString();
            return Ok(new { IsError = true, Msg = data, Data = news });
        }
    }
    public class Token
    {
        public string access_token { get; set; }
        public string token_type { get; set; }
        public string expires_in { get; set; }
    }

Refer:
Secure a Web API with Individual Accounts and Local Login in ASP.NET Web API 2.2

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

Use OWIN to Self-Host ASP.NET Web API 2

http://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-api

OWIN OAuth 2.0 Authorization Server

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

ASP.Net MVC: Creating an OAuth client credentials grant type token endpoint

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

OwinStartup not Starting … Why?

http://stackoverflow.com/questions/19760545/owinstartup-not-starting-why

http://developer.baidu.com/wiki/index.php?title=docs/oauth/client

https://wakatime.com/developers

时间: 2024-10-09 21:17:56

(转)基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】的相关文章

在OWIN WebAPI中基于OAuth授权【客户端模式(Client Credentials Grant)】

适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open API,并且需要开发者提前向开放平台申请,成功对接后方能使用.认证服务器不提供像用户数据这样的重要资源,仅仅是有限的只读资源或者一些开放的 API.例如使用了第三方的静态文件服务,如Google Storage或Amazon S3.这样,你的应用需要通过外部API调用并以应用本身而不是单个用户的身份

基于OWIN WebAPI 使用OAuth授权服务【客户端验证授权(Resource Owner Password)】

RESTful API With Node.js + MongoDB http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb

在ASP.NET中基于Owin OAuth使用Client Credentials Grant授权发放Token

OAuth真是一个复杂的东东,即使你把OAuth规范倒背如流,在具体实现时也会无从下手.因此,Microsoft.Owin.Security.OAuth应运而生(它的实现代码在Katana项目中),帮助开发者偷了不少工,减了不少料. 这篇博文试图通过一个简单的示例分享一下如何基于Microsoft.Owin.Security.OAuth,使用Client Credentials Grant授权方式给客户端发放access token. Client Credentials Grant的授权方式就

基于 IdentityServer3 实现 OAuth 2.0 授权服务【密码模式(Resource Owner Password Credentials)】

密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码.客户端使用这些信息,向"服务商提供商"索要授权.基于之前的 IdentityServer3 实现 OAuth 2.0 授权服务[客户端模式(Client Credentials Grant)] 修改. 客户端 public class Clients { public static List<Client> Get() { return ne

在WebApi中基于Owin OAuth使用授权发放Token

如何基于Microsoft.Owin.Security.OAuth,使用Client CredentialsGrant授权方式给客户端发放access token? Client CredentialsGrant的授权方式就是只验证客户端(Client),不验证用户(Resource Owner),只要客户端通过验证就发accesstoken.举一个对应的应用场景例子,比如我们想提供一个“获取网站首页最新博文列表”的WebAPI给客户端App调用.由于这个数据与用户无关,所以不涉及用户登录与授权

nodejs实现OAuth2.0授权服务

OAuth是一种开发授权的网络标准,全拼为open authorization,即开放式授权,最新的协议版本是2.0. 举个栗子: 有一个"云冲印"的网站,可以将用户储存在Google的照片,冲印出来.用户为了使用该服务,必须让"云冲印"读取自己储存在Google上的照片. 传统方法是,用户将自己的Google用户名和密码,告诉"云冲印",后者就可以读取用户的照片了.这样的做法有以下几个严重的缺点. "云冲印"为了后续的服务,

8. Laravel5学习笔记:在laravel5中使用OAuth授权

OAuth2.0简介 关于它的介绍,给出以下两篇文章,相信看完,应该对它有一定程度的了解: [1] 理解OAuth 2.0 -- 阮一峰 [2] 帮你深入理解OAuth2.0协议 -- seccloud 这里我主要阐述的是在laravel5中使用OAuth2.0.关于这个协议本身,以及运行流程希望大家看完以上两篇文章,理解后,再看我后面的内容! Laravel5中安装OAuth2.0 OAuth2.0在官方中提供了 php的库 ,但是我们这里并不直接使用它官方提供的 Server Librari

【7】.net WebAPI Owin OAuth 2.0 密码模式验证实例

1.OAuth密码模式 2.在VS中创建WebAPI项目 在nuget中安装: Microsoft.AspNet.WebApi.Owin Microsoft.Owin.Host.SystemWeb 这两个类库并添加Owin启动类Startup using System; using System.Threading.Tasks; using Microsoft.Owin; using Owin; using Microsoft.Owin.Security.OAuth; [assembly: Ow

基于oauth授权框架其中获取令牌需要发送Http身份认证的请求

1.首先我们基于curl的请求参数示例: curl http://localhost:8080/oauth/token -X POST -u client:fucksecurity -d "grant_type=refresh_token&refresh_token=1a1fb46e-8ab4-4a3b-84c4-e70892eaa570"其中的 -u 表示的是身份认证的用户名和密码.后来尝试过Jquery的$.ajax的username和password去传这个两个对象,发现