您的位置:首页 > 编程语言 > ASP

Asp.Net Core Authentication Middleware And Generate Token

2017-03-12 10:06 507 查看
或者应该包含什么信息呢?
    1.这个人是谁?
    2.这个人可以用此token访问什么样的内容?(scope)
    3.token的过期时间 (expire)
    4.谁发行的token。
    5.其他任何你希望加入的声明(Claims)
 那我们为什么要使用token呢?使用session或者用redis来实现stateServer不好吗?
    1.token是低(无)状态的,Statelessness
    2.token可以与移动端应用紧密结合
    3.支持多平台服务器和分布式微服务

拿到token后如何带入HTTP请求传给后台?

  答案是两种方式,Cookies和Authorization Header。那么什么时候放到Cookies中,什么时候又放到Authentication中呢?
第一,如果是在Web应用,则放到Cookies当中,并且应该是HttpOnly的,js不能直接对其进行操作,安全性会比将其存在Web Stroage中好一些,因为在Web Storage当中的内容,可以很容的被潜在的XSS脚本攻击并获取。在HttpOnly的cookies当中会相对安全一些,不过也有潜在的CSRF跨站伪造请求的危险,不过这种hack的手段成功率是很低的,有兴趣的朋友可以自行看一下CSRF原理。
第二,如果是手机移动端应用的话,那一定是存储在App本地,并由Authorization Header带到后台并得到身份认证。

WebApp Cookies Authentication

上一段前两周写的最原始的小Demo吧,没有数据库访问等,可根据demo自行改变 ,现在的新代码已经加入了很多业务在其中
startup.cs代码
using Microsoft.AspNetCore.Authentication.Cookies;

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting;

using Microsoft.AspNetCore.Http;

using Microsoft.AspNetCore.Http.Authentication;

using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Logging;

using System.Collections.Generic;

using System.Security.Claims;

using Wings.AuthenticationApp.Middleware;

namespace Wings.AuthenticationApp

{

    public class Startup

    {

        public Startup(IHostingEnvironment env)

        {

            var builder = new ConfigurationBuilder()

                .SetBasePath(env.ContentRootPath)

                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)

                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)

                .AddEnvironmentVariables();

            Configuration = builder.Build();

        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.

        public void ConfigureServices(IServiceCollection services)

        {

            // Add framework services.

            services.AddMvc();

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

        {

            loggerFactory.AddConsole(Configuration.GetSection("Logging"));

            loggerFactory.AddDebug();

            app.UseCookieAuthentication(CookieAuthMiddleware.GetOptions());

            app.UseOwin();

            app.UseCors(a => { a.AllowAnyOrigin(); });

            app.UseMvc();

            // Listen for login and logout requests

            app.Map("/login", builder =>

            {

                builder.Run(async context =>

                {

                    var name = context.Request.Form["name"];

                    var pwd = context.Request.Form["pwd"];

                    if (name == "wushuang" && pwd == "wushuang")

                    {

                        var claims = new List<Claim>() { new Claim("name", name), new Claim("role", "admin") };

                        var identity = new ClaimsIdentity(claims, "password");

                        var principal = new ClaimsPrincipal(identity);

                        await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);

                        context.Response.Redirect("http://www.baidu.com");

                    }

                    else

                    {

                        await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

                        context.Response.Redirect("http://www.google.com");

                    }

                });

            });

            //app.Map("/logout", builder =>

            //{

            //    builder.Run(async context =>

            //    {

            //        // Sign the user out / clear the auth cookie

            //        await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

            //        // Perform a simple redirect after logout

            //        context.Response.Redirect("/");

            //    });

            //});

            

        }

    }

}

下面是Middleware---->CookieAuthMiddleware.cs的代码,
using Microsoft.AspNetCore.Authentication.Cookies;

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Http;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Security.Claims;

using System.Security.Principal;

using System.Threading.Tasks;

namespace Wings.AuthenticationApp.Middleware

{

    public class CookieAuthMiddleware

    {

        public static CookieAuthenticationOptions GetOptions()

        {

            return new CookieAuthenticationOptions

            {

                AutomaticAuthenticate = true,

                AutomaticChallenge = true,

                LoginPath = new PathString("/login"),

                LogoutPath = new PathString("/logout"),

                AccessDeniedPath = new PathString("/test"),

                CookieHttpOnly = false,  //默认就是True了

                CookieName = "wings_access_token",

                SlidingExpiration = true,

                CookieManager = new ChunkingCookieManager()

            };

        }

    }

    public static class IdentityExtension

    {

        public static string FullName(this IIdentity identity)

        {

            var claim = ((ClaimsIdentity)identity).FindFirst("name");

            return (claim != null) ? claim.Value : string.Empty;

        }

        public static string Role(this IIdentity identity)

        {

            var claim = ((ClaimsIdentity)identity).FindFirst("role");

            return (claim != null) ? claim.Value : string.Empty;

        }

    }

}

对应如上demo,简单测试一下,结果如下:



首先使用错误的密码,来请求token endpoint,接下来我们看一下即使窗口,当有请求进入的时候,我用如下代码判断用户的认证情况,拿到的结果必然是false:



接下来,我使用正确的账号密码,来打入token,判断结果一定为true,所以我使用自定义的拓展方法,来获取下,该用户token的信息:



如上demo没有加入一些容错机制,请注意。在用户认证成功后,可以进入带有Authorize Attribute的Action,否则401.如下是几个重要参数的解释



 

自定义Authentication Middle生产Token

 Startup.cs 
using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting;

using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Logging;

using Wings.TokenAuth.Middleware;

using System.Security.Claims;

using Microsoft.IdentityModel.Tokens;

using System.Text;

using Microsoft.Extensions.Options;

namespace Wings.TokenAuth

{

    public class Startup

    {

        public Startup(IHostingEnvironment env)

        {

            var builder = new ConfigurationBuilder()

                .SetBasePath(env.ContentRootPath)

                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)

                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)

                .AddEnvironmentVariables();

            Configuration = builder.Build();

        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.

        public void ConfigureServices(IServiceCollection services)

        {

            // Add framework services.

            services.AddMvc();

        }

        // The secret key every token will be signed with.

        // In production, you should store this securely in environment variables

        // or a key management tool. Don't hardcode this into your application!

        private static readonly string secretKey = "mysupersecret_secretkey!123";

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

        {

            loggerFactory.AddConsole(LogLevel.Debug);

            loggerFactory.AddDebug();

            app.UseStaticFiles();

            // Add JWT generation endpoint:

            var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

            var options = new TokenProviderOptions

            {

                Audience = "ExampleAudience",

                Issuer = "ExampleIssuer",

                SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),

            };

            app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));

            app.UseMvc();

        }

    }

}

TokenProviderOptions.cs
using Microsoft.AspNetCore.Http;

using Microsoft.Extensions.Options;

using Microsoft.IdentityModel.Tokens;

using Newtonsoft.Json;

using System;

using System.Collections.Generic;

using System.IdentityModel.Tokens.Jwt;

using System.Linq;

using System.Security.Claims;

using System.Threading.Tasks;

namespace Wings.TokenAuth.Middleware

{

    public class TokenProviderOptions

    {

        public string Path { get; set; } = "/token";

        public string Issuer { get; set; }

        public string Audience { get; set; }

        public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(5);

        public SigningCredentials SigningCredentials { get; set; }

    }

    public class TokenProviderMiddleware

    {

        private readonly RequestDelegate _next;

        private readonly TokenProviderOptions _options;

        public TokenProviderMiddleware(

          RequestDelegate next,

          IOptions<TokenProviderOptions> options)

        {

            _next = next;

            _options = options.Value;

        }

        public Task Invoke(HttpContext context)

        {

            // If the request path doesn't match, skip

            if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))

            {

             //use new JwtSecurityTokenHandler().ValidateToken() to valid token

                return _next(context);

            }

            // Request must be POST with Content-Type: application/x-www-form-urlencoded

            if (!context.Request.Method.Equals("POST")

              || !context.Request.HasFormContentType)

            {

                context.Response.StatusCode = 400;

                return context.Response.WriteAsync("Bad request.");

            }

            return GenerateToken(context);

        }

        private async Task GenerateToken(HttpContext context)

        {

            var username = context.Request.Form["username"];

            var password = context.Request.Form["password"];

            var identity = await GetIdentity(username, password);

            if (identity == null)

            {

                context.Response.StatusCode = 400;

                await context.Response.WriteAsync("Invalid username or password.");

                return;

            }

            var now = DateTime.UtcNow;

            // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.

            // You can add other claims here, if you want:

            var claims = new Claim[]

            {

    new Claim(JwtRegisteredClaimNames.Sub, username),

    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),

    new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(now).ToString(), ClaimValueTypes.Integer64)

            };

            // Create the JWT and write it to a string

            var jwt = new JwtSecurityToken(

              issuer: _options.Issuer,

              audience: _options.Audience,

              claims: claims,

              notBefore: now,

              expires: now.Add(_options.Expiration),

              signingCredentials: _options.SigningCredentials);

            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

            var response = new

            {

                access_token = encodedJwt,

                expires_in = (int)_options.Expiration.TotalSeconds

            };

            // Serialize and return the response

            context.Response.ContentType = "application/json";

            await context.Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));

        }

        private Task<ClaimsIdentity> GetIdentity(string username, string password)

        {

            // DON'T do this in production, obviously!

            if (username == "wushuang" && password == "wushuang")

            {

                return Task.FromResult(new ClaimsIdentity(new System.Security.Principal.GenericIdentity(username, "Token"), new Claim[] { }));

            }

            // Credentials are invalid, or account doesn't exist

            return Task.FromResult<ClaimsIdentity>(null);

        }

        public static long ToUnixEpochDate(DateTime date)

  => (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);

    }

}

下面上测试结果:
使用错误的账户和密码请求token



使用正确的账户和密码来请求,返回结果如下:



如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下加【推荐】按钮。
如果,您希望更容易地发现我的新博客,不妨点击下方红色【关注】的。
因为,我的分享热情也离不开您的肯定支持。
感谢您的阅读,我将持续输出分享,我是蜗牛, 保持学习,谨记谦虚。不端不装,有趣有梦。
参考文章和论文,不仅限于如下几篇,感谢国外大佬们有深度的分享: http://stackoverflow.com/questions/29055477/oauth-authorization-service-in-asp-net-core https://stormpath.com/blog/token-authentication-asp-net-core https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware#fundamentals-middleware https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie#controlling-cookie-options https://stormpath.com/blog/token-authentication-asp-net-core 相关文章:
AspNet Identity 和 Owin 谁是谁

ASP.NET Core 之 Identity 入门(一)

ASP.NET Core 之 Identity 入门(二)

ASP.NET Core 之 Identity 入门(三)

原文链接:http://www.cnblogs.com/tdws/p/6536864.html
.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: