[原创] IdentityServer4权限控制---客户端授权模式之API服务器搭建(一)
今天简单的研究了一下IdentityServer4授权模式,官网地址在这里,有兴趣的朋友看官网,要比我讲的好多了。由于官网是英文的,看起来伤眼眼,又费时间,(其实最重要的是我英文水平不行)所以在这里,我尝试用国人容易理解的方式简单描述一下测试过程。在开始之前,我们先了解几个基本概念及今天的主要实验目的。
基本概念:
API资源:即,受保护的API资源,在这里主要是一台服务器的API资源,如下图所示:
总共有三个API资源,分别是:
https://localhost:6001/identity
https://localhost:6001/api/Students
https://localhost:6001/WeatherForecast
简单说明一下,第一个API成功访问后返回的是当前用户的一些基础信息,是官网的DEMO。第二个是我写的一个测试接口,成功调用后会随机返回5个学生信息,第三个是VS模板生成的天气预报接口。今天我们就拿这三个接口开刀。
对了,差点跑题了,我们接着上面的话题继续解释一下基本概念。
IdentityServer:认证服务器,由IdentityServer4生成,需要引入IdentityServer4资源包,在用到的时候我们再说。
客户端:即,访问我们API资源的程序,今天的客户端是由一个叫client.exe的程序来扮演的,如果精力与时间允许,我们后面再搞手机APP的。
另外,再讲两个稍微洋气一点的名字,Authentication,Authorization。注意看,两个拼写是不同的,一个是用来验证你的TOKEN有没有符合规则的,也就是检查权限的,一个是用来授权的,大家简单理解一下即可,对了,还有一个概念,TOKEN。也就是我们说的令牌,这是干吗的?
举个例子,公司派你去出差,对方公司不认识你,所以公司就给你开了一份介绍信,信上面写了你的名字,年龄,性别,公司等一些信息,用来给对方接待人员看的,但是这些信息别人也可以伪造,怎么区分真假呢?所以公司给你的介绍信上加盖了一个公章,只要对方接待人员拿你介绍信的公章到公商部门一核实,如果没有问题,说明你的信息是可靠的,这个TOKEN简单理解一下,就和这张介绍信一样。那公商部门又怎么知道这公章是真的还是假的呢?这个过程叫鉴权,其实就是通过加密算法,把你上面的信息(身份证,姓名,年龄,性别...)按一定算法加密,加密过程中有一个极为重要的私钥参与了计算,一般人接触不到,只有工商才能接触到,加密完了以后再和你介绍信上的密串对比,如果一致,说明信息是真的。好了,啰嗦的太多了,差点又跑题了,希望把问题说清楚了。
接下来再说说我们的实验目的。假设我们上面的三个API,天气预报是匿名可访问的,学生是登录用户可访问的,Identity是只有管理员才可以访问的,这样我们也就达到了对接口资源进行保护的目的,本次实验的目的算是交待完了。现在我们就着手去做。
本教程算是菜鸟教程了,我想写的尽量简单一点,首先,我们先来看看如何实现一个API资源站点,快速的做法是去下载官网的DEMO,但是我下下来以后可能是因为版本的问题,尽然报错不能运行,所以干脆自己建一个,反正是为了测试目的,这不是重点,建议朋友们和我一样自己建吧!这样还可以体验DIY的快乐,并且,我们自己写一些自己的权限策略上去,快乐更多!
未完待续,先睡觉...
OK,起床后接着写。
我们在电脑上建立以下目录D:\WEB\ID4\APITEST
选择WEB API
指定工作目录
选择6.0版本
按照向导一步步下来,我们的API接口就做好了,如下图所示
打开上面的JSON文件,把启动端口改成6001,以和试验环境以及官网同步,然后启动测试一下,已经没问题了,如下图所示:
我们试着调用以下API,返回以下代码:
[ { "date": "2022-09-04T11:47:40.9032622+08:00", "temperatureC": -9, "temperatureF": 16, "summary": "Balmy" }, { "date": "2022-09-05T11:47:40.9042275+08:00", "temperatureC": 49, "temperatureF": 120, "summary": "Chilly" }, { "date": "2022-09-06T11:47:40.9042304+08:00", "temperatureC": 40, "temperatureF": 103, "summary": "Sweltering" }, { "date": "2022-09-07T11:47:40.9042306+08:00", "temperatureC": 4, "temperatureF": 39, "summary": "Warm" }, { "date": "2022-09-08T11:47:40.9042308+08:00", "temperatureC": -11, "temperatureF": 13, "summary": "Sweltering" } ]注意,状态码200,并未在上面界面显示出来。说明我们的接口做好了。接下来,除了天气预报,我们再加两个测试接口进去,一个是官方的Identity,一个是我们自己写的测试接口Students,多放几个接口是为了测试我们的权限。
代码分别如下:
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Linq; namespace APITEST.Controllers { [Route("identity")] [Authorize] public class IdentityController : ControllerBase { [HttpGet()] public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); } } }
namespace APITEST { public class Student { public int BoinYear { get; set; } public string XingMing { get; set; } public int Age => System.DateTime.Now.Year- BoinYear; public string? Address { get; set; } } }
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace APITEST.Controllers { [Route("api/[controller]")] [ApiController] public class StudentsController : ControllerBase { private static readonly string[] Students = new[] { "TOM", "Jack", "Aimily", "Alice", "Black Smith", "John", "QiQi", "莫言", "王朔", "易中天" }; private static readonly string[] Address = new[] { "中国", "美国", "新加坡", "英国", "加拿大", "新西兰", "日本", "瑞士", "挪威", "斯里兰卡" }; [HttpGet(Name = "GetStudents")] public IEnumerable<Student> Get() { Random rnd = new Random(); return Enumerable.Range(1, 5).Select(index => new Student { XingMing = Students[rnd.Next(Students.Length)], Address = Address[rnd.Next(Address.Length)], BoinYear=rnd.Next(1950,1990) }) ; } } }
把它们加到项目中去,文件目录结构及运行后显示效果如下:
这样我们三个接口就做好了,注意我们的Identity接口,前面加了
[Route("identity")] [Authorize]第一行是API路由地址,第二行是集成的微软的身份验证,它代表只有经过身份验证的用户才可以访问,我们试着在页面上调用一下试试:
报了500错误,
System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions). at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties) at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context) HEADERS ======= Accept: */* Host: localhost:6001 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36 :method: GET Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Referer: https://localhost:6001/swagger/index.html sec-ch-ua: "Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" sec-fetch-site: same-origin sec-fetch-mode: cors sec-fetch-dest: empty500就是服务器错误,也就是说你的代码是有问题的。为什么会报这个问题呢?从提示看,很明显。这是因为我们的接口访问需要身份验证才可以,而我们却没有给服务器指定身份验证方式。庆幸的一点是我们的IDS4与JWT是兼容的,这样给我们代码的移植带来很大方便,我们现在来指定API资源服务器JWT的身份验证方式,打开program.cs,加入以下代码:
builder.Services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "https://localhost:5001";//认证服务器 options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }; });
然后在下面继续加入
app.UseAuthentication(); app.UseAuthorization();这样才能让鉴权功能开启。因为缺少必要的引用包,所以项目并不能启动,我们把它引进来。
搜索
Microsoft.AspNetCore.Authentication.JwtBearer把它引进来,我安装的是6.0.8版本的。然后回到program.cs中去,发现new TokenValidationParameters有红色提示,我们把using Microsoft.IdentityModel.Tokens;引进来就可以了。好了,我们再去调用接口试试,结果如下:
我们看到了,返回的是401错误,也就是说身份验证被拒绝了,我们再调用其它两个接口试试,均可以正常返回调用结果,说明我们的JWT身份验证已经做好了。眼细的朋友可能要问了,你那代码
options.Authority = "https://localhost:5001";//认证服务器不是还指定了一个验证服务器吗?在哪呢? 是的,JWT验证真是方便,还可以这样指定远程验证服务器,不得不说微软想的也真是周到。我们上面调用接口都是在未登录的情况下调用的,也就是给API服务器提交的TOKEN为空,这样服务器直接就拒绝了。总结一下:到现在为止,我们只是搭建了接口服务器,也就是资源服务器,我们对外开放了三个资源接口,我们的目的就是把这三个资源通过身份验证的手段给它保护起来,请记住我们资源服务器(API服务器)的接口地址:https://localhost:6001 因为后面要用到。今天的代码上传到这里,有需要的朋友自己下载测试。OK,啰嗦这么多,如果你能坚持看到这里还没有瞌睡,我们再来看第二篇,如何搭建认证服务器:
[原创] IdentityServer4权限控制---客户端授权模式之IDS4认证服务器搭建(二)
原创文章,转载请注明出处:https://www.qhwins.com/article-27