认证和授权一般是同时出现的,先做认证,认证的时候进行权限获取,进入接口之前根据接口的权限要求进行授权校验
假设有个接口,他需要的访问权限是用户年龄必须为18岁,采用jwt进行认证
接口访问流程:用户先获取到JWT的token—>请求接口带上token—>进行token校验并将用户的年龄作为Claim设置到ClaimsPrincipal中---->进入接口权限校验
- 先启用认证和授权,该配置一定要在app.UseEndpoints之前,认证最好也是放在授权之前,否则会先进行授权逻辑再进行验证逻辑
// 开启验证
app.UseAuthentication();
//启用授权
app.UseAuthorization();
- 配置认证模块
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.SaveToken = true;
o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
};
o.SecurityTokenValidators.Clear();
o.SecurityTokenValidators.Add(new TokenValidtor());
});
其中TokenValidtor为自定义的验证类,主要进行token的验证,并获取用户权限
public class TokenValidtor : ISecurityTokenValidator
{
public bool CanValidateToken => throw new NotImplementedException();
public int MaximumTokenSizeInBytes { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public bool CanReadToken(string securityToken)
{
if (!string.IsNullOrEmpty(securityToken))
{
return true;
}
return false;
}
ClaimsPrincipal ISecurityTokenValidator.ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
validatedToken = null;
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
identity.AddClaim(new Claim("Age", "17"));
var principal = new ClaimsPrincipal(identity);
return principal;
}
}
- 配置授权模块
services.AddAuthorization(options =>
{
options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =>
{
policy.Requirements.Add(new PermissionRequirement { Age = 18 });
});
});
其中PermissionRequirement 为自定义的权限设置类,再自定义PermissionHandler,继承AuthorizationHandler ,并重写HandleRequirementAsync方法,对用户权限和接口权限进行匹配校验
public class PermissionRequirement : IAuthorizationRequirement
{
///
/// 年龄
///
public int Age{ get; set; }
}
public class PermissionHandler : AuthorizationHandler
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var age = context.User.Claims.First(p => p.Type.Equals("Age")).Value;
if (age.Equals(requirement.Age.ToString()))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
- 注册IAuthorizationHandler对应的实现类,可以使用自己的IOC框架也可以使用.net5自带的DI注册
services.AddTransient();
- 服务端 在需要进行校验的接口方法上增加Authorize属性,该属性的参数对应授权模块注册时候AddPolicy中某一个的名称
[Authorize(JwtBearerDefaults.AuthenticationScheme)] public override TaskGetUserStatus(GetUserStatusInput request, ServerCallContext context)
- 客户端在调用接口的时候在header增加Authorization设置
var channel = GrpcChannel.ForAddress("http://localhost:5000");
var client = new User.UserService.UserServiceClient(channel);
var loginResult = client.Login(new User.LoginInput { Name = "huang", Password = "123456" });
var headers = new Metadata { { "Authorization", $"Bearer {loginResult.Token}" } };
var userStatus = client.GetUserStatus(new User.GetUserStatusInput { Token = loginResult.Token }, headers);



