asp.net core 2.0 web api基于JWT自定义策略授权

源码如下:

JwtToken.cs

    /// <summary>
    /// 获取基于JWT的Token
    /// </summary>
    /// <param name="username"></param>
    /// <returns></returns>
    public static dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement)
    {
        var now = DateTime.UtcNow;
        var jwt = new JwtSecurityToken(
            issuer: permissionRequirement.Issuer,
            audience: permissionRequirement.Audience,
            claims: claims,
            notBefore: now,
            expires: now.Add(permissionRequirement.Expiration),
            signingCredentials: permissionRequirement.SigningCredentials
        );
        var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
        var response = new
        {
            Status = true,
            access_token = encodedJwt,
            expires_in = permissionRequirement.Expiration.TotalMilliseconds,
            token_type = "Bearer"
        };
        return response;
    }

Permission.cs

/// <summary>
/// 用户或角色或其他凭据实体
/// </summary>
public class Permission
{
    /// <summary>
    /// 用户或角色或其他凭据名称
    /// </summary>
    public virtual string Name
    { get; set; }
    /// <summary>
    /// 请求Url
    /// </summary>
    public virtual string Url
    { get; set; }
}

PermissionRequirement.cs

/// <summary>
/// 必要参数类
/// </summary>
public class PermissionRequirement : IAuthorizationRequirement
{
    /// <summary>
    /// 用户权限集合
    /// </summary>
    public List<Permission> Permissions { get; private set; }
    /// <summary>
    /// 无权限action
    /// </summary>
    public string DeniedAction { get; set; }
    /// <summary>
    /// 认证授权类型
    /// </summary>
    public string ClaimType { internal get; set; }
    /// <summary>
    /// 请求路径
    /// </summary>
    public string LoginPath { get; set; } = "/Api/Login";
    /// <summary>
    /// 发行人
    /// </summary>
    public string Issuer { get; set; }
    /// <summary>
    /// 订阅人
    /// </summary>
    public string Audience { get; set; }
    /// <summary>
    /// 过期时间
    /// </summary>
    public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(5000);
    /// <summary>
    /// 签名验证
    /// </summary>
    public SigningCredentials SigningCredentials { get; set; }
    /// <summary>
    /// 构造
    /// </summary>
    /// <param name="deniedAction">无权限action</param>
    /// <param name="userPermissions">用户权限集合</param>
    /// <summary>
    /// 构造
    /// </summary>
    /// <param name="deniedAction">拒约请求的url</param>
    /// <param name="permissions">权限集合</param>
    /// <param name="claimType">声明类型</param>
    /// <param name="issuer">发行人</param>
    /// <param name="audience">订阅人</param>
    /// <param name="signingCredentials">签名验证实体</param>
    public PermissionRequirement(string deniedAction, List<Permission> permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials)
    {
        ClaimType = claimType;
        DeniedAction = deniedAction;
        Permissions = permissions;
        Issuer = issuer;
        Audience = audience;
        SigningCredentials = signingCredentials;
    }
}

自定义策略类PermissionHandler.cs

/// <summary>
/// 权限授权Handler
/// </summary>
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{    
    /// <summary>
    /// 验证方案提供对象
    /// </summary>
    public IAuthenticationSchemeProvider Schemes { get; set; }
    /// <summary>
    /// 自定义策略参数
    /// </summary>
    public PermissionRequirement Requirement
    { get; set; }
    /// <summary>
    /// 构造
    /// </summary>
    /// <param name="schemes"></param>
    public PermissionHandler(IAuthenticationSchemeProvider schemes)
    {
        Schemes = schemes;
    }  
    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
    {
        赋值用户权限       
        Requirement = requirement;
        //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息
        var httpContext = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext;
        //请求Url
        var questUrl = httpContext.Request.Path.Value.ToLower();  
        //判断请求是否停止
        var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
        foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
        {
            var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name) as IAuthenticationRequestHandler;
            if (handler != null && await handler.HandleRequestAsync())
            {
                context.Fail();
                return;
            }
        }
        //判断请求是否拥有凭据,即有没有登录
        var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
        if (defaultAuthenticate != null)
        {
            var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
            //result?.Principal不为空即登录成功
            if (result?.Principal != null)
            {
                httpContext.User = result.Principal;
                //权限中是否存在请求的url
                if (Requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)
                {
                    var name = httpContext.User.Claims.SingleOrDefault(s => s.Type == requirement.ClaimType).Value;
                    //验证权限
                    if (Requirement.Permissions.Where(w => w.Name == name && w.Url.ToLower() == questUrl).Count() <= 0)
                    {
                        //无权限跳转到拒绝页面
                        httpContext.Response.Redirect(requirement.DeniedAction);
                    }
                }
                context.Succeed(requirement);
                return;
            }
        }
        //判断没有登录时,是否访问登录的url,并且是Post请求,并助是form表单提交类型,否则为失败
        if (!questUrl.Equals(Requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST")
           || !httpContext.Request.HasFormContentType))
        {
            context.Fail();
            return;
        }
        context.Succeed(requirement);     
    }
}

先设置配置文件,用户可以定义密匙和发生人,订阅人

“Audience”: {

"Secret": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",

"Issuer": "gsw",

"Audience": "everone"

}

在ConfigureServices中注入验证(Authentication),授权(Authorization),和JWT(JwtBearer)

Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        //读取配置文件
        var audienceConfig = Configuration.GetSection("Audience");
        var symmetricKeyAsBase64 = audienceConfig["Secret"];
        var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
        var signingKey = new SymmetricSecurityKey(keyByteArray);
        var tokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = signingKey,
            ValidateIssuer = true,
            ValidIssuer = audienceConfig["Issuer"],
            ValidateAudience = true,
            ValidAudience = audienceConfig["Audience"],
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };
        var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
        services.AddAuthorization(options =>
        {
            //这个集合模拟用户权限表,可从数据库中查询出来
            var permission = new List<Permission> {
                          new Permission {  Url="/", Name="admin"},
                          new Permission {  Url="/api/values", Name="admin"},
                          new Permission {  Url="/", Name="system"},
                          new Permission {  Url="/api/values1", Name="system"}
                      };
            //如果第三个参数,是ClaimTypes.Role,上面集合的每个元素的Name为角色名称,如果ClaimTypes.Name,即上面集合的每个元素的Name为用户名
            var permissionRequirement = new PermissionRequirement("/api/denied", permission, ClaimTypes.Role, audienceConfig["Issuer"], audienceConfig["Audience"], signingCredentials);
            options.AddPolicy("Permission",
                      policy => policy.Requirements.Add(permissionRequirement));
        }).AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(o =>
        {
            //不使用https
            o.RequireHttpsMetadata = false;
            o.TokenValidationParameters = tokenValidationParameters;
        });
        //注入授权Handler
        services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
        services.AddMvc();
    }

在需要授的Controller上添加授权特性

[Authorize("Permission")]

PermissionController类有两个方法,一个是登录,验证用户名和密码是否正确,如果正确就发放Token,如果失败,验证失败,别一个成功登后的无权限导航action。

[Authorize("Permission")]
public class PermissionController : Controller
{
    /// <summary>
    /// 自定义策略参数
    /// </summary>
    PermissionRequirement _requirement;
    public PermissionController(IAuthorizationHandler authorizationHander)
    {
        _requirement = (authorizationHander as PermissionHandler).Requirement;
    }
    [AllowAnonymous]
    [HttpPost("/api/login")]
    public IActionResult Login(string username,string password,string role)
    { 
        var isValidated = username == "gsw" && password == "111111";
        if (!isValidated)
        {
            return new JsonResult(new
            {
                Status = false,
                Message = "认证失败"
            });
        }
        else
        { 
            //如果是基于角色的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
            var claims =new Claim[]{ new Claim(ClaimTypes.Name, username),new Claim(ClaimTypes.Role, role) };
            //用户标识
            var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme); 
            identity.AddClaims(claims);
            //登录
            HttpContext.SignInAsync(JwtBearerDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
            var token = JwtToken.BuildJwtToken(claims, _requirement);
            return new JsonResult(token);
        }
    }
    [AllowAnonymous]
    [HttpGet("/api/denied")]
    public IActionResult Denied()
    {
        return new JsonResult(new
        {
            Status = false,
            Message = "你无权限访问"
        });
    }
}

下面定义一个控制台(.NetFramewrok)程序,用RestSharp来访问我们定义的web api,其中1为admin角色登录,2为system角色登录,3为错误用户密码登录,4是一个查询功能,在startup.cs中,admin角色是具有查询/api/values的权限的,所以用admin登录是能正常访问的,用system登录,能成功登录,但没有权限访问/api/values,用户名密码错误,访问/aip/values,直接是没有授权的

class Program
{
///
/// 访问Url
///
static string _url = “http://localhost:39286”;
static void Main(string[] args)
{
dynamic token = null;
while (true)
{
Console.WriteLine("1、登录【admin】 2、登录【system】 3、登录【错误用户名密码】 4、查询数据 ");
var mark = Console.ReadLine();
var stopwatch = new Stopwatch();
stopwatch.Start();
switch (mark)
{
case “1”:
token = AdminLogin();
break;
case “2”:
token = SystemLogin();
break;
case “3”:
token = NullLogin();
break;
case “4”:
AdminInvock(token);
break;
}
stopwatch.Stop();
TimeSpan timespan = stopwatch.Elapsed;
Console.WriteLine(KaTeX parse error: Expected 'EOF', got '}' at position 46: …); }̲ } …“状态:{response.StatusCode} 返回结果:{content}”);
}
}
东莞网站建设www.zg886.cn

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值