從壹開始前后端分離【 .NET Core2.0 Api + Vue 2.0 + AOP + 分布式】框架之五 || Swagger的使用 3.3

群友反饋:

群里有小伙伴反饋山卦,在Swagger使用的時(shí)候報(bào)錯(cuò)鞋邑,無法看到列表,這里我說下如何調(diào)試和主要問題:

1、如果遇到問題枚碗,這樣的:


請(qǐng)?jiān)跒g覽器 =》 F12 ==》 console 控制臺(tái) ==》點(diǎn)擊錯(cuò)誤信息地址

或者直接鏈接http://localhost:xxxxx/swagger/v1/swagger.json逾一,就能看到錯(cuò)誤了


2、主要問題就是同一個(gè)controller中的同一個(gè)請(qǐng)求特性(注意[HttpGet]和[HttpGet("{id}")]是兩個(gè)) 肮雨,不能同名



主要修改下路由遵堵,然后配合修改名字就行

[Route("api/[controller]/[action]")]


4、或者直接在方法上增加路由

    [HttpPost]

? ? ? ? [Route("newPost")]

? ? ? ? publicvoidPost([FromBody]string value)

? ? ? ? {

? ? ? ? }



WHY

書接上文怨规,在前邊的兩篇文章中陌宿,我們簡單提到了接口文檔神器Swagger,《從零開始搭建自己的前后端分離【 .NET Core2.0 Api + Vue 2.0 + AOP + 分布式】框架之三 || Swagger的使用 3.1》波丰、《從零開始搭建自己的前后端分離【 .NET Core2.0 Api + Vue 2.0 + AOP + 分布式】框架之四 || Swagger的使用 3.2》壳坪,兩個(gè)文章中,也對(duì)常見的幾個(gè)問題做了簡單的討論呀舔,最后還剩下一個(gè)小問題弥虐,

如何給接口實(shí)現(xiàn)權(quán)限驗(yàn)證?

其實(shí)關(guān)于這一塊媚赖,我思考了下霜瘪,因?yàn)楫吘刮业捻?xiàng)目中是使用的vue + api 搭建一個(gè)前臺(tái)展示,大部分頁面都沒有涉及到權(quán)限驗(yàn)證惧磺,本來要忽略這一章節(jié)颖对,可是猶豫再三,還是給大家簡單分析了下磨隘,個(gè)人還是希望陪大家一直搭建一個(gè)較為強(qiáng)大的缤底,只要是涉及到后端那一定就需要?登陸=》驗(yàn)證了,本文主要是參考網(wǎng)友https://www.cnblogs.com/RayWang/p/9255093.html的思路番捂,我自己稍加改動(dòng)个唧,大家都可以看看。

根據(jù)維基百科定義设预,JWT(讀作 [/d??t/])徙歼,即JSON Web Tokens,是一種基于JSON的鳖枕、用于在網(wǎng)絡(luò)上聲明某種主張的令牌(token)魄梯。JWT通常由三部分組成: 頭信息(header), 消息體(payload)和簽名(signature)。它是一種用于雙方之間傳遞安全信息的表述性聲明規(guī)范宾符。JWT作為一個(gè)開放的標(biāo)準(zhǔn)(RFC 7519)酿秸,定義了一種簡潔的、自包含的方法魏烫,從而使通信雙方實(shí)現(xiàn)以JSON對(duì)象的形式安全的傳遞信息辣苏。

以上是JWT的官方解釋肝箱,可以看出JWT并不是一種只能權(quán)限驗(yàn)證的工具,而是一種標(biāo)準(zhǔn)化的數(shù)據(jù)傳輸規(guī)范考润。所以狭园,只要是在系統(tǒng)之間需要傳輸簡短但卻需要一定安全等級(jí)的數(shù)據(jù)時(shí),都可以使用JWT規(guī)范來傳輸糊治。規(guī)范是不因平臺(tái)而受限制的唱矛,這也是JWT做為授權(quán)驗(yàn)證可以跨平臺(tái)的原因。

如果理解還是有困難的話井辜,我們可以拿JWT和JSON類比:

JSON是一種輕量級(jí)的數(shù)據(jù)交換格式绎谦,是一種數(shù)據(jù)層次結(jié)構(gòu)規(guī)范。它并不是只用來給接口傳遞數(shù)據(jù)的工具粥脚,只要有層級(jí)結(jié)構(gòu)的數(shù)據(jù)都可以使用JSON來存儲(chǔ)和表示窃肠。當(dāng)然,JSON也是跨平臺(tái)的刷允,不管是Win還是Linux冤留,.NET還是Java,都可以使用它作為數(shù)據(jù)傳輸形式树灶。

1)客戶端向授權(quán)服務(wù)系統(tǒng)發(fā)起請(qǐng)求纤怒,申請(qǐng)獲取“令牌”。

2)授權(quán)服務(wù)根據(jù)用戶身份天通,生成一張專屬“令牌”泊窘,并將該“令牌”以JWT規(guī)范返回給客戶端

3)客戶端將獲取到的“令牌”放到http請(qǐng)求的headers中后,向主服務(wù)系統(tǒng)發(fā)起請(qǐng)求像寒。主服務(wù)系統(tǒng)收到請(qǐng)求后會(huì)從headers中獲取“令牌”烘豹,并從“令牌”中解析出該用戶的身份權(quán)限,然后做出相應(yīng)的處理(同意或拒絕返回資源)


一诺祸、通過Jwt獲取Token携悯,并通過緩存記錄,配合中間件實(shí)現(xiàn)驗(yàn)證

在之前的搭建中筷笨,swagger已經(jīng)基本成型蚌卤,其實(shí)其功能之多,不是我這三篇所能寫完的奥秆,想要添加權(quán)限,先從服務(wù)開始


在ConfigureServices中咸灿,增加以下代碼

View Code

?最終的是這樣的

////// ConfigureServices 方法

? ? ? ? //////publicvoid ConfigureServices(IServiceCollection services)

? ? ? ? {

? ? ? ? ? ? services.AddMvc();

? ? ? ? ? ? #regionSwagger? ? ? ? ? ? services.AddSwaggerGen(c =>? ? ? ? ? ? {

? ? ? ? ? ? ? ? c.SwaggerDoc("v1",new Info

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? Version ="v0.1.0",

? ? ? ? ? ? ? ? ? ? Title ="Blog.Core API",

? ? ? ? ? ? ? ? ? ? Description ="框架說明文檔",

? ? ? ? ? ? ? ? ? ? TermsOfService ="None",

? ? ? ? ? ? ? ? ? ? Contact =newSwashbuckle.AspNetCore.Swagger.Contact { Name ="Blog.Core", Email ="Blog.Core@xxx.com", Url ="http://www.reibang.com/u/94102b59cc2a" }

? ? ? ? ? ? ? ? });

? ? ? ? ? ? ? ? //就是這里#region讀取xml信息varbasePath = PlatformServices.Default.Application.ApplicationBasePath;

? ? ? ? ? ? ? ? varxmlPath = Path.Combine(basePath,"Blog.Core.xml");//這個(gè)就是剛剛配置的xml文件名varxmlModelPath = Path.Combine(basePath,"Blog.Core.Model.xml");//這個(gè)就是Model層的xml文件名c.IncludeXmlComments(xmlPath,true);//默認(rèn)的第二個(gè)參數(shù)是false构订,這個(gè)是controller的注釋,記得修改? ? ? ? ? ? ? ? c.IncludeXmlComments(xmlModelPath);

? ? ? ? ? ? ? ? #endregion#regionToken綁定到ConfigureServices//添加header驗(yàn)證信息

? ? ? ? ? ? ? ? //c.OperationFilter();varsecurity =newDictionary> { {"Blog.Core",newstring[] { } }, };

? ? ? ? ? ? ? ? c.AddSecurityRequirement(security);

? ? ? ? ? ? ? ? //方案名稱“Blog.Core”可自定義避矢,上下一致即可c.AddSecurityDefinition("Blog.Core",new ApiKeyScheme

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? Description ="JWT授權(quán)(數(shù)據(jù)將在請(qǐng)求頭中進(jìn)行傳輸) 直接在下框中輸入{token}\"",

? ? ? ? ? ? ? ? ? ? Name ="Authorization",//jwt默認(rèn)的參數(shù)名稱In ="header",//jwt默認(rèn)存放Authorization信息的位置(請(qǐng)求頭中)Type ="apiKey"? ? ? ? ? ? ? ? });

? ? ? ? ? ? ? ? #endregion? ? ? ? ? ? });

? ? ? ? ? ? #endregion#regionToken服務(wù)注冊(cè)? ? ? ? ? ? services.AddSingleton(factory =>? ? ? ? ? ? {

? ? ? ? ? ? ? ? varcache =newMemoryCache(new MemoryCacheOptions());

? ? ? ? ? ? ? ? return cache;

? ? ? ? ? ? });

? ? ? ? ? ? services.AddAuthorization(options =>? ? ? ? ? ? {

? ? ? ? ? ? ? ? options.AddPolicy("Client", policy => policy.RequireRole("Client").Build());

? ? ? ? ? ? ? ? options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build());

? ? ? ? ? ? ? ? options.AddPolicy("AdminOrClient", policy => policy.RequireRole("Admin,Client").Build());

? ? ? ? ? ? });

? ? ? ? ? ? #endregion? ? ? ? }


然后執(zhí)行代碼悼瘾,就可以看到效果


圖 1

圖 2

它的作用就是囊榜,每次請(qǐng)求時(shí),從Header報(bào)文中亥宿,獲取密鑰token卸勺,這里根據(jù)token可以進(jìn)一步判斷相應(yīng)的權(quán)限等。

接下來烫扼,就是在項(xiàng)目中添加五個(gè)文件曙求,如下圖



,圖 3

具體來說:

1:JwtTokenAuth映企,一個(gè)中間件悟狱,用來過濾每一個(gè)http請(qǐng)求,就是每當(dāng)一個(gè)用戶發(fā)送請(qǐng)求的時(shí)候堰氓,都先走這一步挤渐,然后再去訪問http請(qǐng)求的接口

public Task Invoke(HttpContext httpContext)

? ? ? ? {

? ? ? ? ? ? //檢測是否包含'Authorization'請(qǐng)求頭if(!httpContext.Request.Headers.ContainsKey("Authorization"))

? ? ? ? ? ? {

? ? ? ? ? ? ? ? return _next(httpContext);

? ? ? ? ? ? }

? ? ? ? ? ? vartokenHeader = httpContext.Request.Headers["Authorization"].ToString();

? ? ? ? ? ? TokenModelJWT tm = JwtHelper.SerializeJWT(tokenHeader);//序列化token,獲取授權(quán)

? ? ? ? ? ? //授權(quán)varclaimList =newList();

? ? ? ? ? ? varclaim =new Claim(ClaimTypes.Role, tm.Role);

? ? ? ? ? ? claimList.Add(claim);

? ? ? ? ? ? varidentity =new ClaimsIdentity(claimList);

? ? ? ? ? ? varprincipal =new ClaimsPrincipal(identity);

? ? ? ? ? ? httpContext.User = principal;

? ? ? ? ? ? return _next(httpContext);

? ? ? ? }

2:JwtHelper 一個(gè)幫助類双絮,可以生成Token浴麻,和講Token反序列成model

publicclass JwtHelper

? ? {

? ? ? ? publicstaticstringsecretKey {get;set; } ="sdfsdfsrty45634kkhllghtdgdfss345t678fs";

? ? ? ? ////// 頒發(fā)JWT字符串

? ? ? ? /////////publicstaticstring IssueJWT(TokenModelJWT tokenModel)

? ? ? ? {

? ? ? ? ? ? vardateTime = DateTime.UtcNow;

? ? ? ? ? ? varclaims =new Claim[]

? ? ? ? ? ? {

? ? ? ? ? ? ? ? newClaim(JwtRegisteredClaimNames.Jti,tokenModel.Uid.ToString()),//IdnewClaim("Role", tokenModel.Role),//角色new Claim(JwtRegisteredClaimNames.Iat,dateTime.ToString(),ClaimValueTypes.Integer64)

? ? ? ? ? ? };

? ? ? ? ? ? //秘鑰varkey =new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtHelper.secretKey));

? ? ? ? ? ? varcreds =new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

? ? ? ? ? ? varjwt =new JwtSecurityToken(

? ? ? ? ? ? ? ? issuer: "Blog.Core",

? ? ? ? ? ? ? ? claims: claims, //聲明集合expires: dateTime.AddHours(2),

? ? ? ? ? ? ? ? signingCredentials: creds);

? ? ? ? ? ? varjwtHandler =new JwtSecurityTokenHandler();

? ? ? ? ? ? varencodedJwt = jwtHandler.WriteToken(jwt);

? ? ? ? ? ? return encodedJwt;

? ? ? ? }

? ? ? ? ////// 解析

? ? ? ? /////////publicstaticTokenModelJWT SerializeJWT(string jwtStr)

? ? ? ? {

? ? ? ? ? ? varjwtHandler =new JwtSecurityTokenHandler();

? ? ? ? ? ? JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);

? ? ? ? ? ? objectrole =newobject(); ;

? ? ? ? ? ? try? ? ? ? ? ? {

? ? ? ? ? ? ? ? jwtToken.Payload.TryGetValue("Role",out role);

? ? ? ? ? ? }

? ? ? ? ? ? catch (Exception e)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Console.WriteLine(e);

? ? ? ? ? ? ? ? throw;

? ? ? ? ? ? }

? ? ? ? ? ? vartm =new TokenModelJWT

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Uid = (jwtToken.Id).ObjToInt(),

? ? ? ? ? ? ? ? Role = role !=null? role.ObjToString() :"",

? ? ? ? ? ? };

? ? ? ? ? ? return tm;

? ? ? ? }

? ? }



3:定義中的TokenModelJWT,是一個(gè)令牌類囤攀,主要存儲(chǔ)個(gè)人角色信息软免,自己簡單寫一個(gè),也可以是你登陸的時(shí)候的用戶信息抚岗,或者其他或杠,

publicclass TokenModelJWT

? ? {

? ? ? ? ////// Id

? ? ? ? ///publiclongUid {get;set; }

? ? ? ? ////// 角色

? ? ? ? ///publicstringRole {get;set; }

? ? ? ? ////// 職能

? ? ? ? ///publicstringWork {get;set; }

? ? }


4:將四個(gè)文件都添加好后,最后兩步

1宣蔚、然后再Startup的Configure中向抢,將TokenAuth注冊(cè)中間件


2、在需要加權(quán)限的頁面中胚委,增加特性


這個(gè)時(shí)候挟鸠,你運(yùn)行項(xiàng)目,發(fā)現(xiàn)之前寫的都報(bào)錯(cuò)了亩冬,

圖 7

別慌艘希!是因?yàn)槊看尾僮髡?qǐng)求,都會(huì)經(jīng)過TokenAuth 中的Invoke方法硅急,方法中對(duì)Header信息進(jìn)行過濾覆享,因?yàn)楝F(xiàn)在Header中,并沒有相應(yīng)的配置信息营袜,看到這里撒顿,你就想到了,這個(gè)特別像我們常見的[HttpGet]等特性荚板,沒錯(cuò)凤壁!在.Net Core 中吩屹,到處都可以看到AOP編程,真的特別強(qiáng)大拧抖。

這個(gè)時(shí)候我們就用到了最開始的那個(gè)權(quán)限按鈕

煤搜,圖 8

沒錯(cuò)就是這里,但是我們方法寫好了唧席,那Token如何獲取呢擦盾,別急,我們新建一個(gè)LoginController袱吆,來模擬一次登陸操作厌衙,簡單傳遞幾個(gè)參數(shù),將用戶角色和緩存時(shí)間傳遞绞绒,然后生成Token婶希,并生成到緩存中,為之后做準(zhǔn)備蓬衡。


////// 獲取JWT的重寫方法喻杈,推薦這種,注意在文件夾OverWrite下

? ? ? ? //////id///角色///? ? ? ? [HttpGet]

? ? ? ? [Route("Token2")]

? ? ? ? publicJsonResult GetJWTStr(longid =1,stringsub ="Admin")

? ? ? ? {

? ? ? ? ? ? //這里就是用戶登陸以后狰晚,通過數(shù)據(jù)庫去調(diào)取數(shù)據(jù)筒饰,分配權(quán)限的操作TokenModelJWT tokenModel =new TokenModelJWT();

? ? ? ? ? ? tokenModel.Uid = id;

? ? ? ? ? ? tokenModel.Role = sub;

? ? ? ? ? ? stringjwtStr = JwtHelper.IssueJWT(tokenModel);

? ? ? ? ? ? return Json(jwtStr);

? ? ? ? }


這個(gè)時(shí)候我們就得到了我們的Token

圖 9

然后粘貼到我們的上圖權(quán)限窗口中,還記得么

圖 10

接下來壁晒,你再調(diào)用窗口瓷们,就發(fā)現(xiàn)都可以辣!




WHAT

這一篇呢秒咐,寫的比較潦草谬晕,主要是講如何使用,具體的細(xì)節(jié)知識(shí)携取,還是大家摸索攒钳,還是那句話,這里只是拋磚引玉的作用喲雷滋,通過閱讀本文不撑,你會(huì)了解到,什么是JWT晤斩,如何添加配置.net core 中間件焕檬,如何使用Token驗(yàn)證,在以后的項(xiàng)目里你就可以在登陸的時(shí)候澳泵,調(diào)用Token实愚,返回客戶端,然后判斷是否有相應(yīng)的接口權(quán)限。

NEXT

好啦爆侣!項(xiàng)目準(zhǔn)備階段就這么結(jié)束了,以后咱們就可以直接用swagger來調(diào)試了幢妄,而不是沒錯(cuò)都用F5運(yùn)行等兔仰,接下來我們就要正式開始搭建項(xiàng)目了,主要采用的是泛型倉儲(chǔ)模式 Repository+Service蕉鸳,也是一種常見的模式乎赴。

CODE

https://github.com/anjoy8/Blog.Core.git

QQ群:

867095512? ? ?(blod.core)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市潮尝,隨后出現(xiàn)的幾起案子榕吼,更是在濱河造成了極大的恐慌,老刑警劉巖勉失,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羹蚣,死亡現(xiàn)場離奇詭異,居然都是意外死亡乱凿,警方通過查閱死者的電腦和手機(jī)顽素,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來徒蟆,“玉大人胁出,你說我怎么就攤上這事《紊螅” “怎么了全蝶?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寺枉。 經(jīng)常有香客問我抑淫,道長,這世上最難降的妖魔是什么型凳? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任丈冬,我火速辦了婚禮,結(jié)果婚禮上甘畅,老公的妹妹穿的比我還像新娘埂蕊。我一直安慰自己,他們只是感情好疏唾,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布蓄氧。 她就那樣靜靜地躺著,像睡著了一般槐脏。 火紅的嫁衣襯著肌膚如雪喉童。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天顿天,我揣著相機(jī)與錄音堂氯,去河邊找鬼蔑担。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咽白,可吹牛的內(nèi)容都是我干的啤握。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼晶框,長吁一口氣:“原來是場噩夢啊……” “哼排抬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起授段,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤蹲蒲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后侵贵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體届搁,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年模燥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咖祭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蔫骂,死狀恐怖么翰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辽旋,我是刑警寧澤浩嫌,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站补胚,受9級(jí)特大地震影響码耐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜溶其,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一骚腥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瓶逃,春花似錦束铭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至昔汉,卻和暖如春懈万,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工会通, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留口予,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓涕侈,卻偏偏與公主長得像苹威,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子驾凶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容