zoukankan      html  css  js  c++  java
  • 基于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

    基本流程

     

    Client Credentials Grant

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

    服务实现

    使用WEBAPI基于Microsoft.Owin.Security.OAuth实现,源码:http://katanaproject.codeplex.com/SourceControl/latest#src ;新建一个不启用身份验证空的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}

    image

    请求资源

    设置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"}"}

    image

    客户端测试

        /// <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
    [OAuth]基于DotNetOpenAuth实现Client Credentials Grant
    http://www.cnblogs.com/dudu/p/oauth-dotnetopenauth-client-credentials-grant.html
    一些例子
    http://open.koudaitong.com/doc/oauth
    http://developer.baidu.com/wiki/index.php?title=docs/oauth/client
    https://wakatime.com/developers

  • 相关阅读:
    学习进度总结表
    关于软件工程的问题
    自我介绍
    web安全
    spring profile
    spring 装配
    python Descriptor (描述符)
    python string intern
    Java 对象内存占用
    java jdb命令详解
  • 原文地址:https://www.cnblogs.com/Irving/p/4607104.html
Copyright © 2011-2022 走看看