AntDesign Pro + .NET Core 实现基于JWT的登录认证功能
很多同学说AgileConfig的UI实在是太丑了。我想想也是的,本来这个项目是我自己使用的,一开始甚至连UI都没有,全靠手动在数据库里修改数据。后来加上了UI也是使用了老掉牙的bootstrap3做为基础样式。前台框架也是使用了angularjs,同样是老掉牙的东西。过年期间终于下决心翻新AgileConfig的前端UI。最后选择的前端UI框架为AntDesign Pro + React。至于为啥选Ant-Design Pro是因为他好看,而且流行,选择React是因为VUE跟Angular我都略知一二,干脆趁此机会学一学React为何物,为何这么流行。
登录的认证方案为JWT,其实本人对JWT不太感冒(请看这里《我们真的需要jwt吗?》),无奈大家都喜欢,那我也只能随大流。
其实基于ant-design pro的界面我已经翻的差不多了,因为它支持mock数据,所以我一行后台代码都没修改,已经把界面快写完了。从现在开始要真正的跟后端代码进行联调了。那么我们先从登录开始吧。先看看后端asp.net core方面会如何进行修改。
修改ASP.NET Core后端代码
"JwtSetting": { "SecurityKey": "xxxxxxxxxxxx", // 密钥 "Issuer": "agileconfig.admin", // 颁发者 "Audience": "agileconfig.admin", // 接收者 "ExpireSeconds": 20 // 过期时间 s }
在appsettings.json文件添加jwt相关配置。
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; } }
定义一个JwtSetting类,用来读取配置。
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方法,修改认证Scheme为JwtBearerDefaults.AuthenticationScheme,在AddJwtBearer方法内配置jwt相关配置信息。因为前后端分离项目所以有可能api跟ui部署在不同的域名下,所以开启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() { //创建用户身份标识,可按需要添加更多信息 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); //创建令牌 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; } }
添加一个JWT静态类用来生成jwt的token。因为agileconfig的用户只有admin一个所以这里用户名,ID都直接写死。
[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 = "密码错误" }); }
新增一个Action方法做为登录的入口。在这里验证完密码后生成token,并且返回到前端。
到这里.net core这边后端代码改动的差不多了。主要是添加jwt相关的东西,这些内容网上已经写了很多了,不在赘述。
下面开始修改前端代码。
修改AntDesign Pro的代码
AntDesign Pro已经为我们生成好了登录页面,登录的逻辑等,但是原来的登录是假的,也不支持jwt token做为登录凭证,下面我们要修改多个文件来完善这个登录。
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文件内新增2个方法,用来存储跟获取token。我们的jwt token存储在localStorage里。
/** 配置request请求时的默认参数 */ const request = extend({ prefix: 'http://localhost:5000', errorHandler, // 默认错误处理 credentials: 'same-origin', // 默认请求是否带上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文件,定义一个添加Authorization头部的拦截器,并且使用这个拦截器,这有每次请求的时候自动会带上这个头部,把jwt token传送到后台。
设置prefix为http://localhost:5000这是我们的后端api的服务地址,真正生产的时候会替换为正式地址。
设置credentials为same-origin。
export async function accountLogin(params: LoginParamsType) { return request('/admin/jwt/login', { method: 'POST', data: params, }); }
在services/login.ts文件内新增发起登录请求的方法。
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方法,在内部替换原来的fakeAccountLogin为accountLogin。同时修改reducers内部的changeLoginStatus方法,添加setToken的代码,这有修改后登录成功后token就会被存储起来。
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。本来fetchCurrent是会去后台拉当前用户信息的,因为agileconfig的用户就admin一个,所以我直接写死了。
让我们试一下登录吧:)
源码在这:https://github.com/kklldog/AgileConfig/tree/react_ui 🌟🌟🌟
到此这篇关于AntDesign Pro + .NET Core 实现基于JWT的登录认证的文章就介绍到这了,更多相关.NET Core 登录认证内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!