我正在使用asp.net webapi练习,并且想要分开授权服务。
所以我实现了基于令牌(owin)和数据提供者服务的授权服务。现在我想覆盖数据提供程序服务中的Authorize属性。它必须从当前请求中获取承载令牌,向授权服务发出请求,接收有关用户及其角色的信息。
问题是:如何在我的自定义属性中获取持有人令牌,并且可能有更好的方法来进行“令牌转移”?
我想这样用:
//data service
[CustomAttribute (Roles = "admin")]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
public class CustomAttribute : System.Web.Mvc.AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext context)
{
using (WebClient client = new WebClient())
{
string bearerToken;
//somehow get token
client.Headers.Add("Authorization", "Bearer " + bearerToken);
string userinfo = client.DownloadString("authURL/GetUserInfo");
CustomUser user = JsonConvert.DeserializeObject<CustomUser>(userinfo);
if (!user.Roles == this.Roles)
{
//return 401
}
}
}
}
// authorization service
public async Task<UserInfoResponse> GetUserInfo()
{
var owinContext = HttpContext.Current.GetOwinContext();
int userId = owinContext.Authentication.User.Identity.GetUserId<int>();
var response = new UserInfoResponse()
{
UserId = userId.ToString(),
Roles = await UserManager.GetRolesAsync(userId)
};
return response;
}
答案 0 :(得分:1)
要回答有关如何访问请求的Authorization标头中的承载令牌的特定问题:
public class CustomAttribute : System.Web.Mvc.AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext context)
{
System.Net.Http.Headers.AuthenticationHeaderValue authorizationHeader = context.HttpContext.Request.Headers.Authorization;
// Check that the Authorization header is present in the HTTP request and that it is in the
// format of "Authorization: Bearer <token>"
if ((authorizationHeader == null) || (authorizationHeader.Scheme.CompareTo("Bearer") != 0) || (String.IsNullOrEmpty(authorizationHeader.Parameter)))
{
// return HTTP 401 Unauthorized
}
using (WebClient client = new WebClient())
{
client.Headers.Add("Authorization", "Bearer " + authorizationHeader.Parameter);
string userinfo = client.DownloadString("authURL/GetUserInfo");
CustomUser user = JsonConvert.DeserializeObject<CustomUser>(userinfo);
if (!user.Roles == this.Roles)
{
// I recommend return HTTP 403 Forbidden here, not 401. At this point
// the request has been authenticated via the bearer token, but the
// authenticated client does not have sufficient roles to execute the
// request, so they are forbidden from doing so. HTTP 401 Unauthorized
// is a bit of a misnomer because the actual intention is to determine
// whether or not the request is authenticated. HTTP 401 also implies
// that the request should be tried again with credentials, but that
// has already been done!
}
}
}
}
可能会有更好的方法来做您想做的事情,但是我对MVC方面以及您的应用程序的身份验证/授权工作流程了解不足,无法为您提供一个很好的答案。至少这应该有助于知道在授权属性中的哪里可以找到标头值。
答案 1 :(得分:0)
布莱尔·艾伦很伤心,但有更好的方法来做我想做的事。使用 IdentityServer4 生成令牌,只需检查令牌签名,无需任何额外请求。我切换到 net core,这是 mvc 客户端的解决方案:接收令牌并将其保存在 cookie 中。
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
var tokenResult = await AuthService.LoginUserAsync(model.Email, model.Password);
if(!tokenResult.IsSuccess)
{
ModelState.AddModelError("","Wrong email or password");
return View(model);
}
Response.Cookies.Append("access_token", tokenResult.AccessToken, new CookieOptions(){
HttpOnly = true,
SameSite = SameSiteMode.Strict,
Secure = true
});
return RedirectToAction("Index", "Home");
}
然后使用
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, config =>
{
config.Authority = configuration["TokenServerUrl"];
config.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var token = context.HttpContext.Request.Cookies["access_token"];
context.Token = token;
return Task.CompletedTask;
},
};
config.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = configuration["TokenServerUrl"],
ValidateLifetime = true,
};
});