理解ASP.NET Core验证模型(Claim, ClaimsIdentity, ClaimsPrincipal)不得不读的英文博文
上接:.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的请求头中,然后向资源服务器发起请求。服务器根据客户端发送过来的令牌来进行下一步处理(根据身份来响应客户端是否具有当前接口的权限)
如下图所示:
正文:
主要参考园友 在7楼 的这篇文章 https://www.cnblogs.com/raywang/p/9536524.html
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的