Winform宿主Asp.Net WebApi中Owin 自定义Token请求参数

本文介绍的是Winform宿主Asp.net WebAPI的过程,利用了Microsoft.AspNet.WebApi.OwinSelfHost.

Asp.NET WebAPI中的授权验证有很多:

例如选用Bearer Token验证:利用Asp.Net Owin实现:基本套路为:(宿主Winform程序为例)

1:nuget owin相关的安装包:

Microsoft.AspNet.WebApi.OwinSelfHost

Microsoft.Owin.Security.OAuth

Microsoft.AspNet.WebApi.Owin

其他相关依赖包自动下载;

2:开启webapi服务:

 Microsoft.Owin.Hosting.WebApp.Start<Startup>("http://127.0.0.1:8996/");

Startup类如下:

public class Startup
{
        public void Configuration(IAppBuilder app)
        {
            SimpleAuthorizationProvider provider = new SimpleAuthorizationProvider();
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/api/auth/signin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
                Provider = provider,
            };
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions);            
            HttpConfiguration config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}"
                );
            app.UseWebApi(config);
        }
}

 SimpleAuthorizationProvider .cs

/// <summary>
    /// Token验证
    /// </summary>
    public class SimpleAuthorizationProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {          
            await Task.Factory.StartNew(() => context.Validated());
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            await Task.Factory.StartNew(() => context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }));            
            string userName = context.UserName;
            string pwd = context.Password;
            if (userName == "test" && pwd == "1")
            {
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);     
                identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
                context.Validated(identity);
            }
            else
            {
                context.SetError("invalid_grant", "用户和密码验证失败!");
            }
        }
    }

3:Postman验证:

其中:grant_type 和 username、password为固定的参数,且grant_type固定为"password",否则请求token不成功;

至于为何是这样,参考Owin框架源码分析就可以知道原因:

源码地址:https://github.com/aspnet/AspNetKatana

源码位置:TokenEndpointRequest.cs 构造函数处

  

重点来了,如果希望改变获取Token的参数,该如何处理呢?

解决办法如下:

步骤1:找到源码下如下图所示两个文件:原样复制过来:

步骤2:把如上两个文件粘贴到项目中,重命名:如:MyOAuthAuthorizationServerHandler和

MyOAuthAuthorizationServerMiddleware

步骤3:修改Setup 中的Configuration方法:

public void Configuration(IAppBuilder app)
        {
            SimpleAuthorizationProvider provider = new SimpleAuthorizationProvider();
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/api/auth/signin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
                Provider = provider,
            };
            //app.UseOAuthAuthorizationServer(OAuthServerOptions);
            //app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions() { });

            app.Use(typeof(MyOAuthAuthorizationServerMiddleware), app, OAuthServerOptions);
            app.UseOAuthBearerTokens(OAuthServerOptions);


            HttpConfiguration config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}"
                );
            app.UseWebApi(config);
        }

步骤4:修改 MyOAuthAuthorizationServerMiddleware 下获取HttpRequest中 Body参数的代码:

如下所示:

  private async Task InvokeTokenEndpointAsync()
        {
            DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
            // remove milliseconds in case they don't round-trip
            currentUtc = currentUtc.Subtract(TimeSpan.FromMilliseconds(currentUtc.Millisecond));
           
            IDictionary<string, string[]> formDic = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
            formDic.Add(Constants.Parameters.GrantType, new string[] { "password" });
            using (var reader = new StreamReader(Request.Body, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 4 * 1024, leaveOpen: true))
            {
                string text = await reader.ReadToEndAsync();
                var userInfo=  JsonConvert.DeserializeObject<UserInfo>(text);
                formDic.Add(Constants.Parameters.Username, new string[] { userInfo.User });
                formDic.Add(Constants.Parameters.Password, new string[] { userInfo.Pwd });
            }
            IFormCollection form=new FormCollection(formDic);
            
            //以下是原来的代码
            //IFormCollection form = await Request.ReadFormAsync();
            var clientContext = new OAuthValidateClientAuthenticationContext(Context, Options, form);

            await Options.Provider.ValidateClientAuthentication(clientContext);
        //....省略后面的代码
}

关键思路就是读取Body Stream流,然后解析得到Json字符串,转成对象后,给IFormCollection赋值实例化。此后继续Owin框架的功能流程不变;

实现效果如下图所示:

 如果要自定义返回的Json格式呢,例如变成下面这样:

同理:在最后的返回结果处,修改成想要的格式:

 private async Task InvokeTokenEndpointAsync()
        {
            DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
            // remove milliseconds in case they don't round-trip
            currentUtc = currentUtc.Subtract(TimeSpan.FromMilliseconds(currentUtc.Millisecond));
           
            IDictionary<string, string[]> formDic = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
            formDic.Add(Constants.Parameters.GrantType, new string[] { "password" });
            using (var reader = new StreamReader(Request.Body, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 4 * 1024, leaveOpen: true))
            {
                string text = await reader.ReadToEndAsync();
                var userInfo=  JsonConvert.DeserializeObject<UserInfo>(text);
                formDic.Add(Constants.Parameters.Username, new string[] { userInfo.User });
                formDic.Add(Constants.Parameters.Password, new string[] { userInfo.Pwd });
            }
            IFormCollection form=new FormCollection(formDic);
            
            //以下是原来的代码
            //IFormCollection form = await Request.ReadFormAsync();
            var clientContext = new OAuthValidateClientAuthenticationContext(Context, Options, form);

            await Options.Provider.ValidateClientAuthentication(clientContext);

            if (!clientContext.IsValidated)
            {
                _logger.WriteError("clientID is not valid.");
                if (!clientContext.HasError)
                {
                    clientContext.SetError(Constants.Errors.InvalidClient);
                }
                await SendErrorAsJsonAsync(clientContext);
                return;
            }

            var tokenEndpointRequest = new TokenEndpointRequest(form);

            var validatingContext = new OAuthValidateTokenRequestContext(Context, Options, tokenEndpointRequest, clientContext);

            AuthenticationTicket ticket = null;
            if (tokenEndpointRequest.IsAuthorizationCodeGrantType)
            {
                // Authorization Code Grant http://tools.ietf.org/html/rfc6749#section-4.1
                // Access Token Request http://tools.ietf.org/html/rfc6749#section-4.1.3
                ticket = await InvokeTokenEndpointAuthorizationCodeGrantAsync(validatingContext, currentUtc);
            }
            else if (tokenEndpointRequest.IsResourceOwnerPasswordCredentialsGrantType)
            {
                // Resource Owner Password Credentials Grant http://tools.ietf.org/html/rfc6749#section-4.3
                // Access Token Request http://tools.ietf.org/html/rfc6749#section-4.3.2
                ticket = await InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(validatingContext, currentUtc);
            }
            else if (tokenEndpointRequest.IsClientCredentialsGrantType)
            {
                // Client Credentials Grant http://tools.ietf.org/html/rfc6749#section-4.4
                // Access Token Request http://tools.ietf.org/html/rfc6749#section-4.4.2
                ticket = await InvokeTokenEndpointClientCredentialsGrantAsync(validatingContext, currentUtc);
            }
            else if (tokenEndpointRequest.IsRefreshTokenGrantType)
            {
                // Refreshing an Access Token
                // http://tools.ietf.org/html/rfc6749#section-6
                ticket = await InvokeTokenEndpointRefreshTokenGrantAsync(validatingContext, currentUtc);
            }
            else if (tokenEndpointRequest.IsCustomExtensionGrantType)
            {
                // Defining New Authorization Grant Types
                // http://tools.ietf.org/html/rfc6749#section-8.3
                ticket = await InvokeTokenEndpointCustomGrantAsync(validatingContext, currentUtc);
            }
            else
            {
                // Error Response http://tools.ietf.org/html/rfc6749#section-5.2
                // The authorization grant type is not supported by the
                // authorization server.
                _logger.WriteError("grant type is not recognized");
                validatingContext.SetError(Constants.Errors.UnsupportedGrantType);
            }

            if (ticket == null)
            {
                await SendErrorAsJsonAsync(validatingContext);
                return;
            }

            ticket.Properties.IssuedUtc = currentUtc;
            ticket.Properties.ExpiresUtc = currentUtc.Add(Options.AccessTokenExpireTimeSpan);

            var tokenEndpointContext = new OAuthTokenEndpointContext(Context, Options, ticket, tokenEndpointRequest);

            await Options.Provider.TokenEndpoint(tokenEndpointContext);

            if (tokenEndpointContext.TokenIssued)
            {
                ticket = new AuthenticationTicket(tokenEndpointContext.Identity, tokenEndpointContext.Properties);
            }
            else
            {
                _logger.WriteError("Token was not issued to tokenEndpointContext");
                validatingContext.SetError("invalid_grant");
                await SendErrorAsJsonAsync(validatingContext);
                return;
            }

            var accessTokenContext = new AuthenticationTokenCreateContext(Context, Options.AccessTokenFormat, ticket);

            await Options.AccessTokenProvider.CreateAsync(accessTokenContext);

            string accessToken = accessTokenContext.Token;
            if (string.IsNullOrEmpty(accessToken))
            {
                accessToken = accessTokenContext.SerializeTicket();
            }
            DateTimeOffset? accessTokenExpiresUtc = ticket.Properties.ExpiresUtc;

            var refreshTokenCreateContext = new AuthenticationTokenCreateContext(Context, Options.RefreshTokenFormat, accessTokenContext.Ticket);
            await Options.RefreshTokenProvider.CreateAsync(refreshTokenCreateContext);
            string refreshToken = refreshTokenCreateContext.Token;

            var tokenEndpointResponseContext = new OAuthTokenEndpointResponseContext(Context, Options, ticket, tokenEndpointRequest, accessToken, tokenEndpointContext.AdditionalResponseParameters);

            await Options.Provider.TokenEndpointResponse(tokenEndpointResponseContext);

            var memory = new MemoryStream();
            byte[] body;

            /*
             *
             *public T Data { get; set; }
        public bool IsError { get; set; }
        public string Message { get; set; }
        public int ErrorCode { get; set; }
             */
            var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
            
            using (var writer = new JsonTextWriter(new StreamWriter(memory)))
            {
                writer.WriteStartObject();
                writer.WritePropertyName("Data");
                TokenInfo tokenInfo = new TokenInfo();
                tokenInfo.Token = accessToken;
                tokenInfo.TokenType = "bearer";

                JsonSerializer serializer = new JsonSerializer();
                //var jsn = new JObject();
                //jsn["Data"] = JObject.FromObject( tokenInfo);
                serializer.Serialize(writer, tokenInfo);            
                writer.WritePropertyName("IsError");
                writer.WriteValue(false);

                writer.WritePropertyName("Message");
                writer.WriteValue(String.Empty);

                writer.WritePropertyName("ErrorCode");
                writer.WriteValue(0);

                if (accessTokenExpiresUtc.HasValue)
                {
                    TimeSpan? expiresTimeSpan = accessTokenExpiresUtc - currentUtc;
                    var expiresIn = (long)expiresTimeSpan.Value.TotalSeconds;
                    if (expiresIn > 0)
                    {
                        writer.WritePropertyName("expires_in");
                        writer.WriteValue(expiresIn);
                    }
                }
                if (!String.IsNullOrEmpty(refreshToken))
                {
                    writer.WritePropertyName("refresh_token");
                    writer.WriteValue(refreshToken);
                }
                foreach (var additionalResponseParameter in tokenEndpointResponseContext.AdditionalResponseParameters)
                {
                    writer.WritePropertyName(additionalResponseParameter.Key);
                    writer.WriteValue(additionalResponseParameter.Value);
                }
                writer.WriteEndObject();
                writer.Flush();
                body = memory.ToArray();
            }
            Response.ContentType = "application/json;charset=UTF-8";
            Response.Headers.Set("Cache-Control", "no-cache");
            Response.Headers.Set("Pragma", "no-cache");
            Response.Headers.Set("Expires", "-1");
            Response.ContentLength = body.Length;
            await Response.WriteAsync(body, Request.CallCancelled);
        }

 参考#region 自定义格式部分代码

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值