前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《ASP.NET Core 与 RESTful API 开发实战》-- (第8章)-- 读书笔记(上)

《ASP.NET Core 与 RESTful API 开发实战》-- (第8章)-- 读书笔记(上)

作者头像
郑子铭
发布2021-01-13 15:45:21
6050
发布2021-01-13 15:45:21
举报
文章被收录于专栏:DotNet NB && CloudNative

第 8 章 认证和安全

8.1 认证

认证(Authentication)是指验证用户身份的过程,授权(Authorization)是验证一个已经通过认证的用户是否有权限做某些事的过程

常见的 HTTP 认证方式包括:

  • Basic 认证:用户名密码
  • Digest 认证:摘要认证
  • Bearer 认证:token 认证

常见的 Token 有两种类型,一种是引用类型,另一种是自包含类型。前者是服务器根据自己定义的规则随机生成的字符串,后者是对用户信息以及 Token 元数据等信息进行编码、加密之后得到的结果。JWT 是最为常见的自包含类型的 Token

JWT 全名为 JSON Web Token,是一个开放标准,一种 Token 格式,它由3部分组成,分别是头部、负载和签名,各部分之间以.隔开,例如:header.payload.signature

头部由两部分组成,即 Token 类型和使用的算法名称

代码语言:javascript
复制
{
    "alg": "HS256",
    "type": "JWT"
}

负载部分包括要传输的信息,通常由多个 Claim 构成,Claim 是与实体相关的信息以及其他元数据

Claim 有3种类型:已注册、公有、私有

公共类型的 Claim 主要是常见且通用的 Claim,如 name、email 和 gender 等

一个典型的 JWT 负载如下:

代码语言:javascript
复制
{
    "sub": "1234567890",
    "name": "John Doe",
    "Admin": true
}

签名部分通过使用头部指定的算法以及一个密钥,对 Base64 编码后的头部和负载加密而成

代码语言:javascript
复制
HMAC-SHA256(
encodeBase64Url(header) + '.' +
encodeBase64Url(payload),
secret)

签名主要用于验证消息不会被篡改

最终,上述3个部分的内容均使用 Base64 编码,并使用 "." 将各部分隔开,即为一个标准的 JWT

使用 JWT 能够以紧凑的方式传递用户信息,并通过签名保护其中的信息不会被修改。需要注意到是,它很容易被解码,因此不应该在 Token 中包含敏感信息,如用户密码等

接下来,通过 JwtBearer 认证中间件实现基于 Token 的认证

添加nuget

代码语言:javascript
复制
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

在 Startup 类中注册服务

代码语言:javascript
复制
// defaultScheme用于指定当未指定具体认证方案时将会使用的默认方案
services.AddAuthentication(defaultScheme: JwtBearerDefaults.AuthenticationScheme);

在 Startup 类中使用服务

代码语言:javascript
复制
app.UseAuthentication();

对于不同的认证方式(如 Cookie 或 JwtBearer),ASP.NET Core 均在其实现的包中包含相应的扩展方法,以便添加相应类型的认证方式,例如

代码语言:javascript
复制
services.AddAuthentication(defaultScheme: JwtBearerDefaults.AuthenticationScheme)
.AddCookie()
.AddJwtBearer();

可以添加多个相同类型的认证方式,但指定的名称必须不同

代码语言:javascript
复制
services.AddAuthentication()
.AddCookie("cookie1")
.AddCookie("cookie2");

当添加 JwtBearer 认证方式时,JwtBearerOptions 对象能够配置该认证的选项,它的 TokenValidationParameters 属性用于指定验证 Token 时的规则

代码语言:javascript
复制
var tokenSection = Configuration.GetSection("Security:Token");

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuer = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = tokenSection["Issuer"],// 合法的签发者
            ValidAudience = tokenSection["Audience"],// 合法的接受方
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenSection["Key"])),// 签名验证的安全密钥
            ClockSkew = TimeSpan.Zero// 验证时间的时间偏移值
        };
    });

上述代码会从配置文件中读取关于 Token 的信息,因此还需要在 appsettings.json 中添加如下内容

代码语言:javascript
复制
"Security": {
"Token": {
  "Issuer": "demo_issuer",
  "Audience": "demo_audience",
  "Key": "<your_secret_key>" 
}
},

接下来,为了使用 ASP.NET Core 的认证功能来保护资源,应为 Controller 或 Action 添加 [Authorize] 特性

代码语言:javascript
复制
[Authorize]
public class AuthorController : ControllerBase
{}

如果使用了多个认证方式,可以使用 [Authorize] 特性的 AuthenticationSchemes 属性指明当前使用哪一种认证方式

代码语言:javascript
复制
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class BookController : ControllerBase
{}

JwtBearer 中间件提供了对 JWT 的验证功能,然而并未能提供生成 Token 的功能。要生成 Tokne,可以使用 JwtSecurityTokenHandler 类

接下来,我们创建一个 Controller,它将会根据用户的认证信息生成 JWT,并返回给客户端

代码语言:javascript
复制
namespace Library.API.Controllers
{
    [Route("auth")]
    [ApiController]
    public class AuthenticateController : ControllerBase
    {
        public IConfiguration Configuration { get; set; }

        public AuthenticateController(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        [HttpPost("token", Name = nameof(GenerateToken))]
        public IActionResult GenerateToken(LoginUser loginUser)
        {
            if (loginUser.UserName != "demouser" || loginUser.Password != "demopassword")
            {
                return Unauthorized();
            }

            var claims = new List<Claim>
            {
                new Claim(JwtRegisteredClaimNames.Sub,loginUser.UserName)
            };

            var tokenConfigSection = Configuration.GetSection("Security:Token");
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenConfigSection["Key"]));
            var signCredential = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var jwtToken = new JwtSecurityToken(
                issuer: tokenConfigSection["Issuer"],
                audience: tokenConfigSection["Audience"],
                claims: claims,
                expires: DateTime.Now.AddMinutes(3),// 由于 JWT 不支持销毁以及撤回功能,因此在设置它的有效时间时,应该设置一个较短的时间
                signingCredentials: signCredential);

            return Ok(new
            {
                token = new JwtSecurityTokenHandler().WriteToken(jwtToken),
                expiration = TimeZoneInfo.ConvertTimeFromUtc(jwtToken.ValidTo, TimeZoneInfo.Local)
            });
        }
    }

    public class LoginUser
    {
        public string UserName { get; set; }
        public string Password { get; set; }
    }
}

对于受保护的资源,应在每一次请求时均携带 Authorization 消息头

如果不希望添加认证功能,则应为其添加 [AllowAnonymous] 特性

当服务器验证 Token 通过时,JwtBearer 认证处理器会通过 JwtSecurityTokenHandler 将 Token 转换为 ClaimPrincipal 对象,并将他赋值给 HttpContext 对象的 User 属性

ClaimPrincipal 类代表一个用户,它包含一些重要的属性(如 Identity 和 Identities),它们分别返回该对象中主要的 ClaimsIdentity 对象和所有的 ClaimsIdentity 对象集合

ClaimsIdentity 类代表用户的一个身份,一个用户可以有一个或多个身份;ClaimsIdentity 类则又由一个或多个 Claim 组成

Claim 类代表与用户相关的具体信息(如用户名和出生日期等)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第 8 章 认证和安全
    • 8.1 认证
    相关产品与服务
    消息队列 TDMQ
    消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档