AntDesign Pro + .NET Core 實(shí)現(xiàn)基于JWT的登錄認(rèn)證功能
很多同學(xué)說(shuō)AgileConfig的UI實(shí)在是太丑了。我想想也是的,本來(lái)這個(gè)項(xiàng)目是我自己使用的,一開(kāi)始甚至連UI都沒(méi)有,全靠手動(dòng)在數(shù)據(jù)庫(kù)里修改數(shù)據(jù)。后來(lái)加上了UI也是使用了老掉牙的bootstrap3做為基礎(chǔ)樣式。前臺(tái)框架也是使用了angularjs,同樣是老掉牙的東西。過(guò)年期間終于下決心翻新AgileConfig的前端UI。最后選擇的前端UI框架為AntDesign Pro + React。至于為啥選Ant-Design Pro是因?yàn)樗每矗伊餍?,選擇React是因?yàn)閂UE跟Angular我都略知一二,干脆趁此機(jī)會(huì)學(xué)一學(xué)React為何物,為何這么流行。
登錄的認(rèn)證方案為JWT,其實(shí)本人對(duì)JWT不太感冒(請(qǐng)看這里《我們真的需要jwt嗎?》),無(wú)奈大家都喜歡,那我也只能隨大流。
其實(shí)基于ant-design pro的界面我已經(jīng)翻的差不多了,因?yàn)樗С謒ock數(shù)據(jù),所以我一行后臺(tái)代碼都沒(méi)修改,已經(jīng)把界面快寫(xiě)完了。從現(xiàn)在開(kāi)始要真正的跟后端代碼進(jìn)行聯(lián)調(diào)了。那么我們先從登錄開(kāi)始吧。先看看后端asp.net core方面會(huì)如何進(jìn)行修改。
修改ASP.NET Core后端代碼
"JwtSetting": { "SecurityKey": "xxxxxxxxxxxx", // 密鑰 "Issuer": "agileconfig.admin", // 頒發(fā)者 "Audience": "agileconfig.admin", // 接收者 "ExpireSeconds": 20 // 過(guò)期時(shí)間 s }
在appsettings.json文件添加jwt相關(guān)配置。
public class JwtSetting { static JwtSetting() { Instance = new JwtSetting(); Instance.Audience = Global.Config["JwtSetting:Audience"]; Instance.SecurityKey = Global.Config["JwtSetting:SecurityKey"]; Instance.Issuer = Global.Config["JwtSetting:Issuer"]; Instance.ExpireSeconds = int.Parse(Global.Config["JwtSetting:ExpireSeconds"]); } public string SecurityKey { get; set; } public string Issuer { get; set; } public string Audience { get; set; } public int ExpireSeconds { get; set; } public static JwtSetting Instance { get; } }
定義一個(gè)JwtSetting類,用來(lái)讀取配置。
public void ConfigureServices(IServiceCollection services) { services.AddMemoryCache(); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = JwtSetting.Instance.Issuer, ValidAudience = JwtSetting.Instance.Audience, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSetting.Instance.SecurityKey)), }; }); services.AddCors(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0).AddRazorRuntimeCompilation(); services.AddFreeSqlDbContext(); services.AddBusinessServices(); services.AddAntiforgery(o => o.SuppressXFrameOptionsHeader = true); }
修改Startup文件的ConfigureServices方法,修改認(rèn)證Scheme為JwtBearerDefaults.AuthenticationScheme,在AddJwtBearer方法內(nèi)配置jwt相關(guān)配置信息。因?yàn)榍昂蠖朔蛛x項(xiàng)目所以有可能api跟ui部署在不同的域名下,所以開(kāi)啟Cors。
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseMiddleware<ExceptionHandlerMiddleware>(); } app.UseCors(op=> { op.AllowAnyOrigin(); op.AllowAnyMethod(); op.AllowAnyHeader(); }); app.UseWebSockets(new WebSocketOptions() { KeepAliveInterval = TimeSpan.FromSeconds(60), ReceiveBufferSize = 2 * 1024 }); app.UseMiddleware<WebsocketHandlerMiddleware>(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); }
修改Startup的Configure方法,配置Cors為Any。
public class JWT { public static string GetToken() { //創(chuàng)建用戶身份標(biāo)識(shí),可按需要添加更多信息 var claims = new Claim[] { new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim("id", "admin", ClaimValueTypes.String), // 用戶id new Claim("name", "admin"), // 用戶名 new Claim("admin", true.ToString() ,ClaimValueTypes.Boolean) // 是否是管理員 }; var key = Encoding.UTF8.GetBytes(JwtSetting.Instance.SecurityKey); //創(chuàng)建令牌 var token = new JwtSecurityToken( issuer: JwtSetting.Instance.Issuer, audience: JwtSetting.Instance.Audience, signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature), claims: claims, notBefore: DateTime.Now, expires: DateTime.Now.AddSeconds(JwtSetting.Instance.ExpireSeconds) ); string jwtToken = new JwtSecurityTokenHandler().WriteToken(token); return jwtToken; } }
添加一個(gè)JWT靜態(tài)類用來(lái)生成jwt的token。因?yàn)閍gileconfig的用戶只有admin一個(gè)所以這里用戶名,ID都直接寫(xiě)死。
[HttpPost("admin/jwt/login")] public async Task<IActionResult> Login4AntdPro([FromBody] LoginVM model) { string password = model.password; if (string.IsNullOrEmpty(password)) { return Json(new { status = "error", message = "密碼不能為空" }); } var result = await _settingService.ValidateAdminPassword(password); if (result) { var jwt = JWT.GetToken(); return Json(new { status="ok", token=jwt, type= "Bearer", currentAuthority = "admin" }); } return Json(new { status = "error", message = "密碼錯(cuò)誤" }); }
新增一個(gè)Action方法做為登錄的入口。在這里驗(yàn)證完密碼后生成token,并且返回到前端。
到這里.net core這邊后端代碼改動(dòng)的差不多了。主要是添加jwt相關(guān)的東西,這些內(nèi)容網(wǎng)上已經(jīng)寫(xiě)了很多了,不在贅述。
下面開(kāi)始修改前端代碼。
修改AntDesign Pro的代碼
AntDesign Pro已經(jīng)為我們生成好了登錄頁(yè)面,登錄的邏輯等,但是原來(lái)的登錄是假的,也不支持jwt token做為登錄憑證,下面我們要修改多個(gè)文件來(lái)完善這個(gè)登錄。
export function setToken(token:string): void { localStorage.setItem('token', token); } export function getToken(): string { var tk = localStorage.getItem('token'); if (tk) { return tk as string; } return ''; }
在utils/authority.ts文件內(nèi)新增2個(gè)方法,用來(lái)存儲(chǔ)跟獲取token。我們的jwt token存儲(chǔ)在localStorage里。
/** 配置request請(qǐng)求時(shí)的默認(rèn)參數(shù) */ const request = extend({ prefix: 'http://localhost:5000', errorHandler, // 默認(rèn)錯(cuò)誤處理 credentials: 'same-origin', // 默認(rèn)請(qǐng)求是否帶上cookie, }); const authHeaderInterceptor = (url: string, options: RequestOptionsInit) => { const authHeader = { Authorization: 'Bearer ' + getToken() }; return { url: `${url}`, options: { ...options, interceptors: true, headers: authHeader }, }; }; request.interceptors.request.use(authHeaderInterceptor);
修改utils/request.ts文件,定義一個(gè)添加Authorization頭部的攔截器,并且使用這個(gè)攔截器,這有每次請(qǐng)求的時(shí)候自動(dòng)會(huì)帶上這個(gè)頭部,把jwt token傳送到后臺(tái)。
設(shè)置prefix為http://localhost:5000這是我們的后端api的服務(wù)地址,真正生產(chǎn)的時(shí)候會(huì)替換為正式地址。
設(shè)置credentials為same-origin。
export async function accountLogin(params: LoginParamsType) { return request('/admin/jwt/login', { method: 'POST', data: params, }); }
在services/login.ts文件內(nèi)新增發(fā)起登錄請(qǐng)求的方法。
effects: { *login({ payload }, { call, put }) { const response = yield call(accountLogin, payload); yield put({ type: 'changeLoginStatus', payload: response, }); // Login successfully if (response.status === 'ok') { const urlParams = new URL(window.location.href); const params = getPageQuery(); message.success('🎉 🎉 🎉 登錄成功!'); let { redirect } = params as { redirect: string }; if (redirect) { console.log('redirect url ' , redirect); const redirectUrlParams = new URL(redirect); if (redirectUrlParams.origin === urlParams.origin) { redirect = redirect.substr(urlParams.origin.length); if (redirect.match(/^\/.*#/)) { redirect = redirect.substr(redirect.indexOf('#') + 1); } } else { window.location.href = '/'; return; } } history.replace(redirect || '/'); } }, reducers: { changeLoginStatus(state, { payload }) { setAuthority(payload.currentAuthority); setToken(payload.token) return { ...state, status: payload.status, type: payload.type, }; }, },
修改models/login.ts文件,修改effects的login方法,在內(nèi)部替換原來(lái)的fakeAccountLogin為accountLogin。同時(shí)修改reducers內(nèi)部的changeLoginStatus方法,添加setToken的代碼,這有修改后登錄成功后token就會(huì)被存儲(chǔ)起來(lái)。
effects: { *fetch(_, { call, put }) { const response = yield call(queryUsers); yield put({ type: 'save', payload: response, }); }, *fetchCurrent(_, { call, put }) { const response = { name: '管理員', userid: 'admin' }; yield put({ type: 'saveCurrentUser', payload: response, }); }, },
修改models/user.ts文件,修改effects的fetchCurrent方法為直接返回response。本來(lái)fetchCurrent是會(huì)去后臺(tái)拉當(dāng)前用戶信息的,因?yàn)閍gileconfig的用戶就admin一個(gè),所以我直接寫(xiě)死了。
讓我們?cè)囈幌碌卿洶桑海?br /> 源碼在這:https://github.com/kklldog/AgileConfig/tree/react_ui 🌟🌟🌟
到此這篇關(guān)于AntDesign Pro + .NET Core 實(shí)現(xiàn)基于JWT的登錄認(rèn)證的文章就介紹到這了,更多相關(guān).NET Core 登錄認(rèn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Asp.net 通用萬(wàn)級(jí)數(shù)據(jù)分頁(yè)代碼[修正下載地址]
在萬(wàn)級(jí)數(shù)據(jù)量下的分頁(yè)代碼2008-10-10asp.net動(dòng)態(tài)添加js文件調(diào)用到網(wǎng)頁(yè)的方法
這篇文章主要介紹了asp.net動(dòng)態(tài)添加js文件調(diào)用到網(wǎng)頁(yè)的方法,涉及asp.net動(dòng)態(tài)添加js的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04asp.net UrlReWriter使用經(jīng)驗(yàn)小結(jié)
UrlRewriter 是微軟封裝好了的一個(gè)URL重寫(xiě)組件。使用它可以讓我節(jié)約很多自已開(kāi)發(fā)的時(shí)間。 好了,開(kāi)始講述我的應(yīng)用經(jīng)驗(yàn),這只是很菜鳥(niǎo)的經(jīng)驗(yàn),高手就不用看了。2008-11-11根據(jù)Eval()函數(shù)綁定的值,來(lái)顯示GridView中的控件的方法
根據(jù)Eval()函數(shù)綁定的值,來(lái)顯示GridView中的控件的方法,需要的朋友可以參考一下2013-03-03asp.net中利用ashx實(shí)現(xiàn)圖片防盜鏈代碼
直接分析盜鏈原理:看下面用httpwatch截獲的http發(fā)送的數(shù)據(jù)2008-11-11ASP.NET連接 Access數(shù)據(jù)庫(kù)的幾種方法
這篇文章主要介紹了ASP.NET連接 Access數(shù)據(jù)庫(kù)的幾種方法,每種方法都非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友一起學(xué)習(xí)吧2016-08-08asp.net javascript 文件無(wú)刷新上傳實(shí)例代碼
最近在寫(xiě)C# .net代碼的時(shí)候,遇到一個(gè)上傳刷新的問(wèn)題。2009-06-06