11

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

 3 years ago
source link: https://www.cnblogs.com/xhznl/p/15406270.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

上一篇已经介绍了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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK