代码之家  ›  专栏  ›  技术社区  ›  pseabury

ASP。NET Core 2.0无身份承载身份验证

  •  34
  • pseabury  · 技术社区  · 7 年前

    当我一天前开始在上实现一个自包含的bearer auth webapi时,我认为我有一个非常简单的目标。NET核心2.0,但我还没有任何远程工作。下面列出了我要做的事情:

    • 实现受承载令牌保护的webapi
    • 使用[授权]属性控制对api表面的访问
    • 不使用ASP。Net Identity(我的用户/会员需求要轻得多)

    我觉得这可能是一个常见的场景,不应该这么难(也许不是,也许只是缺少文档/示例?)。据我所知,IdentityServer4与Core 2.0 Auth不兼容,opendiddict似乎需要标识。我也不想在单独的进程中托管令牌端点,而是在同一个webapi实例中。

    2 回复  |  直到 7 年前
        1
  •  26
  •   Mitch    7 年前

    进行了编辑,使其与ASP兼容。NET Core 2.0。


    首先,一些Nuget软件包:

    • 微软AspNetCore。身份
    • 系统标识模型。代币。Jwt公司
    • 系统安全密码学。顾客服务提供商

    // Presumably you will have an equivalent user account class with a user name.
    public class User
    {
        public string UserName { get; set; }
    }
    
    public class JsonWebToken
    {
        public string access_token { get; set; }
    
        public string token_type { get; set; } = "bearer";
    
        public int expires_in { get; set; }
    
        public string refresh_token { get; set; }
    }
    

    进入正确的功能后,您将需要一个登录/令牌web方法来实际将授权令牌发送给用户。

    [Route("api/token")]
    public class TokenController : Controller
    {
        private ITokenProvider _tokenProvider;
    
        public TokenController(ITokenProvider tokenProvider) // We'll create this later, don't worry.
        {
            _tokenProvider = tokenProvider;
        }
    
        public JsonWebToken Get([FromQuery] string grant_type, [FromQuery] string username, [FromQuery] string password, [FromQuery] string refresh_token)
        {
            // Authenticate depending on the grant type.
            User user = grant_type == "refresh_token" ? GetUserByToken(refresh_token) : GetUserByCredentials(username, password);
    
            if (user == null)
                throw new UnauthorizedAccessException("No!");
    
            int ageInMinutes = 20;  // However long you want...
    
            DateTime expiry = DateTime.UtcNow.AddMinutes(ageInMinutes);
    
            var token = new JsonWebToken {
                access_token = _tokenProvider.CreateToken(user, expiry),
                expires_in   = ageInMinutes * 60
            };
    
            if (grant_type != "refresh_token")
                token.refresh_token = GenerateRefreshToken(user);
    
            return token;
        }
    
        private User GetUserByToken(string refreshToken)
        {
            // TODO: Check token against your database.
            if (refreshToken == "test")
                return new User { UserName = "test" };
    
            return null;
        }
    
        private User GetUserByCredentials(string username, string password)
        {
            // TODO: Check username/password against your database.
            if (username == password)
                return new User { UserName = username };
    
            return null;
        }
    
        private string GenerateRefreshToken(User user)
        {
            // TODO: Create and persist a refresh token.
            return "test";
        }
    }
    

    您可能注意到令牌创建仍然只是一些虚构的ITokenProvider传递的“魔法”。定义令牌提供程序接口。

    public interface ITokenProvider
    {
        string CreateToken(User user, DateTime expiry);
    
        // TokenValidationParameters is from Microsoft.IdentityModel.Tokens
        TokenValidationParameters GetValidationParameters();
    }
    

    我在JWT上使用RSA安全密钥实现了令牌创建。所以

    public class RsaJwtTokenProvider : ITokenProvider
    {
        private RsaSecurityKey _key;
        private string _algorithm;
        private string _issuer;
        private string _audience;
    
        public RsaJwtTokenProvider(string issuer, string audience, string keyName)
        {
            var parameters = new CspParameters { KeyContainerName = keyName };
            var provider = new RSACryptoServiceProvider(2048, parameters);
    
            _key = new RsaSecurityKey(provider);
    
            _algorithm = SecurityAlgorithms.RsaSha256Signature;
            _issuer = issuer;
            _audience = audience;
        }
    
        public string CreateToken(User user, DateTime expiry)
        {
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
    
            ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.UserName, "jwt"));
    
            // TODO: Add whatever claims the user may have...
    
            SecurityToken token = tokenHandler.CreateJwtSecurityToken(new SecurityTokenDescriptor
            {
                Audience = _audience,
                Issuer = _issuer,
                SigningCredentials = new SigningCredentials(_key, _algorithm),
                Expires = expiry.ToUniversalTime(),
                Subject = identity
            });
    
            return tokenHandler.WriteToken(token);
        }
    
        public TokenValidationParameters GetValidationParameters()
        {
            return new TokenValidationParameters
            {
                IssuerSigningKey = _key,
                ValidAudience = _audience,
                ValidIssuer = _issuer,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromSeconds(0) // Identity and resource servers are the same.
            };
        }
    }
    

    所以你现在正在生成代币。是时候实际验证它们并将其连接起来了。进入创业阶段。反恐精英。

    在里面 ConfigureServices()

    var tokenProvider = new RsaJwtTokenProvider("issuer", "audience", "mykeyname");
    services.AddSingleton<ITokenProvider>(tokenProvider);
    
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options => {
            options.RequireHttpsMetadata = false;
            options.TokenValidationParameters = tokenProvider.GetValidationParameters();
        });
    
    // This is for the [Authorize] attributes.
    services.AddAuthorization(auth => {
        auth.DefaultPolicy = new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
            .RequireAuthenticatedUser()
            .Build();
    });
    

    Configure()

    public void Configure(IApplicationBuilder app)
    {
        app.UseAuthentication();
    
        // Whatever else you're putting in here...
    
        app.UseMvc();
    }
    

    令人高兴的结果是。。。

    [Authorize] // Yay!
    [Route("api/values")]
    public class ValuesController : Controller
    {
        // ...
    }
    
        2
  •  14
  •   Sethles    7 年前

    using System.Text;
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.IdentityModel.Tokens;
    
    namespace JwtWithoutIdentity
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(cfg =>
                    {
                        cfg.RequireHttpsMetadata = false;
                        cfg.SaveToken = true;
    
                        cfg.TokenValidationParameters = new TokenValidationParameters()
                        {
                            ValidIssuer = "me",
                            ValidAudience = "you",
                            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("rlyaKithdrYVl6Z80ODU350md")) //Secret
                        };
    
                    });
    
                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)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseAuthentication();
    
                app.UseMvc();
            }
        }
    }
    

    令牌控制器

    using System;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using System.Text;
    using System.Threading.Tasks;
    using JwtWithoutIdentity.Models;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.IdentityModel.Tokens;
    
    namespace JwtWithoutIdentity.Controllers
    {
        public class TokenController : Controller
        {
    
            [AllowAnonymous]
            [Route("api/token")]
            [HttpPost]
            public async Task<IActionResult> Token(LoginViewModel model)
            {
    
                if (!ModelState.IsValid) return BadRequest("Token failed to generate");
    
                var user = (model.Password == "password" && model.Username == "username");
    
                if (!user) return Unauthorized();
    
                //Add Claims
                var claims = new[]
                {
                    new Claim(JwtRegisteredClaimNames.UniqueName, "data"),
                    new Claim(JwtRegisteredClaimNames.Sub, "data"),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                };
    
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("rlyaKithdrYVl6Z80ODU350md")); //Secret
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
                var token = new JwtSecurityToken("me",
                    "you",
                    claims,
                    expires: DateTime.Now.AddMinutes(30),
                    signingCredentials: creds);
    
                return Ok(new JsonWebToken()
                {
                    access_token = new JwtSecurityTokenHandler().WriteToken(token),
                    expires_in = 600000,
                    token_type = "bearer"
                });
            }
        }
    }
    

    值控制器

    using System.Collections.Generic;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    
    namespace JwtWithoutIdentity.Controllers
    {
        [Route("api/[controller]")]
        public class ValuesController : Controller
        {
            // GET api/values
            [Authorize]
            [HttpGet]
            public IEnumerable<string> Get()
            {
                var name = User.Identity.Name;
                var claims = User.Claims;
    
                return new string[] { "value1", "value2" };
            }
        }
    }