ASP.NET Core 3.0轻量级角色API控制授权库
说明
asp.net core 3.0 一个 jwt 的轻量角色/用户、单个api控制的授权认证库
最近得空,重新做一个角色授权库,而之前做了一个角色授权库,是利用微软的默认接口做的,查阅了很多文档,因为理解不够,所以最终做出了有问题。
之前的旧版本 https://github.com/whuanle/czgl.auth/tree/1.0.0
如果要使用微软的默认接口,我个人认为过于繁杂,而且对于这部分的资料较少。。。
使用默认接口实现授权认证,可以参考我另一篇文章
asp.net core 使用 jwt 自定义角色/策略授权需要实现的接口
得益于大笨熊哥的引导,利用放假时间重新做了一个,利用微软本身的授权认证,在此基础上做拓展。特点是使用十分简便,无需过多配置;因为本身没有“造*”,所以如果需要改造,也十分简单。
此库更新到 .net core 3.0 了,如果需要在 2.2x 上使用,可以到仓库下载项目,然后把 nuget 包换成 2.2 的。
感谢大笨熊哥的指导。
项目仓库地址 https://github.com/whuanle/czgl.auth
一、定义角色、api、用户
随便新建一个网站或api项目,例如 myauth。
nuget 里搜索 czgl.auth,按照 2.0.1 版本,或者使用 package manager 命令
install-package czgl.auth -version 2.0.1
czgl.auth 设计思路是,网站可以存在多个角色、多个用户、多个api,
一个角色拥有一些 api,可以添加或删除角色或修改角色所有权访问的 api;
一个用户可以同时属于几个角色。
第一步要考虑网站的角色、用户、api设计,
czgl.auth 把这些信息存储到内存中,一个用户拥有那几个角色、一个角色具有哪些api的访问权限。
角色跟 api 是对应关系,用户跟角色是多对多关系。
新建一个类 roleservice.cs ,引入 using czgl.auth.services;
,roleservice 继承 manarole。
通过以下接口操作角色权限信息
protected bool addrole(rolemodel role); protected bool adduser(usermodel user); protected bool removerole(string rolename); protected bool removeuser(string username);
很明显,添加/移除一个角色,添加/移除一个用户
假如有 a、b、c 三个角色,
有 /a、/b、/c、/ab、/ac、/bc、/abc 共7个api,设定权限
a 可以访问 a、ab、ac、abc
b 可以访问 b、ab、bc、abc
c 可以访问 c、ac、bc、abc
这里采用模拟数据的方法,不从数据库里面加载实际数据。
在 roleservice 里面增加一个方法
/// <summary> /// 用于加载角色和api /// </summary> public void updaterole() { list<rolemodel> roles = new list<rolemodel> { new rolemodel { rolename="a", apis=new list<oneapimodel> { new oneapimodel { apiname="a", apiurl="/a" }, new oneapimodel { apiname="ab", apiurl="/ab" }, new oneapimodel { apiname="ac", apiurl="/ac" }, new oneapimodel { apiname="abc", apiurl="/abc" } } }, new rolemodel { rolename="b", apis=new list<oneapimodel> { new oneapimodel { apiname="b", apiurl="/b" }, new oneapimodel { apiname="ab", apiurl="/ab" }, new oneapimodel { apiname="bc", apiurl="/bc" }, new oneapimodel { apiname="abc", apiurl="/abc" } } }, new rolemodel { rolename="a", apis=new list<oneapimodel> { new oneapimodel { apiname="a", apiurl="/a" }, new oneapimodel { apiname="ab", apiurl="/ab" }, new oneapimodel { apiname="ac", apiurl="/ac" }, new oneapimodel { apiname="abc", apiurl="/abc" } } } }; foreach (var item in roles) { addrole(item); } }
有了角色和对应的api信息,就要添加用户了,
假设有 aa、bb、cc 三个用户,密码都是 123456,aa 属于 a 角色, bb 属于 b角色...
public void updateuser() { adduser(new usermodel { username = "aa", beroles = new list<string> { "a" } }); adduser(new usermodel { username = "bb", beroles = new list<string> { "b" } }); adduser(new usermodel { username = "cc", beroles = new list<string> { "c" } }); }
为了能够把角色和用户加载进 czgl.auth ,你需要在程序启动时,例如在 program 里,使用
roleservice roleservice = new roleservice(); roleservice.updaterole(); roleservice.updateuser();
二、添加自定义事件
授权是,可能会有各种情况,你可以添加自定义事件记录下用户访问的授权信息、影响授权结果。
引用 using czgl.auth.interface;
,
添加一个类 roleevents 继承 iroleeventshadner
public class roleevents : iroleeventshadner { public async task start(httpcontext httpcontext) { await task.completedtask; } public void tokenebnormal(object eventsinfo) { } public void tokenissued(object eventsinfo) { } public void nopermissions(object eventsinfo) { } public void success(object eventsinfo) { } public async task end(httpcontext httpcontext) { await task.completedtask; } }
在 czgl.auth 开始验证授权前调用 start,结束时调用 end,传入传参数是 httpcontext 类型,你可以在里面添加自定义授权的信息,在里面可以影响请求管道。
其他几个方法含义如下:
- tokenebnormal 客户端携带的 token 不是有效的 jwt 令牌,将不能被解析
- tokenissued 令牌解码后,issuer 或 audience不正确
- nopermissions 无权访问此 api
在授权认证的各个阶段将会调用上面的方法。
三、注入授权服务和中间件
使用 czgl.auth ,你需要注入以下两个服务
services.addroleservice(authoptions); services.addsingleton<iroleeventshadner, roleevents>();
addroleservice
是注入授权服务,addsingleton
注入你的事件。
addroleservice 需要一个 authconfigmodel 类型作参数。
你可以这样配置
var authoptions = new authbuilder() .security("aaaafsfsfdrhdhrejtrjrt", "aspnetcore", "aspnetcore") .jump("accoun/login", "account/error", false, false) .time(timespan.fromminutes(20)) .infoscheme(new czgl.auth.models.authenticatescheme { tokenebnormal = "login authentication failed!", tokenissued = "login authentication failed!", nopermissions = "login authentication failed!" }).build(); services.addroleservice(authoptions); services.addsingleton<iroleeventshadner, roleevents>();
security 配置密钥相关,参数分别是密钥字符串、颁发者、订阅者。
jump 配置授权失败时,跳转地址。参数分别是未授权时跳转、授权无效跳转,后面两个 bool 可以设置跳转或跳转。
time 配置 token 有效期。
infoscheme 授权失败提示信息,例如
上图的是时间过期的提示消息,用户请求api失败时返回 401 状态码,header 会携带提示消息,czgl.auth 里面设置了三种情况下,自定义头部:
tokenebnormal 客户端携带的 token 不是有效的 jwt 令牌,将不能被解析
tokenissued 令牌解码后,issuer 或 audience不正确
nopermissions 无权访问此 api
添加三个中间件
app.useauthentication(); app.useauthorization(); app.usemiddleware<rolemiddleware>();
app.useauthorization();
是微软授权认证的中间件,czgl.auth 会先让,默认的验证管道过滤一些无效请求和认证信息,再由 czgl.auth 来校验授权。
四、如何设置api的授权
很简单,czgl.auth 的认证授权,你只需在 controller 或 action上 添加 [authorize]
。
czgl.auth 只会对使用了 [authorize]
特性的 controller 或 action 生效。
如果一个 controller 已经设置了 [authorize]
,但是你想里面的 action 跳过授权认证,则使用 [allowanonymous]
修饰 action。
使用方法跟微软的默认的完全一致。这样无需过多配置。
如果你想另外定义一个特性用来另外设置 授权的话,可以到我的仓库提 issue 或者直接联系我微信。
添加一个 apicontroller ,
[authorize] [route("api/[controller]")] [apicontroller] public class testcontroller : controllerbase { [httpget("/a")] public jsonresult a() { return new jsonresult(new { code = 200, message = "success!" }); } [httpget("/b")] public jsonresult b() { return new jsonresult(new { code = 200, message = "success!" }); } [httpget("/c")] public jsonresult c() { return new jsonresult(new { code = 200, message = "success!" }); } [httpget("/ab")] public jsonresult ab() { return new jsonresult(new { code = 200, message = "success!" }); } [httpget("/bc")] public jsonresult bc() { return new jsonresult(new { code = 200, message = "success!" }); } [httpget("/ac")] public jsonresult ac() { return new jsonresult(new { code = 200, message = "success!" }); } [httpget("/abc")] public jsonresult abc() { return new jsonresult(new { claims = user.claims }); } /// <summary> /// 任何人都不能访问 /// </summary> /// <returns></returns> [httpget("d")] public jsonresult d() { return new jsonresult(new { code = 200, message = "success!" }); } [httpget("error")] public jsonresult denied() { return new jsonresult( new { code = 0, message = "访问失败!", data = "此账号无权访问!" }); } }
五、添加登录颁发 token
添加一个 accountcontroller.cs 用来颁发登录、 token。
[route("api/[controller]")] [apicontroller] public class accountcontroller : controllerbase { [httppost("/login")] public async task<jsonresult> login([fromquery]string username, string password, string rolename) { // 用户名密码是否正确 if (string.isnullorwhitespace(username) || string.isnullorwhitespace(password) || string.isnullorwhitespace(rolename)) { return new jsonresult(new { code = 0, message = "尼玛,上传什么垃圾信息", }); } if(!((username=="aa"||username=="bb"||username=="cc")&&password=="123456")) { return new jsonresult(new { code = 0, message = "账号或密码错误", }); } // 你自己定义的角色/用户信息服务 roleservice roleservice = new roleservice(); // 检验用户是否属于此角色 var role = roleservice.isusertorole(username,rolename); // czgl.auth 中一个用于加密解密的类 encryptionhash hash = new encryptionhash(); // 设置用户标识 var userclaims = hash.buildclaims(username, rolename); //// 自定义构建配置用户标识 /// 自定义的话,至少包含如下标识 //var userclaims = new claim[] //{ //new claim(claimtypes.name, username), // new claim(claimtypes.role, rolename), // new claim(jwtregisteredclaimnames.aud, audience), // new claim(claimtypes.expiration, timespan.totalseconds.tostring()), // new claim(jwtregisteredclaimnames.iat, new datetimeoffset(datetime.now).tounixtimeseconds().tostring()) //}; /* iss (issuer):签发人 exp (expiration time):过期时间 sub (subject):主题 aud (audience):受众 nbf (not before):生效时间 iat (issued at):签发时间 jti (jwt id):编号 */ // 方法一,直接颁发 token responsetoken token = hash.buildtoken(userclaims); //方法二,拆分多步,颁发 token,方便调试 //var identity = hash.getidentity(userclaims); //var jwt = hash.buildjwttoken(userclaims); //var token = hash.buildjwtresponsetoken(jwt); return new jsonresult(token); } }
六、部分说明
注入 jwt 服务、颁发 token
czgl.auth 把使用 jwt 的服务和颁发 token 的代码封装好了,这个库不是在“造*”,所以实际上你可以很轻松的把这部分的代码抽出来,另外设计。
这部分的代码所在位置 roleserviceextension.cs 、encryptionhash.cs。
授权中间件
app.useauthentication(); app.useauthorization(); app.usemiddleware<rolemiddleware>();
我的写法是利用 asp.net core 的 jwt 完成基础的认证授权,然后在下一个管道中实现拓展的认证。但是本身的认证是在 app.useauthorization(); 做了拓展,所以使用 czgl.auth,只需要按照平常 jwt 的方式去使用,只是加了一个 rolemiddleware 中间件。
czgl.auth 只是我受到新思路启发临时写出来的。。。最好不要直接用于生产,去 github 库把项目下载下来,按照自己应用场景改一下~。
七、验证
先使用 aa 用户登录,登录时选择 a 角色。
因为 a 用户只能访问 “带有 a ” 的api, "/a"、"/ab" 等,所以我们可以试试。
继续用这个 token 访问一下 "/b"
可以继续尝试添加 api 或者使用其他用户登录,访问不同的 api。
由于别人对前端不熟,所以就不写带页面的示例了~。
可以用 postman 就行测试。
什么示例的 项目可以到仓库里下载,名称是 myauth。
一般上,用户权限、角色权限信息是存储在数据库里面的,另一个示例是 czgl.auth.sample2。
这个库只是较为粗略的授权认证,与更丰富的需求请自行下载源码修改~
到此这篇关于asp.net core 3.0轻量级角色api控制授权库的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。