ASP.NET Core 3.1 WebApi 系列【四】使用JWT认证

上一篇中我们学习了使用AuTofac实现依赖注入,这一篇我们继续学习在ASP.Net Core 3.1 中使用JWT认证

JWT介绍:

JSON Web Tokens:一种基于JSON的、用于在网络上声明某种主张的令牌(token)。

 

jwt验证流程:

1.首先,前端通过 Web 表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个 HTTP POST 请求。建议的方式是通过 SSL 加密的传输(https 协议),从而避免敏感信息被嗅探。 

 

2.后端核对用户名和密码成功后,将用户的 id 等其他信息作为 JWT Payload(有效载荷),将其与头部分别进行 Base64 编码拼接后签名,形成一个 JWT。形成的 JWT 就是一个形同 lll.zzz.xxx 的字符串。 

 

3.后端将 JWT 字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在 localStorage 或 sessionStorage 上,退出登录时前端删除保存的 JWT 即可。 

 

4.前端在每次请求时将 JWT 放入 HTTP Header 中的 Authorization 位。(解决 XSS 和 XSRF 问题) 

 

5.后端检查是否存在,如存在验证 JWT 的有效性。例如,检查签名是否正确;检查 Token 是否过期;检查 Token 的接收方是否是自己(可选)。 

 

6.验证通过后后端使用 JWT 中包含的用户信息进行其他逻辑操作,返回相应结果。

 

jwt验证流程图:

 

 

若还不清楚什么是JWT的请先了解下什么是JWT,下面我们直接上步骤。

一、创建一个类,用来存储签发或者验证jwt时用到的信息,项目结构如下:

 public class TokenManagement
    {
        public string Secret { get; set; }

        public string Issuer { get; set; }

        public string Audience { get; set; }

        public int AccessExpiration { get; set; }

        public int RefreshExpiration { get; set; }
    }

二、在 appsettings.json 增加jwt使用到的配置信息

 

"tokenManagement": {
  "secret": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
  "issuer": "webapi.cn", //发行人
  "audience": "WebApi", //受众
  "accessExpiration": 30, //访问过期时间
  "refreshExpiration": 60 //刷新过期时间
}

三、在startup类的ConfigureServices方法中增加读取配置信息

  #region 获取tokenManagement
     services.Configure<TokenManagement>(Configuration.GetSection("tokenManagement"));
     var token = Configuration.GetSection("tokenManagement").Get<TokenManagement>();
  #endregion

到目前为止,我们完成了一些基础工作,下面在webapi中注入jwt的验证服务,并在中间件管道中启用authentication中间件。

startup类中要引用jwt验证服务的命名空间

四、注入jwt服务

#region 注入jwt的验证服务
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,//是否调用对签名securityToken的SecurityKey进行验证
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(token.Secret)),
                    ValidIssuer = token.Issuer,//将用于检查令牌的发行者是否与此发行者相同
                    ValidAudience = token.Audience,//检查令牌的受众群体是否与此受众群体相同
                    ValidateIssuer = false,//是否验证发行者
                    ValidateAudience = false//在令牌验证期间验证受众
                };
            });
            #endregion

五、中间件管道中启用authentication中间件,在Configure方法中启用验证

注意事项:app.UseAuthentication();必须在app.UseRouting()和  app.UseEndpoints之间。

 //jwt
 app.UseAuthentication();

六、引入jwt验证服务的命名空间

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;

至此完成了JWT验证的功能,下面就需要增加签发token的逻辑。

七、增加一个专门用来用户认证和签发token逻辑。

(1)新增命名为AuthenticationController的控制器;

 private readonly IAuthenticateService _authService;
        public AuthenticationController(IAuthenticateService authService)
        {
            _authService = authService;
        }

        /// <summary>
        /// 获取token
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult RequestToken(UserEntity request)
        {
            return Ok(_authService.IsAuthenticated(request));
        }

(2)新增命名为IAuthenticateService的接口;

public interface IAuthenticateService
    {
        OperationResult IsAuthenticated(UserEntity request);
    }

(3)新增命名为AuthenticateService的类并实现IAuthenticateService接口;

 public class AuthenticateService : IAuthenticateService
    {
        private readonly TokenManagement _tokenManagement;
        public AuthenticateService(IOptions<TokenManagement> tokenManagement)
        {
            _tokenManagement = tokenManagement.Value;
        }
        public OperationResult IsAuthenticated(UserEntity request)
        {
            LoginService LoginService = new LoginService();
            var loginResult = LoginService.Login(request);
            if (!loginResult.State)
            {
                return loginResult;
            }
            var claims = new[]
            {
                new Claim(ClaimTypes.Name,request.USERNAME),
                new Claim(ClaimTypes.Role,request.PASSWORD)
            };
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenManagement.Secret));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var jwtToken = new JwtSecurityToken(
                _tokenManagement.Issuer,
                _tokenManagement.Audience,
                claims,
                expires: DateTime.Now.AddMinutes(_tokenManagement.AccessExpiration),
                signingCredentials: credentials
                );
            string token = string.Empty;
            token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
            return OperationResult.Success(token);
        }
    }

注意事项: LoginService.Login(request);此方法是账户密码方法,测试时候可以默认返回想要的数据。

 

八、测试

 

(1)在测试用的APi,打上Authorize特性,表明需要授权!

 

 

 

 

先测试一个访问需要授权的接口,但没有携带token信息,返回是401,表示未授权。

(2)下面我们先通过认证接口,获取token

 

 (3)在Startup类中,ConfigureServices方法里设置swagger添加提供jwt验证token

#region 添加提供jwt验证token
                m.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                    Description = "在下框中输入请求头中需要添加Jwt授权Token: Bearer Token",
                    Name = "Authorization",
                    BearerFormat = "JWT",
                    Scheme = "Bearer"
                });
                m.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                           Reference = new OpenApiReference
                           {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer"
                           }
                        }, new string[] { }
                    }
                });
                #endregion

(4)f5运行,在图示位置加上之前获取的token值

 

 

 

 注意事项:在token之前要加上“Bearer ”。

(5)再次执行api,执行成功。

 (6)jwt不仅可以进行身份验证,还可以进行权限授权,只要打上[Authorize(Roles = "admin")]特性即可,具体内容这里不做陈述。感兴趣的朋友可以自己去深度学习。

九、总结

基于token的认证方式,让我们构建分布式/松耦合的系统更加容易。任何地方生成的token,只有拥有相同秘钥,就可以再任何地方进行签名校验。

当然要用好jwt认证方式,还有其他安全细节需要处理,比如palyload中不能存放敏感信息,使用https的加密传输方式等等,可以根据业务实际需要再进一步安全加固!

同时我们也发现使用token,就可以摆脱cookie的限制,所以JWT是移动app开发的首选!

posted @ 2022-02-17 16:02  你去了龙城我留了姑苏  阅读(634)  评论(0编辑  收藏  举报