目录:
.NetCore WebApi——基于JWT的简单身份认证与授权(Swagger)
.NetCore WebApi —— Swagger版本控制
任何项目都有权限这一关键部分。比如我们有许多接口。有的接口允许任何人访问,另有一些接口需要认证身份之后才可以访问;以保证重要数据不会泄露。
关于JWT
维基百科上是这样定义的:
JWT(读作 [/dʒɒt/]),即JSON Web Tokens,是一种基于JSON的、用于在网络上声明某种主张的令牌(token)。JWT通常由三部分组成: 头信息(header), 消息体(payload)和签名(signature)。它是一种用于双方之间传递安全信息的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的、自包含的方法,从而使通信双方实现以JSON对象的形式安全的传递信息。
认证的工作流程:
1. 客户端携带用户名、密码向授权服务申请 "令牌(token)"
2.授权服务器验证用户名、密码后根据它的身份生成一张专属的 "令牌" 并JWT的格式规范返回给客户端
3.客户端将获取到的令牌放入到http的请求头中,然后向资源服务器发起请求。服务器根据客户端发送过来的令牌来进行下一步处理(根据身份来响应客户端是否具有当前接口的权限)
如下图所示:
1. 启用Swagger的验证功能
在startup类中新添加红色部分代码。启动项目观察效果。
// 注册Swagger服务 services.AddSwaggerGen(c => { // 添加文档信息 c.SwaggerDoc("v1", new Info { Title = "CoreWebApi", Version = "v1", Description = "ASP.NET CORE WebApi", Contact = new Contact { Name = "Jee", Email = "xiaomaprincess@gmail.com", Url = "https://www.cnblogs.com/jixiaosa/" } }); #region 读取xml信息 // 使用反射获取xml文件。并构造出文件的路径 var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); // 启用xml注释. 该方法第二个参数启用控制器的注释,默认为false. c.IncludeXmlComments(xmlPath, true); #endregion #region 启用swagger验证功能 //添加一个必须的全局安全信息,和AddSecurityDefinition方法指定的方案名称一致即可,CoreAPI。 var security = new Dictionary<string, IEnumerable<string>> { { "CoreAPI", new string[] { } }, }; c.AddSecurityRequirement(security); c.AddSecurityDefinition("CoreAPI", new ApiKeyScheme { Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格", Name = "Authorization",//jwt默认的参数名称 In = "header",//jwt默认存放Authorization信息的位置(请求头中) Type = "apiKey" }); #endregion });
没启动Swagger验证之前是这样的:
启用验证之后再看:多了个小按钮
点开之后是如下界面:文本框里要如输入从服务器获取的Token。格式为:Bearer + 空格+token。 Bearer可以看作是一个默认的规则。
2.生成token
.net core 内置了许多帮助类,巧妙的使用这些类组合,就可以生成我们想要的 token
新建一个tokenl类,编写一个方法来获取JWT字符串
/// <summary> /// 生成JWT字符串 /// </summary> public class Token { // 密钥,注意不能太短 public static string secretKey { get; set; } = "xiaomaPrincess@gmail.com"; /// <summary> /// 生成JWT字符串 /// </summary> /// <param name="tokenModel"></param> /// <returns></returns> public static string GetJWT(TokenModel tokenModel) { //DateTime utc = DateTime.UtcNow; var claims = new List<Claim> { new Claim(JwtRegisteredClaimNames.Jti,tokenModel.ID.ToString()), // 令牌颁发时间 new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"), new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"), // 过期时间 100秒 new Claim(JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(100)).ToUnixTimeSeconds()}"), new Claim(JwtRegisteredClaimNames.Iss,"API"), // 签发者 new Claim(JwtRegisteredClaimNames.Aud,"User") // 接收者 }; // 密钥 var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var tokenHandler = new JwtSecurityTokenHandler(); JwtSecurityToken jwt = new JwtSecurityToken( claims: claims,// 声明的集合 //expires: .AddSeconds(36), // token的有效时间 signingCredentials: creds ); var handler = new JwtSecurityTokenHandler(); // 生成 jwt字符串 var strJWT = handler.WriteToken(jwt); return strJWT; } }
基本信息类
public class TokenModel { /// <summary> /// ID /// </summary> public int ID { get; set; } /// <summary> /// 名称 /// </summary> public string Name { get; set; } /// <summary> /// 手机 /// </summary> public string Phone { get; set; } /// <summary> /// 邮箱 /// </summary> public string Email { get; set; } /// <summary> /// 身份 /// </summary> public string Sub { get; set; } }
添加一个方法来获取token
/// <summary> /// 获取令牌 /// </summary> /// <param name="ID">ID</param> /// <param name="name">账号</param> /// <returns></returns> [HttpPost] public string GetJwt(int ID,string name) { TokenModel tokenModel = new TokenModel { ID = ID, Name=name }; return Token.GetJWT(tokenModel); }
在Startup类中配置身份认证服务
(1)在ConfigureServices方法中注册服务
#region 添加验证服务 // 添加验证服务 services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(o => { o.TokenValidationParameters = new TokenValidationParameters { // 是否开启签名认证 ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Token.secretKey)), // 发行人验证,这里要和token类中Claim类型的发行人保持一致 ValidateIssuer = true, ValidIssuer = "API",//发行人 // 接收人验证 ValidateAudience = true, ValidAudience = "User",//订阅人 ValidateLifetime = true, ClockSkew = TimeSpan.Zero, }; }); #endregion
(2)在Configure方法中启用验证中间件
// 启用Swagger中间件 app.UseSwagger(); // 配置SwaggerUI app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "CoreAPI"); c.RoutePrefix = string.Empty; }); // 启用认证中间件 app.UseAuthentication(); app.UseMvc();
3. 添加一个测试测控制器来检测是否成功
注意要添加 [Authorize]标签
/// <summary> /// 需要身份认证的控制器 /// </summary> [Route("api/[controller]/[action]")] [Produces("application/json")] [ApiController] [Authorize]// 添加授权特性 public class TestController : ControllerBase { /// <summary> /// 认证通过之后可访问 /// </summary> /// <returns></returns> [HttpPost] public ActionResult<TokenModel> Get(TokenModel tokenModel) { return new TokenModel{ ID=1 }; } }
启动项目
测试一: 在没有获取token时访问此方法
返回401 身份验证未通过
测试二:先访问GetJWT接口获取token,在访问Test接口
最后将获取的token输入到Swagger的文本框中:Bearer +空格+Token
再次访问Test接口:成功返回数据
至此,一个简单身份认证加授权就完成了。
推荐一篇大神的文章 :讲述Claim的
理解ASP.NET Core验证模型(Claim, ClaimsIdentity, ClaimsPrincipal)不得不读的英文博文源码:GitHub
https://github.com/xiaoMaPrincess/Asp.NetCore-WebApi
多层架构版本:
https://github.com/xiaoMaPrincess/.NetCoreWebApi
原作者是:千金不如一默
作者: 王春天
QQ群:322581894 关于作者: 大连天翼信息科技有限公司 技术总监。 SNF快速开发平台 创始人。应用平台架构师、IT规划咨询专家、业务流程设计专家。 专注于快速开发平台的开发、代码生成器。同时专注于微软平台项目架构、管理和企业解决方案,多年项目开发与管理经验,精通DotNet系列技术Vue、.NetCore、MVC、Webapi、C#、WinForm等,DB(SqlServer、Oracle等)技术,移动端开发。熟悉Java、VB及PB开发语言。在面向对象、面向服务以及数据库领域有一定的造诣。现从事项目实施、开发、架构等工作。并从事用友软件产品U8、U9、PLM 客开工作。 如有问题或建议,请多多赐教! 本文版权归作者和CNBLOGS博客共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过邮箱或QQ 联系我,非常感谢。