有许多资源描述了使用.Net Web API在C#中实现JWT创建和使用。然而,我正在努力使我当前的系统适应列出的任何教程。
到目前为止,我一直在发送我的JWT令牌作为查询字符串的一部分,如下所示:
/api/用户?jwt=
我的路线看起来像这样:
public async Task<IHttpActionResult> GetUsers(string jwt)
{
var userValidationResult = await Utility.ValidateUser(jwt);
if (!userValidationResult.Validated)
{
return Unauthorized();
}
...
}
您可以在下面查看该验证用户方法的内容。请注意,我实际上是通过将JWT发送到另一个服务器进行身份验证来验证它的。
public class UserValidationResult
{
public bool Validated { get; set; }
public string Email { get; set; }
public List<string> Roles { get; set; }
}
public static Task<UserValidationResult> ValidateUser(string jwt)
{
var taskCompletionSource = new TaskCompletionSource<UserValidationResult>();
var authenticationRoute = "http://authserver:5000/authenticate?jwt=" +jwt;
var authenticationClient = new RestClient(authenticationRoute);
var authenticationRequest = new RestRequest(Method.GET);
authenticationClient.ExecuteAsync(authenticationRequest, authenticationResponse =>
{
var authenticationResponseInJson = JObject.Parse(authenticationResponse.Content.ToString());
var payload = JObject.Parse(authenticationResponseInJson["payload"].ToString());
taskCompletionSource.SetResult(new UserValidationResult()
{
Validated = bool.Parse(authenticationResponseInJson["success"].ToString()),
Email = ...
Roles = ...
});
});
return taskCompletionSource.Task;
}
这很管用。然而,我更喜欢使用您在所有OWIN/Katana教程中看到的漂亮的[Authorize]
属性。有没有一种方法可以让Owin中间件像现在一样,根据我的Validate方法验证jwt令牌,除了使用Authorize属性?
编辑
因此,我实现了以下中间件,并在我的启动类中使用它:
public class AuthorizationMiddleware : OwinMiddleware
{
private OwinMiddleware _next;
public AuthorizationMiddleware(OwinMiddleware next):base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
var jwt = context.Request.Query.Get("jwt");
if (jwt != null)
{
var userValidationResult = await Utility.ValidateUser(jwt);
if (userValidationResult.Validated)
{
var jwtoken = new JwtSecurityToken(jwt);
var identity = new ClaimsIdentity("jwt");
//foreach (var role in userValidationResult.Roles)
// identity.AddClaim(new Claim(ClaimTypes.Role, role));
identity.AddClaim(new Claim(ClaimTypes.Email, userValidationResult.Email));
context.Request.User = new ClaimsPrincipal(identity);
}
}
await _next.Invoke(context);
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use<AuthorizationMiddleware>();
var config = new HttpConfiguration();
app.UseWebApi(config);
}
}
但是,我用Authorize
属性修饰的路由似乎都没有碰到invoke方法中的任何断点。我总是得到一个未经授权的401。为什么会这样?
您可以在OWIN管道之上实现一个基本中间件。简单的东西可以满足你的要求:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use(async (ctx, next) =>
{
var jwt = ctx.Request.Query.Get("jwt");
if (jwt != null)
{
var userValidationResult = await Utility.ValidateUser(jwt);
if (userValidationResult.Validated)
{
var identity = new ClaimsIdentity("jwt");
foreach(var role in userValidationResult.Roles)
identity.AddClaim(new Claim(ClaimTypes.Role, role));
identity.AddClaim(new Claim(ClaimTypes.Email, userValidationResult.Email));
//etc... add every claim
ctx.Request.User = new ClaimsPrincipal(identity);
}
}
await next.Invoke();
});
var config = new HttpConfiguration();
app.UseWebApi(config);
//etc...
}
}
这只是一个存根,您可能需要扩展代码以使其在您的特定环境中工作。
像这样的中间件将设置Request.User
,然后由其他中间件使用(包括Web API Authorize过滤器)。然后,您可以使用[Authorize()]
属性自由地装饰控制器/操作。
如果您需要更灵活的东西,那么我建议您阅读更多关于如何实现身份验证中间件的内容。
Brock Allen的这个资源也可以帮助您实现这样一个实现:OWIN身份验证中间件体系结构。