• asp.net core使用identity+jwt保护你的webapi(二)——获取jwt token


    前言

    上一篇已经介绍了identity在web api中的基本配置,本篇来完成用户的注册,登录,获取jwt token。

    开始

    开始之前先配置一下jwt相关服务。

    配置JWT

    首先NuGet安装包:

    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.10" />
    

    appsettings.json中添加jwt配置:

    "JwtSettings": {
      "SecurityKey": "qP1yR9qH2xS0vW2lA3gI4nF0zA7fA3hB",
      "ExpiresIn": "00:10:00"
    }
    

    为了方便,新建一个配置类JwtSettings

    public class JwtSettings
    {
        public string SecurityKey { get; set; }
        public TimeSpan ExpiresIn { get; set; }
    }
    

    在Startup中配置jwt:

    public void ConfigureServices(IServiceCollection services)
    {
        //省略......
        
        var jwtSettings = Configuration.GetSection(nameof(JwtSettings)).Get<JwtSettings>();
        services.AddSingleton(jwtSettings);
        var tokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.SecurityKey)),
            ClockSkew = TimeSpan.Zero,
        };
        services
            .AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options => { options.TokenValidationParameters = tokenValidationParameters; });
    }
    

    最后别忘了UseAuthentication

    app.UseAuthentication(); // add
    app.UseAuthorization();
    

    结构搭建

    下面把项目基本结构搭建好,做好接口,后面实现:

    image-20210927232821355

    以下是各个类的定义:

    // 用户注册请求参数
    public class RegisterRequest
    {
        public string UserName { get; set; }
        
        public string Password { get; set; }
        
        public string Address { get; set; }
    }
    
    // 用户登录请求参数
    public class LoginRequest
    {
        public string UserName { get; set; }
        
        public string Password { get; set; }
    }
    
    // 注册 登录 成功后返回 token
    public class TokenResponse
    {
        [JsonPropertyName("access_token")] 
        public string AccessToken { get; set; }
        
        [JsonPropertyName("token_type")] 
        public string TokenType { get; set; }
    }
    
    // 登录 注册 失败时返回错误信息
    public class FailedResponse
    {
        public IEnumerable<string> Errors { get; set; }
    }
    
    // IUserService 接口
    public interface IUserService
    {
        Task<TokenResult> RegisterAsync(string username, string password, string address);
        
        Task<TokenResult> LoginAsync(string username, string password);
    }
    
    // UserService 实现
    public class UserService : IUserService
    {
        public Task<TokenResult> RegisterAsync(string username, string password, string address)
        {
            throw new System.NotImplementedException();
        }
        public Task<TokenResult> LoginAsync(string username, string password)
        {
            throw new System.NotImplementedException();
        }
    }
    
    // TokenResult 定义
    public class TokenResult
    {
        public bool Success => Errors == null || !Errors.Any();
        public IEnumerable<string> Errors { get; set; }
        
        public string AccessToken { get; set; }
        public string TokenType { get; set; }
    }
    

    最后是UserController

    [Route("api/[controller]")]
    [ApiController]
    public class UserController : ControllerBase
    {
        private readonly IUserService _userService;
        public UserController(IUserService userService)
        {
            _userService = userService;
        }
        
        [HttpPost("Register")]
        public async Task<IActionResult> Register(RegisterRequest request)
        {
            var result = await _userService.RegisterAsync(request.UserName, request.Password, request.Address);
            if (!result.Success)
            {
                return BadRequest(new FailedResponse()
                {
                    Errors = result.Errors
                });
            }
            return Ok(new TokenResponse
            {
                AccessToken = result.AccessToken,
                TokenType = result.TokenType
            });
        }
        
        [HttpPost("Login")]
        public async Task<IActionResult> Login(LoginRequest request)
        {
            var result = await _userService.LoginAsync(request.UserName, request.Password);
            if (!result.Success)
            {
                return Unauthorized(new FailedResponse()
                {
                    Errors = result.Errors
                });
            }
            
            return Ok(new TokenResponse
            {
                AccessToken = result.AccessToken,
                TokenType = result.TokenType
            });
        }
    }
    

    service实现

    上面已经做好了基本的结构,接下来就是实现UserService中的RegisterAsyncLoginAsync方法了。这里主要用到identity中的UserManagerUserManager封装了很多用户操作的现成方法。

    UserService中先做一个私有方法,根据user创建jwt token;用户注册,登录成功后调用此方法得到token返回即可:

    private TokenResult GenerateJwtToken(AppUser user)
    {
        var key = Encoding.ASCII.GetBytes(_jwtSettings.SecurityKey);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")),
                new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString())
            }),
            IssuedAt = DateTime.UtcNow,
            NotBefore = DateTime.UtcNow,
            Expires = DateTime.UtcNow.Add(_jwtSettings.ExpiresIn),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
                SecurityAlgorithms.HmacSha256Signature)
        };
        var jwtTokenHandler = new JwtSecurityTokenHandler();
        var securityToken = jwtTokenHandler.CreateToken(tokenDescriptor);
        var token = jwtTokenHandler.WriteToken(securityToken);
        return new TokenResult()
        {
            AccessToken = token,
            TokenType = "Bearer"
        };
    }
    

    注册方法实现:

    public async Task<TokenResult> RegisterAsync(string username, string password, string address)
    {
        var existingUser = await _userManager.FindByNameAsync(username);
        if (existingUser != null)
        {
            return new TokenResult()
            {
                Errors = new[] {"user already exists!"}, //用户已存在
            };
        }
        var newUser = new AppUser() {UserName = username, Address = address};
        var isCreated = await _userManager.CreateAsync(newUser, password);
        if (!isCreated.Succeeded)
        {
            return new TokenResult()
            {
                Errors = isCreated.Errors.Select(p => p.Description)
            };
        }
        return GenerateJwtToken(newUser);
    }
    

    登录方法实现:

    public async Task<TokenResult> LoginAsync(string username, string password)
    {
        var existingUser = await _userManager.FindByNameAsync(username);
        if (existingUser == null)
        {
            return new TokenResult()
            {
                Errors = new[] {"user does not exist!"}, //用户不存在
            };
        }
        var isCorrect = await _userManager.CheckPasswordAsync(existingUser, password);
        if (!isCorrect)
        {
            return new TokenResult()
            {
                Errors = new[] {"wrong user name or password!"}, //用户名或密码错误
            };
        }
        return GenerateJwtToken(existingUser);
    }
    

    最后,别忘了注册UserService

    services.AddScoped<IUserService, UserService>();
    

    swagger配置

    为了方便测试,可以配置一下swagger

    NuGet安装包:

    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
    

    ConfigureServices:

    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo
        {
            Title = "Sample.Api",
            Version = "v1",
            Description = "Sample.Api Swagger Doc"
        });
        c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
        {
            Description = "Input the JWT like: Bearer {your 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"
                    }
                },
                Array.Empty<string>()
            }
        });
    });
    
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Sample.Api v1"));
    

    测试一下

    image-20210927234957931

    随便输入abc进行注册,返回了一些密码规则的错误:

    image-20210927235203175

    这个规则在注册identity服务时可以配置:

    services.AddIdentityCore<AppUser>(options =>
    {
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = false;
        options.Password.RequireUppercase = false;
        options.Password.RequireNonAlphanumeric = false;
    }).AddEntityFrameworkStores<AppDbContext>();
    

    identityOptions还支持一些其他配置。

    下面注册成功后返回了token:

    image-20210927235821636

    使用刚刚注册的账号测试登录,也没有问题:

    image-20210927235940868

    最后

    本篇完成了identity的登录,注册,获取token,下一篇将介绍如何使用refresh token。

    参考:

    ASP.NET Core 简介 Identity | Microsoft Docs

    Mohamad Lawand - DEV Community

    ——本文使用【Typora】+【EasyBlogImageForTypora】编辑

    欢迎关注我的公众号,一起学习。

    如果本文对您有所帮助,您可以点击右下方的【推荐】按钮支持一下;文中如有不妥之处,还望指正,非常感谢!!!


    作者:xhznl

    出处:http://www.cnblogs.com/xhznl/

    文章可以转载,但请注明出处

  • 相关阅读:
    Microsoft Office 2007 Compatibility Pack
    BizTalk Server Memo, Part 2
    Database Engine Tuning Adviser: How to tune your new SQL Server 2005
    SSB FAQ (2) – Create a stored procedure as a service program
    TFS Change TFS server's service account
    SSB FAQ (1) – Target Queue cannot receive message
    SSB FAQ (3) – A rowset provider OPENXML
    GridView / RowCommand / e.CommandArgument
    Distinguished Fields vs. Promoted Properties in Biztalk
    Using a SQL Adapter in BizTalk Server 2004
  • 原文地址:https://www.cnblogs.com/xhznl/p/15406270.html
Copyright © 2020-2023  润新知