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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM