StarBlog Development Notes: Authentication Feature
Authentication uses JWT. For AspNetCore authentication and JWT, you can check out my previous blog post First, install the NuGet package dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer Create an AuthService class in the Services directory. Leave it without writing any code for now; we'll come back after finishing the prep work. User model In the Models of the StarBlog.Data project
目录12 个章节
Authentication uses JWT. For AspNetCore authentication and JWT, you can check out my previous blog post.
First, install the NuGet package
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Create an AuthService class in the Services directory. Leave it without writing any code for now; we'll come back after finishing the prep work.
User Model
Create a User class in the Models directory of the StarBlog.Data project
The code is as follows
namespace StarBlog.Data.Models;
public class User {
public string Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
}
Configuration
Edit appsettings.json
Add the following configuration
Issueris the token issuer; I used this project's nameAudienceis the token recipient (consumer); I used the admin backend project's nameKeyis the password used to encrypt the token. Just generate one with a random password generator; note that the length should be a multiple of 8
"SecuritySettings": {
"Token": {
"Issuer": "starblog",
"Audience": "starblog-admin-ui",
"Key": "Pox40XC.D5>v^2B7+KAt%WfXaz0B6zC5"
}
}
PS: Putting the Key in the config file as shown in this article is not secure, especially in open-source projects. It's better to store this kind of secret data in environment variables. MSDN has more detailed materials on security; if you're going to production, you may want to look into that. To avoid adding complexity, I just took the lazy route and put the Key in appsettings.json. Sorry about that~
Configuration Model
Write an entity class to make it convenient to bind with the configuration
Create SecuritySettings.cs in the Models/Config directory. The code is as follows
namespace StarBlog.Web.Models.Config;
public class SecuritySettings {
public Token Token { get; set; }
}
public class Token {
public string Issuer { get; set; }
public string Audience { get; set; }
public string Key { get; set; }
}
Register Services
To keep the code in Program.cs clean, we register services using extension methods
Create ConfigureAppSettings.cs in the Extensions directory to bind configuration. The code is as follows
using StarBlog.Web.Models.Config;
namespace StarBlog.Web.Extensions;
public static class ConfigureAppSettings {
public static void AddSettings(this IServiceCollection services, IConfiguration configuration) {
// 安全配置
services.Configure<SecuritySettings>(configuration.GetSection(nameof(SecuritySettings)));
}
}
Next, create a ConfigureAuth.cs file to register some of the services needed for authentication. The code is as follows
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using StarBlog.Web.Models.Config;
using StarBlog.Web.Services;
namespace StarBlog.Web.Extensions;
public static class ConfigureAuth {
public static void AddAuth(this IServiceCollection services, IConfiguration configuration) {
services.AddScoped<AuthService>();
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => {
var secSettings = configuration.GetSection(nameof(SecuritySettings)).Get<SecuritySettings>();
options.TokenValidationParameters = new TokenValidationParameters {
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidIssuer = secSettings.Token.Issuer,
ValidAudience = secSettings.Token.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secSettings.Token.Key)),
ClockSkew = TimeSpan.Zero
};
});
}
}
AuthService in here hasn't been written yet, but no rush—let's register the services first
Edit the Program.cs file and add these two lines of code
builder.Services.AddSettings(builder.Configuration);
builder.Services.AddAuth(builder.Configuration);
Core Authentication Code
The AspNetCore framework already has authentication built in, so there's very little we need to do—we just need to generate a JWT token for the client.
Let's get started
Models
First, create two classes in the ViewModels directory: LoginToken and LoginUser.
The code is as follows
namespace StarBlog.Web.ViewModels;
public class LoginToken {
public string Token { get; set; }
public DateTime Expiration { get; set; }
}
namespace StarBlog.Web.ViewModels;
public class LoginUser {
public string Username { get; set; }
public string Password { get; set; }
}
AuthService
First, add the dependency injection
public class AuthService {
private readonly SecuritySettings _securitySettings;
private readonly IBaseRepository<User> _userRepo;
public AuthService(IOptions<SecuritySettings> options, IBaseRepository<User> userRepo) {
_securitySettings = options.Value;
_userRepo = userRepo;
}
}
Then we need to implement these features in AuthService
- Generate token
- Get user from the database
- Extract user info from the token
Let's go through them one by one
Generate Token
First is generating the token. The code is as follows
public LoginToken GenerateLoginToken(User user) {
var claims = new List<Claim> {
new("username", user.Name),
new(JwtRegisteredClaimNames.Name, user.Id), // User.Identity.Name
new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), // JWT ID
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_securitySettings.Token.Key));
var signCredential = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var jwtToken = new JwtSecurityToken(
issuer: _securitySettings.Token.Issuer,
audience: _securitySettings.Token.Audience,
claims: claims,
expires: DateTime.Now.AddDays(7),
signingCredentials: signCredential);
return new LoginToken {
Token = new JwtSecurityTokenHandler().WriteToken(jwtToken),
Expiration = TimeZoneInfo.ConvertTimeFromUtc(jwtToken.ValidTo, TimeZoneInfo.Local)
};
}
Get User from the Database
The code is as follows
public User? GetUserById(string userId) {
return _userRepo.Where(a => a.Id == userId).ToOne();
}
public User? GetUserByName(string name) {
return _userRepo.Where(a => a.Name == name).ToOne();
}
Extract User Info from the Token
Because the client submits the token to the server, and the JWT token contains user information, we can decrypt the user info directly from the token without accessing the database and wasting I/O performance.
The code is as follows
public User? GetUser(ClaimsPrincipal userClaim) {
var userId = userClaim.Identity?.Name;
var userName = userClaim.Claims.FirstOrDefault(c => c.Type == "username")?.Value;
if (userId == null || userName == null) return null;
return new User { Id = userId, Name = userName };
}
OK, the most critical part is done. Next up is writing a login API.
Login API
Nothing much here. Create AuthController.cs in the Apis directory, then here's the code
using Microsoft.AspNetCore.Mvc;
using StarBlog.Web.Services;
using StarBlog.Web.ViewModels;
using StarBlog.Web.ViewModels.Response;
namespace StarBlog.Web.Controllers;
[ApiController]
[Route("Api/[controller]")]
public class AuthController : ControllerBase {
private readonly AuthService _authService;
public AuthController(AuthService authService) {
_authService = authService;
}
[HttpPost]
public ApiResponse<LoginToken> Login(LoginUser loginUser) {
var user = _authService.GetUserByName(loginUser.Username);
if (user==null) return ApiResponse.NotFound(Response);
if(loginUser.Password != user.Password) return ApiResponse.Unauthorized(Response);
return new ApiResponse<LoginToken>(_authService.GenerateLoginToken(user));
}
}
Oh, and let's also add an endpoint to get the currently logged-in user's info.
The code is as follows
[Authorize]
[HttpGet]
public ActionResult<User> GetUser() {
var user = _authService.GetUser(User);
if (user == null) return NotFound();
return user;
}
Added the [Authorize] attribute, which means this API requires login to use
Swagger little green lock
It's actually Swagger's request filter. After you configure the token, you can attach the token when making requests to access APIs that require login.
First, install a new NuGet dependency
dotnet add package Swashbuckle.AspNetCore.Filters
This component's project URL: https://github.com/mattfrear/Swashbuckle.AspNetCore.Filters
To keep the Program.cs code as concise as possible, we still create a new extension method to hold the Swagger configuration
Create a new ConfigureSwagger class under the Extensions directory
The code is as follows
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
namespace StarBlog.Web.Extensions;
public static class ConfigureSwagger {
public static void AddSwagger(this IServiceCollection services) {
services.AddSwaggerGen(options => {
var security = new OpenApiSecurityScheme {
Description = "JWT模式授权,请输入 \"Bearer {Token}\" 进行身份验证",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey
};
options.AddSecurityDefinition("oauth2", security);
options.AddSecurityRequirement(new OpenApiSecurityRequirement { { security, new List<string>() } });
options.OperationFilter<AddResponseHeadersFilter>();
options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
options.OperationFilter<SecurityRequirementsOperationFilter>();
var filePath = Path.Combine(System.AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml");
options.IncludeXmlComments(filePath, true);
});
}
}
Then go back to the Program.cs file and just add this line of code
builder.Services.AddSwagger();
_EOF_
StarBlog Development Notes: Authentication Feature
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
基于.NetCore开发博客项目 StarBlog - (31) 发布和部署
部署?老生常谈。dotnet8的AOT吹得天花乱坠,StarBlog还没用上,只能选框架依赖或自包含。supervisor、docker、流水线,随便选,反正都一样麻烦。
2024-02-08 · StarBlog博客开发笔记 · 约 1 分钟
博客上线
终于上线啦! StarBlog博客项目从春节开始,跨越三个季节终于上线了~ 其实服务器这几天已经部署好了,然后我又测试了一会,暂时觉得OK,终于可以和大家分享了~ 博客地址是:blog.deali.cn 欢迎访问哈~ PS:友链模块即将完成,欢迎各位站长大佬们来交换友链~! 系列文章 顺便回顾一下已更新的12篇文章,接下来要介绍RESTFul接口和管理后台的前端开发了,欢迎继续关注~ 基于.Net
2022-06-20 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(3):模型设计
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetCore开发博客项目 StarBlog - (3) 模型设计 ... 基于.NetCore开发博客项目 StarBlog - 接口返回值包装 基于.NetCore开发博客项目 StarBlog - 上传
2022-04-22 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记:上传图片功能
后端实现,前端的后面说 可以先在swagger里测试 ImageSharp组件文档:https://docs.sixlabors.com/articles/imagesharp/gettingstarted.html service private string GetPhotoFilePath(Photo photo) { return Path.Combine(_environment
2022-02-28 · StarBlog博客开发笔记 · 约 1 分钟
讨论
共 0 条加载评论中…