zoukankan      html  css  js  c++  java
  • ASP.NET Core 3.1 WebApi+JWT+Swagger+EntityFrameworkCore构建REST API

    一、准备

    • 使用vs2019新建ASP.NET Core Web应用程序,选用api模板:
    • 安装相关的NuGet包:

    二、编码

    • 首先编写数据库模型:

      用户表 User.cs:
    public class User
        {
            [Key]
            public Guid ID { get; set; }
    
            [Required]
            [Column(TypeName = "VARCHAR(16)")]
            public string UserName { get; set; }
    
            [Required]
            [Column(TypeName = "VARCHAR(16)")]
            public string Password { get; set; }
        }
    

    数据库上下文 DemoContext.cs,在数据库创建时增加一条种子数据admin:

    public class DemoContext : DbContext
        {
            public DemoContext(DbContextOptions<DemoContext> options)
                : base(options)
            {
    
            }
    
            public DbSet<User> Users { get; set; }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
                modelBuilder.Entity<User>().HasData(new User
                {
                    ID = Guid.Parse("94430DDF-E6E1-4836-A7D2-49A9FCEF722E"),
                    UserName = "admin",
                    Password = "123456"
                });
            }
        }
    
    • 编写数据访问服务:

      IUserService接口,这里简单定义几个添加查询的方法:
    public interface IUserService
        {
            Task<IEnumerable<User>> GetUserAsync();
    
            Task<User> GetUserAsync(Guid id);
    
            Task<User> GetUserAsync(string username, string password);
    
            Task<User> AddUserAsync(string username, string password);
        }
    

    UserService实现类:

    public class UserService : IUserService
        {
            private readonly DemoContext context;
    
            public UserService(DemoContext context)
            {
                this.context = context ?? throw new ArgumentNullException(nameof(context));
            }
    
            public async Task<User> AddUserAsync(string username, string password)
            {
                User user = new User();
                user.ID = Guid.NewGuid();
                user.UserName = username;
                user.Password = password;
                await context.Users.AddAsync(user);
                context.SaveChanges();
                return user;
            }
    
            public async Task<User> GetUserAsync(string username, string password)
            {
                return await context.Users.FirstOrDefaultAsync(p => p.UserName == username && p.Password == password);
            }
    
            public async Task<IEnumerable<User>> GetUserAsync()
            {
                return await context.Users.ToListAsync();
            }
    
            public async Task<User> GetUserAsync(Guid id)
            {
                return await context.Users.FirstOrDefaultAsync(p => p.ID == id);
            }
    
        }
    
    • appsettings.json中增加jwt,efcore相关的配置 JwtSetting、ConnectionStrings:
    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
      "AllowedHosts": "*",
      "JwtSetting": {
        "SecurityKey": "88d082e6-5672-4c6c-bc42-6fcce20fbf51", // 密钥
        "Issuer": "jwtIssuertest", // 颁发者
        "Audience": "jwtAudiencetest", // 接收者
        "ExpireSeconds": 3600 // 过期时间(3600)
      },
      "ConnectionStrings": {
        "DemoContext": "data source=.;Initial Catalog=WebApiDemoDB;User ID=sa;Password=123456;MultipleActiveResultSets=True;App=EntityFramework"
      }
    }
    
    • 增加jwt配置对象:
        /// <summary>
        /// jwt配置对象
        /// </summary>
        public class JwtSetting
        {
            public string SecurityKey { get; set; }
            public string Issuer { get; set; }
            public string Audience { get; set; }
            public int ExpireSeconds { get; set; }
        }
    
    public static class AppSettings
        {
            public static JwtSetting JwtSetting { get; set; }
    
            /// <summary>
            /// 初始化jwt配置
            /// </summary>
            /// <param name="configuration"></param>
            public static void Init(IConfiguration configuration)
            {
                JwtSetting = new JwtSetting();
                configuration.Bind("JwtSetting", JwtSetting);
            }
        }
    
    • 在Startup.cs中配置相关服务和中间件:
    public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                AppSettings.Init(Configuration);
    
                services.AddSwaggerGen(c =>
                {
                    c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
                    // Set the comments path for the Swagger JSON and UI.
                    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                    c.IncludeXmlComments(xmlPath);
                    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
                    {
                        Description = "在下框中输入请求头中需要添加Jwt授权Token:Bearer Token",
                        Name = "Authorization",
                        In = ParameterLocation.Header,
                        Type = SecuritySchemeType.ApiKey,
                        BearerFormat = "JWT",
                        Scheme = "Bearer"
                    });
    
                    c.AddSecurityRequirement(new OpenApiSecurityRequirement
                        {
                            {
                                new OpenApiSecurityScheme{
                                    Reference = new OpenApiReference {
                                                Type = ReferenceType.SecurityScheme,
                                                Id = "Bearer"}
                               },new string[] { }
                            }
                        });
                });
    
                services
                  .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                  .AddJwtBearer(options =>
                  {
                      options.TokenValidationParameters = new TokenValidationParameters
                      {
                          ValidIssuer = AppSettings.JwtSetting.Issuer,
                          ValidAudience = AppSettings.JwtSetting.Audience,
                          IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AppSettings.JwtSetting.SecurityKey)),
                          // 默认允许 300s  的时间偏移量,设置为0
                          ClockSkew = TimeSpan.Zero,
                      };
                  });
    
                services.AddCors(options =>
                {
                    options.AddPolicy("any",
                        builder =>
                        {
                            builder.AllowAnyMethod()
                                .AllowAnyOrigin()
                                .AllowAnyHeader();
                        });
                });
    
                services.AddControllers();
                services.AddScoped<IUserService, UserService>();
                services.AddDbContext<DemoContext>(opt => opt.UseSqlServer(Configuration.GetConnectionString("DemoContext")));
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                app.UseAuthentication();
    
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseSwagger();
                app.UseSwaggerUI(c =>
                {
                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
                });
    
                app.UseRouting();
    
                app.UseAuthorization();
    
                //CORS 中间件必须配置为在对 UseRouting 和 UseEndpoints的调用之间执行。 配置不正确将导致中间件停止正常运行。
                app.UseCors("any");
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    
    • 打开项目文件,增加项目xml文档生成配置,swagger需要用到:
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <NoWarn>$(NoWarn);1591</NoWarn>
    

    • 数据库迁移:
      打开程序包管理控制台:执行命令Add-Migration Initial

      然后执行Update-Database

      此时数据库已经成功生成:
    • 下面是controller:

      先建一个数据传输实体,方便统一controller的返回值:
    public class BaseDto<T>
        {
            public BaseDto(StatusCode code, string message)
            {
                Code = code;
                Message = message;
            }
    
            public BaseDto(StatusCode code, string message, T data)
            {
                Code = code;
                Message = message;
                Data = data;
            }
    
            public StatusCode Code { get; set; }
    
            public string Message { get; set; }
    
            public T Data { get; set; }
        }
    
        public enum StatusCode
        {
            Success = 0,
            Error = 1,
        }
    

    UserController:

    /// <summary>
        /// 用户
        /// </summary>
        [Authorize]
        [Route("api/[controller]")]
        [ApiController]
        public class UserController : ControllerBase
        {
            private readonly IUserService userService;
    
            public UserController(IUserService userService)
            {
                this.userService = userService;
            }
    
            /// <summary>
            /// 所有用户
            /// </summary>
            /// <returns></returns>
            [Route("")]
            [HttpGet]
            public async Task<ActionResult<BaseDto<IEnumerable<User>>>> Get()
            {
                var users = await userService.GetUserAsync();
                BaseDto<IEnumerable<User>> dto = new BaseDto<IEnumerable<User>>(Dto.StatusCode.Success, "", users);
                return Ok(dto);
            }
    
            /// <summary>
            /// 当前用户
            /// </summary>
            /// <returns></returns>
            [Route("me")]
            [HttpGet]
            public async Task<ActionResult<BaseDto<User>>> UserInfo()
            {
                string id = User.FindFirst("id")?.Value;
                var user = await userService.GetUserAsync(Guid.Parse(id));
                BaseDto<User> dto = new BaseDto<User>(Dto.StatusCode.Success, "", user);
                return Ok(dto);
            }
    
            /// <summary>
            /// 根据ID获取用户
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            [Route("{id}")]
            [HttpGet]
            public async Task<ActionResult<BaseDto<User>>> Get(Guid id)
            {
                var user = await userService.GetUserAsync(id);
                BaseDto<User> dto = new BaseDto<User>(Dto.StatusCode.Success, "", user);
                return Ok(dto);
            }
    
            /// <summary>
            /// 添加用户
            /// </summary>
            /// <param name="loginParameter"></param>
            /// <returns></returns>
            [HttpPost]
            public async Task<ActionResult<BaseDto<User>>> Add(LoginParameter loginParameter)
            {
                var user = await userService.AddUserAsync(loginParameter.UserName, loginParameter.Password);
                BaseDto<User> dto = new BaseDto<User>(Dto.StatusCode.Success, "", user);
                return Ok(dto);
            }
        }
    
        public class LoginParameter
        {
            public string UserName { get; set; }
    
            public string Password { get; set; }
        }
    

    TokenController:

    /// <summary>
        /// 鉴权
        /// </summary>
        [Route("api/[controller]")]
        [ApiController]
        public class TokenController : ControllerBase
        {
            private readonly IUserService userService;
    
            public TokenController(IUserService userService)
            {
                this.userService = userService;
            }
    
            /// <summary>
            /// 获取token
            /// </summary>
            /// <param name="loginParameter"></param>
            /// <returns></returns>
            [AllowAnonymous]
            [HttpPost(Name = nameof(Login))]
            public async Task<ActionResult<BaseDto<object>>> Login([FromBody]LoginParameter loginParameter)
            {
                var user = await userService.GetUserAsync(loginParameter.UserName, loginParameter.Password);
                if (user != null)
                {
                    var token = AppHelper.Instance.GetToken(user);
                    BaseDto<object> dto = new BaseDto<object>(Dto.StatusCode.Success, "", new { token });
                    return Ok(dto);
                }
                return Ok(new BaseDto<object>(Dto.StatusCode.Error, "", null));
            }
        }
    

    AppHelper中生成token的方法:

    public class AppHelper
        {
            public readonly static AppHelper Instance = new AppHelper();
    
            private AppHelper() { }
    
            /// <summary>
            /// 生成token
            /// </summary>
            /// <param name="user"></param>
            /// <returns></returns>
            public string GetToken(User user)
            {
                //创建用户身份标识,可按需要添加更多信息
                var claims = new Claim[]
                {
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim("id", user.ID.ToString(), ClaimValueTypes.Integer32), // 用户id
                    new Claim("name", user.UserName), // 用户名
                };
    
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AppSettings.JwtSetting.SecurityKey));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
                //创建令牌
                var token = new JwtSecurityToken(
                  issuer: AppSettings.JwtSetting.Issuer,
                  audience: AppSettings.JwtSetting.Audience,
                  signingCredentials: creds,
                  claims: claims,
                  notBefore: DateTime.Now,
                  expires: DateTime.Now.AddSeconds(AppSettings.JwtSetting.ExpireSeconds)
                );
    
                string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
    
                return jwtToken;
            }
    
        }
    

    三、效果

    运行项目,浏览器访问:

    测试一下用户接口:

    这时返回401错误,因为我们还没有鉴权
    使用admin/123456获取token:

    拿到token 点击authorize:

    然后再测试用户接口:

    此时已经可以正常请求。
    代码:https://github.com/xiajingren/NetCore3.1-WebApi-Demo

  • 相关阅读:
    Nuget 多平台多目标快速自动打包
    .Net Core 环境下构建强大且易用的规则引擎
    .Net 4.X 提前用上 .Net Core 的配置模式以及热重载配置
    [搬运] DotNetAnywhere:可供选择的 .NET 运行时
    [搬运] .NET Core 2.1中改进的堆栈信息
    [开源]OSharpNS 步步为营系列
    [开源]OSharpNS 步步为营系列
    [开源]OSharpNS 步步为营系列
    [开源]OSharpNS 步步为营系列
    [开源]OSharpNS 步步为营系列
  • 原文地址:https://www.cnblogs.com/xhznl/p/12922690.html
Copyright © 2011-2022 走看看