文章目录
- 开启鉴权和授权
- Cookie登录
- 配置
- 测试
- 整合JWT
- 配置
- 测试
- 刷新token
- 添加UserRefreshTokenManager
- 修改login方法
- swagger添加jwt功能
开启鉴权和授权
在OnApplicationInitialization里添加鉴权和授权
app.UseRouting();app.UseAuthentication();//身份认证,我是谁app.UseAuthorization();//身份授权,我的权限app.UseConfiguredEndpoints();Cookie登录
适合mvc模式
配置
- 在ConfigureServices里添加cookie验证
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options=>{//登录失败后重定向页面//后面会跟着QueryParam ReturnUrl=?,表示从哪个链接调过来的//这样登录成功以后就可以调到原页面了options.LoginPath="/Home/Index";// 设置Cookie的过期时间为7天options.ExpireTimeSpan=TimeSpan.FromDays(3);//打开滑动延期,实现连续3天不登录,cookie自动删除,要求用户重新登录options.SlidingExpiration=true;});- 登录方法
[HttpPost]publicasyncTask<IActionResult>Login([FromBody]Useruser){//添加用户名密码验证逻辑varclaims=newList<Claim>{new(ClaimTypes.Name,"me"),};varroleList=newList<string>{"Admin"};//从数据库拿到用户所有的角色foreach(varroleinroleList){claims.Add(new(ClaimTypes.Role,role));}varclaimsIdentity=newClaimsIdentity(claims,CookieAuthenticationDefaults.AuthenticationScheme);varrememberMe=true;varauthProperties=newAuthenticationProperties{IsPersistent=rememberMe,// 根据用户选择是否记住登录状态设置过期时间ExpiresUtc=rememberMe?DateTimeOffset.UtcNow.AddDays(7):null//token的有效期为7天};awaitHttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,newClaimsPrincipal(claimsIdentity),authProperties);returnRedirectToAction("Index");//登录成功后跳回首页}- 简单的登录页面
<formaction="@Url.Action("Login","Home")"method="post">@Html.AntiForgeryToken()<buttontype="submit">登录</button></form>- 将ICurrentUser注入进controller
publicclassHomeController:Controller{privatereadonlyILogger<HomeController>_logger;privatereadonlyICurrentUser_currentUser;publicHomeController(ILogger<HomeController>logger,ICurrentUseruser){_logger=logger;_currentUser=user;}- 做一个需要Admin权限的页面
[Authorize(Roles="Admin,Staff")]//在一起逗号分割是“或”//[Authorize(Roles = "Staff")] 另起一行是“且”publicIActionResultAdminAllow(){_logger.LogInformation("进来了");_logger.LogInformation($"当前用户是{_currentUser.UserName}");//拿到了mereturnRedirectToAction("Index");}测试
- 进入首页后直接访问http://localhost:5062/Home/AdminAllow
- 被重定向了
- 点击登录按钮,登录成功后返回首页,查看cookie
- 查看log日志
整合JWT
添加Microsoft.AspNetCore.Authentication.JwtBearer包
配置
- 在appsettings.json里添加JWT配置
"JwtSettings":{"SecretKey":"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ","Issuer":"your_issuer","Audience":"your_audience","ExpireMinutes":60}- 在module里创建ConfigureAuthentication封装验证配置
privatevoidConfigureAuthentication(IServiceCollectionservices,IConfigurationconfig){services.AddAuthentication(options=>{options.DefaultScheme="DualScheme";}).AddPolicyScheme("DualScheme","Cookie or JWT",options=>{options.ForwardDefaultSelector=context=>{// 根据请求头或路径选择认证方式if(context.Request.Headers.Authorization.Any(x=>x.StartsWith("Bearer "))){returnJwtBearerDefaults.AuthenticationScheme;}else{returnCookieAuthenticationDefaults.AuthenticationScheme;}};}).AddCookie(options=>{//登录失败后重定向页面//后面会跟着QueryParam ReturnUrl=?,表示从哪个链接调过来的//这样登录成功以后就可以调到原页面了options.LoginPath="/Home/Index";// 设置Cookie的过期时间为3天options.ExpireTimeSpan=TimeSpan.FromDays(3);//打开滑动延期,实现连续3天不登录,cookie自动删除,要求用户重新登录options.SlidingExpiration=true;}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme,options=>{options.TokenValidationParameters=newTokenValidationParameters{ValidateIssuer=true,ValidateAudience=true,ValidateLifetime=true,ValidateIssuerSigningKey=true,ValidIssuer=config["JwtSettings:Issuer"],ValidAudience=config["JwtSettings:Audience"],IssuerSigningKey=newSymmetricSecurityKey(Encoding.UTF8.GetBytes(config["JwtSettings:SecretKey"])),// 确保角色声明正确映射RoleClaimType=ClaimTypes.Role};});}- ConfigureServices里调用ConfigureAuthentication
publicoverridevoidConfigureServices(ServiceConfigurationContextcontext){varservices=context.Services;varconfig=services.GetConfiguration();ConfigureAuthentication(services,config);ConfigureSwagger(services);}- 创建登录API
publicIActionResultPostLogin(){varid=GuidGenerator.Create().ToString();varclaims=new[]{newClaim(AbpClaimTypes.UserName,"me"),//用户名newClaim(AbpClaimTypes.Role,"Admin"),//角色可多个newClaim(AbpClaimTypes.Role,"SuperUser"),newClaim(AbpClaimTypes.UserId,id)//user id};varkey=newSymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JwtSettings:SecretKey"]));varcredentials=newSigningCredentials(key,SecurityAlgorithms.HmacSha256);//因为256,所以字符串长度至少32位varminutes=int.Parse(_config["JwtSettings:ExpireMinutes"]);vartoken=newJwtSecurityToken(issuer:_config["JwtSettings:Issuer"],audience:_config["JwtSettings:Audience"],claims:claims,expires:DateTime.UtcNow.AddMinutes(minutes),signingCredentials:credentials);returnnewJsonResult(new{token=newJwtSecurityTokenHandler().WriteToken(token),id=id});//可以显式地传给前端,也可以让前端自己去解token}- 创建带权限验证的API
[Authorize(Roles="Admin")]publicIActionResultGetAccess(){Logger.LogDebug(JsonConvert.SerializeObject(CurrentUser,Formatting.Indented));}测试
- 访问接口获取token
- 带token访问
刷新token
添加UserRefreshTokenManager
这里使用EasyCaching.SQLite做缓存框架。正式项目可以用IDistributedCache,稍作修改即可。
publicclassUserRefreshTokenManager:DomainService{privatereadonlyIConfiguration_config;privatereadonlyIGuidGenerator_guidGenerator;privatereadonlyIEasyCachingProvider_cachingProvider;publicUserRefreshTokenManager(IConfigurationconfig,IGuidGeneratorguidGenerator,IEasyCachingProvidercachingProvider){_config=config;_guidGenerator=guidGenerator;_cachingProvider=cachingProvider;}publicasyncTask<Guid>GenerateRefreshToken(stringusername){varrefreshTokenStr=_guidGenerator.Create();varexpireTime=TimeSpan.FromHours(int.Parse(_config["JwtSettings:RefreshTokenExpireHours"]);await_cachingProvider.SetAsync(username,refreshTokenStr,expireTime);returnrefreshTokenStr;}publicstringGenerateToken(stringusername){varclaims=new[]{newClaim(ClaimTypes.Name,"me"),newClaim(ClaimTypes.Role,"Admin")};//用户示例varkey=newSymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JwtSettings:SecretKey"]));varcredentials=newSigningCredentials(key,SecurityAlgorithms.HmacSha256);//因为256,所以字符串长度至少32位varminutes=int.Parse(_config["JwtSettings:ExpireMinutes"]);vartoken=newJwtSecurityToken(issuer:_config["JwtSettings:Issuer"],audience:_config["JwtSettings:Audience"],claims:claims,expires:DateTime.UtcNow.AddMinutes(minutes),signingCredentials:credentials);returnnewJwtSecurityTokenHandler().WriteToken(token);}publicasyncTask<(string,Guid)>RefreshTokenAsync(GuidtokenStr,stringusername){varoldToken=await_cachingProvider.GetAsync<Guid>(username);if(!oldToken.HasValue||oldToken.Value!=tokenStr){thrownewUserFriendlyException($"Refresh token{tokenStr}is invalid!");}else{varnewToken=GenerateToken(username);varnewRefreshToken=awaitGenerateRefreshToken(username);return(newToken,newRefreshToken);}}}修改login方法
publicvirtualasyncTask<IActionResult>PostLogin(){varuserQ=await_userDb.GetQueryableAsync();varuser=userQ.Where(u=>u.Username=="Admin").FirstOrDefault();vartoken=_userRefreshTokenManager.GenerateToken(user.Username);varrefreshToken=await_userRefreshTokenManager.GenerateRefreshToken(user.Username);vartokenResponse=new{token,refreshToken,username=user.Username,};returnnewJsonResult(tokenResponse);}publicvirtualasyncTask<IActionResult>PostRefreshToken([FromForm]GuidoldToken,[FromForm]stringusername){var(token,refreshToken)=await_userRefreshTokenManager.RefreshTokenAsync(oldToken,username);vartokenResponse=new{token,refreshToken,username,};returnnewJsonResult(tokenResponse);}前端存储这两个token
swagger添加jwt功能
修改AddAbpSwaggerGen方法
services.AddAbpSwaggerGen(options=>{options.SwaggerDoc("v1",newOpenApiInfo{Title="Test API",Version="v1"});options.DocInclusionPredicate((docName,description)=>true);options.CustomSchemaIds(type=>type.FullName);#region登录验证options.AddSecurityDefinition("Bearer",newOpenApiSecurityScheme{Name="Authorization",Type=SecuritySchemeType.Http,Scheme="Bearer",BearerFormat="JWT",In=ParameterLocation.Header,Description="请输入 JWT Token"});// 全局应用该安全方案(可选:也可以只对特定操作应用)options.AddSecurityRequirement(newOpenApiSecurityRequirement{{newOpenApiSecurityScheme{Reference=newOpenApiReference{Type=ReferenceType.SecurityScheme,Id="Bearer"}},Array.Empty<string>()}});#endregion});重启项目,发现swagger页面右上角多了一个Authorize按钮
在点击后的弹窗内直接输入token
点Authorize会把token存在前端,每次访问的时候都带上。