ASP.NET Core 2.0不带身份的承载身份验证
当我一天前开始在.NETCore2.0上实现一个自包含的bearer auth webapi时,我想我有一个非常简单的目标,但我还没有实现任何远程工作。下面是我正在尝试做的事情的列表:ASP.NET Core 2.0不带身份的承载身份验证,asp.net,asp.net-core,bearer-token,Asp.net,Asp.net Core,Bearer Token,当我一天前开始在.NETCore2.0上实现一个自包含的bearer auth webapi时,我想我有一个非常简单的目标,但我还没有实现任何远程工作。下面是我正在尝试做的事情的列表: 实现受承载令牌保护的webapi 发出令牌&从同一项目中的端点刷新令牌 使用[Authorize]属性控制对api表面的访问 不使用ASP.Net身份(我有更轻的用户/会员要求) 我完全可以在登录中构建identity/claims/principal并将其添加到请求上下文中,但我还没有看到一个关于如何在没有
- 实现受承载令牌保护的webapi
- 发出令牌&从同一项目中的端点刷新令牌
- 使用[Authorize]属性控制对api表面的访问
- 不使用ASP.Net身份(我有更轻的用户/会员要求)
有谁能给我举个具体的例子,或者至少给我一些关于什么是最佳步骤/选项的指导 进行了编辑,使其与ASP.NET Core 2.0兼容
首先,一些Nuget软件包:
- Microsoft.AspNetCore.Authentication.JwtBearer
- Microsoft.AspNetCore.Identity
- System.IdentityModel.Tokens.Jwt
- System.Security.Cryptography.Csp
// 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.
};
}
}
所以你现在正在生成代币。是时候实际验证它们并将其连接起来了。转到Startup.cs
在ConfigureServices()中
这应该就是你所需要的了。希望我没有错过任何东西
令人高兴的结果是
[Authorize] // Yay!
[Route("api/values")]
public class ValuesController : Controller
{
// ...
}
在@Mitch answer之后:Auth stack在迁移到.NETCore2.0时发生了很大的变化。下面的答案只是使用新的实现
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" };
}
}
}
使用系统;
使用System.IdentityModel.Tokens.Jwt;
使用System.Security.Claims;
使用系统文本;
使用System.Threading.Tasks;
使用JwtWithoutIdentity.Models;
使用Microsoft.AspNetCore.Authorization;
使用Microsoft.AspNetCore.Mvc;
使用Microsoft.IdentityModel.Tokens;
命名空间JwtWithoutIdentity.Controllers
{
公共类令牌控制器:控制器
{
[异名]
[路由(“api/令牌”)]
[HttpPost]
公共异步任务令牌(LoginViewModel模型)
{
如果(!ModelState.IsValid)返回BadRequest(“生成令牌失败”);
var user=(model.Password==“Password”和&model.Username==“Username”);
如果(!user)返回Unauthorized();
//添加索赔
风险值索赔=新[]
{
新索赔(JwtRegisteredClaimNames.UniqueName,“数据”),
新索赔(JwtRegisteredClaimNames.Sub,“数据”),
新声明(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
};
var key=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(“rlyaKithdrYVl6Z80ODU350md”);//Secret
var creds=新的签名凭证(key,SecurityAlgorithms.HmacSha256);
var token=新的JwtSecurityToken(“我”,
“你”,
声称,
过期:DateTime.Now.AddMinutes(30),
签署证书:信誉);
返回Ok(新的JsonWebToken()
{
access_token=new JwtSecurityTokenHandler().WriteToken(令牌),
expires_in=600000,
令牌类型=“承载人”
});
}
}
}
值控制器
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" };
}
}
}
使用System.Collections.Generic;
使用Microsoft.AspNetCore.Authorization;
使用Microsoft.AspNetCore.Mvc;
命名空间JwtWithoutIdentity.Controllers
{
[路由(“api/[控制器]”)]
公共类值控制器:控制器
{
//获取api/值
[授权]
[HttpGet]
公共IEnumerable Get()
{
var name=User.Identity.name;
var索赔=User.claims;
返回新字符串[]{“value1”,“value2”};
}
}
}
希望这有帮助 我也很想看看这个例子。它与JWT机制是解耦的。读和读。您好。我甚至没有真正想过在所有关于新Core2 auth stack等的讨论中使用我自己的——我以为这将是现成的。在任何情况下,您的解决方案中唯一缺少的就是刷新令牌,但鉴于上述情况,这是微不足道的。一个问题-这些安全令牌是不透明的还是透明的?(即,当提供令牌时,身份验证堆栈是否会取消保护并将标识附加到webapi上下文,或者这是一个附加步骤?)谢谢Mitch!它解密令牌并为您设置上下文标识。在您的控制器中,User.Identity.Name
将是传递到JWT中的用户名。是的,我还没有抽出时间刷新令牌-它的操作与JWT生成代码完全不同。通过一些随机散列生成令牌,存储它,并在刷新调用期间检查它。这段代码是为一个快速API编写的,我在.NET核心测试阶段不得不重新编写。如果有人在更新的功能上有一个更简单的实现,那就太好了