我有一个使用承载令牌进行身份验证和授权的Web API。令牌的存在表示用户已通过身份验证;该令牌中的声明指定了用户拥有的授权。
我编写了一个OAuth类,用于在用户登录时查询数据库以确定用户的权限。所有这一切都很好。
现在,对于每个控制器中的每个方法,我需要从持有者令牌的声明中提取信息。我现在所做的是定义一个包含令牌所需实体的类,并编写一个静态方法从控制器方法中获取User
对象,并生成包含令牌数据的类实例:
// Bearer token class
class TokenData
{
public string UserId { get; set; }
public int GroupId { get; set; }
public string UserTag { get; set; }
// ... more properties as needed
}
// Method to get bearer token data from user object
internal static TokenData getTokenDataFromUserPrincipal(System.Security.Principal.IPrincipal p)
{
var identity = (ClaimsIdentity)p.Identity;
IEnumerable<Claim> claims = identity.Claims;
var enumerable = claims as Claim[] ?? claims.ToArray();
var claimTypes = from x in enumerable select x.Type;
var types = claimTypes as string[] ?? claimTypes.ToArray();
if (!types.Contains("userId")) return null; // if a token was given but it has no UserID it's not valid.
var claimsByType = enumerable.ToDictionary(x => x.Type, x => x.Value);
TokenData td = new TokenData
{
UserId = claimsByType["userId"],
GroupId = types.Contains("groupId") ? int.Parse(claimsByType["groupId"]) : 0,
UserTag = types.Contains("userTag") ? claimsByType["userTag"]) : null,
// more properies as needed
};
return td;
}
// A controller method that uses the claims
public HttpResponseMessage DoSomething()
{
TokenData td = getTokenDataFromUserPrincipal(User);
// Now I can access the token data in the td variable.
return Request.CreateResponse(td.UserId);
}
好的,所以这一切都很完美。我正在寻找的东西是,如果有一种方法可以自动化在调用控制器方法时从User对象中拉出声明。
Web API架构基本上已经使用User
对象本身执行此操作 - 它包含作为与用户相关的请求的一部分的任何信息。 (类似于Request
对象,其中包含所有HTTP请求数据。)
我希望能够做的是,而不是在每个控制器的开头调用getTokenDataFromUserPrincipal
,而是只有一个类似于Web API的静态变量 - 例如TokenData
。例如:
// A controller method that uses the claims and doesn't have to explicitly retrieve them.
public HttpResponseMessage DoSomething()
{
return Request.CreateResponse(TokenData.UserId);
}
我确实知道如何使用属性 - 我实际上编写了自定义属性,它们提取相同的令牌数据并使用它来确定用户是否可以访问某类功能 - 例如我可以只包含[MinimumUserLevel(2)]
在我的控制器方法面前。但是必须为每个方法添加一个属性只会将问题移出方法之外。
总结:是否可以在请求级别设置另一个静态变量,用户代码可以按请求填充,而无需将代码复制到每个控制器方法的开头?
理想情况下,有一种方法可以在管道中插入一个函数,这样在运行控制器方法之前,我可以运行代码从主体获取令牌数据,因此它将在控制器准备就绪方法运行。 (请注意,如果数据不存在,则拉取令牌数据的方法只返回null - 这是在没有令牌的调用实例中此静态变量的预期行为。)
答案 0 :(得分:0)
您可以手动验证jwt令牌。或者将JwtBearerAuthentication添加到Owin堆栈。
Here是找到的样本之一。这将解析每个请求的令牌并设置每个请求的用户属性。
您必须为创建jwt令牌时使用的秘密进行设置。
提供的示例验证JWT令牌并将其转换为在每个请求上设置的ClaimsIdentity,并且可以通过每个控制器中的public static class IIdentityExtensions
{
public static string UserId(this IIdentity identity)
{
return identity.GetClaimValue("userId");
}
public static string GetClaimValue(this IIdentity identity, string claimType)
{
return identity
.AsClaimsIdentity()
.Claims.FirstOrDefault(c => c.Type == claimType)?.Value;
}
private static ClaimsIdentity AsClaimsIdentity(this IIdentity identity)
{
if (!(identity?.IsAuthenticated ?? false))
throw new UnauthorizedAccessException("User not logged-in");
return identity as ClaimsIdentity;
}
}
属性访问。不需要自定义代码。
由于这个软件包是由Microsoft提供的,它可能比一些自制的JWT解析器更好。
User.Identity.UserId()
访问您的用户ID。
您还可以使用以下代码扩展IIdentity。
{{1}}
然后访问用户ID,例如{{1}}
答案 1 :(得分:0)
解决这个问题的方法比我做的要简单:定义ApiController
的子类。
所以我只是写了一个看起来像这样的类:
public class MyApiController : ApiController
{
// Makes token data available to any method on a class deriving from MyApiController
public TokenData Token { get; set; }
// Override constructor - parse token whenever instance is created
public MyApiController()
{
// Extract the token data from the User variable (User is pulled in from the base ApiController)
Token = getTokenDataFromUserPrincipal(User);
}
private static TokenData getTokenDataFromUserPrincipal(System.Security.Principal.IPrincipal p)
{
...
}
}
现在,我所有的控制器都从MyApiController
派生,而不是从ApiController
派生。从这一点开始,我可以在任何控制器的逻辑中访问Token
变量,该变量将包含已解析的令牌数据。