威势网络,为您的企业和团队注入互联网活力!
服务热线:138-9741-0341

[原创] IdentityServer4权限控制---客户端授权模式之API服务器搭建(一)

发布日期:2022/9/3 作者: 浏览:951

    今天简单的研究了一下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: empty
500就是服务器错误,也就是说你的代码是有问题的。为什么会报这个问题呢?从提示看,很明显。这是因为我们的接口访问需要身份验证才可以,而我们却没有给服务器指定身份验证方式。庆幸的一点是我们的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


下拉加载更多评论
最新评论
暂无!