GraphQL:验证与授权

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 =>{//不使用httpso.RequireHttpsMetadata = false;o.TokenValidationParameters = tokenValidationParameters;});//注入授权Handlerservices.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信息,再次访问,成功获取数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/306523.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python 创建目录_第二天:Python中目录及文件操作

Python创建目录使用os模块mkdir创建一级目录&#xff0c;但不会创建父级目录#创建test目录&#xff0c;成功 import os os.mkdir(E:test)##若无父级目录python,则创建test目录失败 import os os.mkdir(E:pythontest)makedirs可创建父级目录import os os.makedirs(E:pythontest)…

计算机科普小知识——U盘格式化

在格式化U盘的时候我们需要选择文件系统类型&#xff0c;分别有FAT32&#xff08;默认&#xff09;&#xff0c;NTFS和exFAT这三种格式 常见格式一&#xff1a;FAT32 FAT32是windows传统的文件格式&#xff0c;对每个分区只有4GB的容量&#xff0c;是任何一种usb存储设备都会预…

.NET 5干货来袭 嘉宾李杨桂素伟

Azure Show大家好&#xff0c;欢迎来到Azure Show第八期&#xff0c;好久不见&#xff01;因为工作的原因有两个月没和大家见面&#xff0c;但12月Azure Show回归&#xff0c;会有更多大家感兴趣的话题&#xff0c;也有更多嘉宾与大家见面。除了这一期&#xff0c;在12月我们还…

setnx和expire合成一条指令_Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?...

从接触 Python 时起&#xff0c;我就觉得 Python 的元组解包&#xff08;unpacking&#xff09;挺有意思&#xff0c;非常简洁好用。最显而易见的例子就是多重赋值&#xff0c;即在一条语句中同时给多个变量赋值&#xff1a;>>> x, y 1, 2 >>> print(x, y) …

计算机科普小知识——Win7系统32位与64位的区别,该如何选择?

Win7系统32位与64位的区别 首先我们要知道32位和64位指的是什么。其实这是根据CPU内的寄存器字长来确定的&#xff0c;计算机内部数据都是二进制来呈现的&#xff0c;32位的计算机CPU一次最多能处理32位的二进制数据&#xff0c;而64位的计算机CPU一次最多能处理64位的二进制数…

你知道这个C#开发跨平台APP的样例介绍开源项目吗?

站长英文太差就不翻译了&#xff0c;大家看效果图&#xff0c;都是使用Xamarin.Forms开发的开源移动App介绍&#xff0c;感兴趣的可以访问Github和Gitee仓库看看&#xff0c;下载对应的App项目研究。Github&#xff1a;https://github.com/jsuarezruiz/xamarin-forms-goodlooki…

mybatis plus 批量保存_mybatis源码分析

原理图&#xff1a;Configuration解析&#xff1a;Configuration表示配置&#xff0c;该对象中维护了很多mybatis的配置参数&#xff1b;大致可分为四部分&#xff1a;1.环境变量Environment 2.配置参数&#xff1b;3.缓存集合&#xff1b;4.插件及其他1.1环境变量EnvironmentE…

判断领导是在压榨你,还是在培养你?就看这5点!别被骗了!

职场&认知洞察 丨 作者 / findyi这是findyi公众号分享的第102篇原创文章前文写了职场PUA的文章&#xff0c;有读者问洋哥&#xff1a;我的领导有点像PUA&#xff0c;但又总是说要培养我&#xff0c;怎么破&#xff1f;读者问的问题并不是个案&#xff0c;前不久一个哥们挺郁…

java使用教程——组件及事件处理——窗口(设置窗口的颜色和背景)

用JFrame时&#xff0c;设置背景颜色需使用JFrame.getContentPane().setBackground(Color.red) Container conthis.getContentPane();//得到内容窗格 con.setBackground(Color.blue); 而使用Frame时则可以直接使用setBackground(Color.red)&#xff0c;且需要设置窗体默认关闭事…

揭秘软件开发的达摩克利斯之剑

↑ ???? 万字长文不想看&#xff0c;那就听一听叭 ↑为什么你的程序总是出现 bug&#xff1f;凭什么让改 bug 占据了你大部分的时间&#xff1f;看完本文&#xff0c;保证你能设计出更稳定的程序&#xff0c;摆脱 bug 的缠绕&#xff0c;做项目更安心&#xff01;记得我在学…

java使用教程——组件及事件处理——菜单(添加图标)

菜单条JMenuBar 菜单JMenu 菜单项JMenuItem menuFruit.addSeparator(); //在菜单添加分隔线 public class Example9_2 {public static void main(String args[]) {WindowMenu winnew WindowMenu("带菜单的窗口",20,30,600,290);} }C:/Users/86156/OneDrive/图片/水…

java使用教程——组件及事件处理——常用组件与布局

常用组件&#xff1a; 1.JTextField(文本框) 允许用户在文本框中输入单行文本 2.JTextArea(文本区) 允许用户文本区中输入多行文本 3.JLabel(标签) 标签为用户提供信息 4.JButton(按钮) 允许用户单击按钮 5.JCheckBox(复选框) 为用户提供多种选择 6.JComboBox(下拉列表&#xf…

数据库大战,AWS又将目标瞄准了微软SQL Server

喜欢就关注我们吧&#xff01;文|白开水AWS 宣布了一种新的数据库产品 — 用于 Aurora PostgreSQL 的 Babelfish。该产品旨在效仿 Microsoft 的 SQL Server&#xff0c;并吸引 SQL Server 用户迁移到 AWS 云平台。图片来源&#xff1a;techcrunchAWS 首席执行官 Andy Jassy 在 …

惊!Kubernetes 将弃用 Docker,开发者们怎么办?

喜欢就关注我们吧&#xff01;文|大东BE近日&#xff0c;Kubernetes 官方发布公告&#xff0c;宣布自 v1.20 起放弃对 Docker 的支持&#xff0c;届时用户将收到 Docker 弃用警告&#xff0c;并需要改用其他容器运行时。但 Docker 作为容器镜像构建工具的作用将不受影响&#x…

sql计算留存_SQL基础第七讲:关于用户留存率的计算

最近&#xff0c;好几个小伙伴都拿着关于用户留存的面试题来问我&#xff0c;所以今天单独开一篇文章讲一下留存问题。首先看一下留存是什么&#xff0c;简单来说&#xff0c;我和你今天在一家超市购物了&#xff0c;明天我来购物了&#xff0c;你没来&#xff0c;那么我就是这…

java实用教程——组件及事件处理——ActionEvent事件

事件源&#xff1a; 文本框&#xff0c;按钮&#xff0c;菜单项&#xff0c;密码框&#xff0c;单选按钮 注册监视器&#xff1a; 能够触发ActionEvent事件的组件使用方法 addActionListener(ActionListener listener) 处理事件接口&#xff1a; ActionListener接口中只有一个方…

【Azure Show】|第七期 特别版线上沙龙直播回顾. 嘉宾张坤段清华谭国欣柯克黄炜锵...

我是MVP 继与广州图书馆合作推出【搭上AI快车】在线公益课堂和大家分享了基于移动应用的人工智能开发经验后&#xff0c;本期继续与广州图书馆合作&#xff0c;邀请更多的微软技术专家&#xff0c;推出我们Azure Show节目的特别版&#xff0c;为大家带来各IT技术领域的经验分享…

java实用教程——组件及事件处理——ItemEvent事件(设置字体类型)

ItemEvent事件源&#xff1a; 选择框&#xff0c;下拉列表都可以触发ItemEvent事件 注册监视器&#xff1a; 能够触发ItemEvent事件的组件使用addItemListener(ItemListener listen) 将实现ItemListener的接口的类的实例注册为事件源的监视器 ItemListener接口&#xff1a; 接口…

理解 redis 中的 哈希对象类型

redis中的hash也是我们使用中的高频数据结构&#xff0c;它的构造基本上和编程语言中的HashTable&#xff0c;Dictionary大同小异&#xff0c;如果大家往后有什么逻辑需要用Dictionary存放的话&#xff0c;可以根据场景优先考虑下redis哦&#xff0c;起码可以装装嘛&#xff0c…

java实用教程——组件及事件处理——DocumentEvent事件

DocumentEvent事件源&#xff1a; 文本区Document的维护 注册监视器&#xff1a; 使用addDocumentListener(DocumentListener listen)为事件源添加监视器 DocumentListener接口&#xff1a; 接口中有三个方法&#xff1a; public void changUpdate(DocumentEvent e); public vo…