ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

GraphQL:验证与授权

2022-02-01 16:34:03  阅读:191  来源: 互联网

标签:验证 System new Microsoft GraphQL using var 授权 public


  GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

                                  ——出自 https://graphql.cn

  由于HotChocklate是是基于asp.net core框架,所以授权策略与原生的asp.net core mvc项目大同小异,都是通过固定角色,自定义策略等方式来进行的。下面的例子就是通过一个自定义策略的例子来进行的。

  并且授权是在实体类和实体类的属性上的,而不是像之前在Controller的Action上,对应的是一个api的url。

  看实例了解详情:

添加Nuget包

  HotChocolate.AspNetCore

  HotChocolate.Data

  HotChocolate.Data.EntityFramework

  HotChocolate.AspNetCore.Authorization

 

权限类

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

创建自定义策略处理类

using Microsoft.AspNetCore.Authorization;
using System.Threading.Tasks;
using System;

namespace GraphQLDemo02
{
    /// <summary>
    /// 权限授权Handler
    /// </summary>
    public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
    {
        /// <summary>
        /// 验证权限
        /// </summary>
        /// <param name="context"></param>
        /// <param name="requirement"></param>
        /// <returns></returns>
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
            //这里可以过滤属性授权
            Console.WriteLine(context.Resource.GetType().GetProperty("Path").GetValue(context.Resource));
            //是否经过验证
            var isAuthenticated = context?.User?.Identity?.IsAuthenticated;
            if (isAuthenticated.HasValue && isAuthenticated.Value)
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }
            return Task.CompletedTask;
        }
    }
}

AuthorizationRequirement类

using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using System;

namespace GraphQLDemo02
{

    /// <summary>
    /// 必要参数类
    /// </summary>
    public class PermissionRequirement : IAuthorizationRequirement
    { 
        /// <summary>
        /// 认证授权类型
        /// </summary>
        public string ClaimType { internal get; set; }
       
        /// <summary>
        /// 发行人
        /// </summary>
        public string Issuer { get; set; }
        /// <summary>
        /// 订阅人
        /// </summary>
        public string Audience { get; set; }
        /// <summary>
        /// 过期时间
        /// </summary>
        public TimeSpan Expiration { get; set; }
        /// <summary>
        /// 签名验证
        /// </summary>
        public SigningCredentials SigningCredentials { get; set; }       
        /// <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 claimType, string issuer, string audience, SigningCredentials signingCredentials, TimeSpan expiration)
        {
            ClaimType = claimType;                
            Issuer = issuer;
            Audience = audience;
            Expiration = expiration;
            SigningCredentials = signingCredentials;
        }
    }
}

Token生成类

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace GraphQLDemo02
{
    public class JwtToken
    {
        /// <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;
        }
    }
}

Starup.cs

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Security.Claims;
using System.Text;
using Microsoft.EntityFrameworkCore;

namespace GraphQLDemo02
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public void ConfigureServices(IServiceCollection services)
        {          
            services.AddPooledDbContextFactory<AdventureWorks2016Context>(
              (services, options) => options
              .UseSqlServer(Configuration.GetConnectionString("ConnectionString"))
              .UseLoggerFactory(services.GetRequiredService<ILoggerFactory>()))
              .AddGraphQLServer()
              .AddAuthorization()
              .AddQueryType<Query>()
              .AddFiltering()
              .AddSorting()
              .AddProjections();
            AddAuth(services);
        }


        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseAuthentication();
            app.UseRouting();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGraphQL();
            });
        }
        void AddAuth(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,
                RequireExpirationTime = true,

            };
            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);

            //如果第三个参数,是ClaimTypes.Role,上面集合的每个元素的Name为角色名称,如果ClaimTypes.Name,即上面集合的每个元素的Name为用户名
            var permissionRequirement = new PermissionRequirement(              
                ClaimTypes.Role,
                audienceConfig["Issuer"],
                audienceConfig["Audience"],
                signingCredentials,
                expiration: TimeSpan.FromSeconds(1000000)//设置Token过期时间
                );

            services.AddAuthorization(options =>
            {
                options.AddPolicy("Permission", policy => policy.AddRequirements(permissionRequirement));
            }).
            AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o =>
            {
                //不使用https
                o.RequireHttpsMetadata = false;
                o.TokenValidationParameters = tokenValidationParameters;

            });
            //注入授权Handler
            services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
            services.AddSingleton(permissionRequirement);
        }
    }
}

Query.cs

using HotChocolate;
using HotChocolate.Data;
using HotChocolate.Types;
using System;
using System.Linq;
using System.Security.Claims;

namespace GraphQLDemo02
{       
    public class Query
    {
        [UseDbContext(typeof(AdventureWorks2016Context))]
        [UseOffsetPaging]
        [UseProjection]
        [UseFiltering]
        [UseSorting]
        public IQueryable<Product> GetProducts([ScopedService] AdventureWorks2016Context context)
        {
            return context.Products;
        }

        [UseDbContext(typeof(AdventureWorks2016Context))]
        [UsePaging]
        [UseProjection]
        [UseFiltering]
        [UseSorting]
        public IQueryable<Person> GetPersons([ScopedService] AdventureWorks2016Context context)
        {
            return context.People;
        }
        public TokenModel Login(string username, string password, [Service] PermissionRequirement requirement)
        {
            Console.WriteLine(username);
            var isValidated = username == "gsw" && password == "111111";
            if (!isValidated)
            {
                return new TokenModel()
                {
                    Result = false,
                    Message = "认证失败"
                };
            }
            else
            {
                //如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
                var claims = new Claim[] {
                    new Claim(ClaimTypes.Name, username),
                    new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(200000).ToString())
                };

                var token = JwtToken.BuildJwtToken(claims, requirement);
                return new TokenModel()
                {
                    Result = true,
                    Data = token.access_token
                };
            }
        }    
    }
}

HotChocklate的验证是通过在实体类或实体类的属性上加自定义策略来对数据进行权限控制,所以下面的例子是加在实体类上,全部属性进行授权验证。

Product.cs

[HotChocolate.AspNetCore.Authorization.Authorize(Policy = "Permission")]
    public partial class Product
    {
       //此处略n多行
    }

Person.cs

 [Authorize(Policy = "Permission")]
    public partial class Person
    {
      //此处略n多行
    }

运行结果:

未验证

 

 登录

 

 拿到token后,在header上追加验证token信息,再次访问,成功获取数据

 

 

  想要更快更方便的了解相关知识,可以关注微信公众号   

 

 

标签:验证,System,new,Microsoft,GraphQL,using,var,授权,public
来源: https://www.cnblogs.com/ljknlb/p/15859184.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有