LoginSignup
0
4

More than 1 year has passed since last update.

ASP.NET Identityを使ったユーザー管理APIの作成

Last updated at Posted at 2021-10-10

概要

ASP.NET Identityを使ったユーザー管理機能を作成します。

作成物:https://github.com/koki-2424/dotnet-identity-sample

前提

  • .NET 5.x
  • dotnet cli

事前準備

dotnet new api  -o dotnet-identity-sample 
cd dotnet-identity-sample 

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Design

dotnet add package Microsoft.EntityFrameworkCore.Sqlite

データベース

データベースはSQLiteを使用し、マイグレーションまでを実施します。

appsetting.jsonを編集

{
  "ConnectionStrings": {
    "DefaultConnection": "DataSource=app.db;Cache=Shared"
  }
}

ApplicationDbContext.csを作成
IdentityDbContextを継承することでIdentity用のクラスを使うことができる。

namespace dotnet_identity_sample.Data
{
    public class ApplicationDbContext : IdentityDbContext<AppUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
}

DIコンテナに先程作成したApplicationDbContextを設定する。

この設定はマストではありません。
参考:Entity Framework の DB 接続の解放について、 using した場合と DI コンテナを使った場合の違い。

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddDbContext<ApplicationDbContext>(options =>
            options
                .UseSqlite(Configuration
                    .GetConnectionString("DefaultConnection")));
}

以下のコマンドを実行しテーブルの作成をします。

dotnet ef migrations add InitialCreate
dotnet ef database update

 認証サービスの作成

Token発行のService、TokenService.csを作成

namespace dotnet_identity_sample.Services
{
    public class TokenService
    {
        public string CreateToken(AppUser user){
          var claims = new List<Claim>{
            new Claim(ClaimTypes.Name, user.UserName),
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Email, user.Email)
          };

          // ToDo: set a secret string
          var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("super secret key"));
          var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);

          var tokenDescriptor = new SecurityTokenDescriptor{
            Subject = new ClaimsIdentity(claims),
            Expires = System.DateTime.Now.AddDays(7),
            SigningCredentials = creds
          };

          var tokenHandler = new JwtSecurityTokenHandler();

          var token = tokenHandler.CreateToken(tokenDescriptor);

          return tokenHandler.WriteToken(token);
        }
    }
}

認証用の拡張サービスを作成。作成した拡張サービスをStartup.csのDIコンテナに設定する

IdentityServiceExtension.csを作成

namespace dotnet_identity_sample.Extensions
{
  public static class IdentityServiceExtensions
  {
    public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration config)
    {
      services.AddIdentityCore<AppUser>(opt =>
      {
        opt.Password.RequireNonAlphanumeric = false;
      })
      .AddEntityFrameworkStores<ApplicationDbContext>()
      .AddSignInManager<SignInManager<AppUser>>();

      var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("super secret key"));

      services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
      .AddJwtBearer(opt =>
      {
        opt.TokenValidationParameters = new TokenValidationParameters
        {
          ValidateIssuerSigningKey = true,
          IssuerSigningKey = key,
          ValidateIssuer = false,
          ValidateAudience = false
        };
      });
      services.AddScoped<TokenService>();

      return services;
    }
  }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentityServices(Configuration);
}

作成したServiceをControllerで使用する。

DTOの作成

  public class LoginDto
  {
    public string Email { get; set; }
    public string Password { get; set; }
  }

  public class RegisterDto
  {
    [Required]
    [EmailAddress]
    public string Email { get; set; }
    [Required]
    public string Password { get; set; }
    public string UserName { get; set; }
  }

  public class UserDto
  {
    public string Id { get; set; }
    public string DisplayName { get; set; }
    public string Token { get; set; }
    public string UserName { get; set; }
    public string Iamge { get; set; }
  }

Controllerの作成

namespace dotnet_identity_sample.Controllers
{
  [ApiController]
  [Route("api/[controller]")]
  public class AccountController : ControllerBase
  {
    private readonly UserManager<AppUser> _userManager;
    private readonly TokenService _tokenService;

    private readonly SignInManager<AppUser> _signInManager;
    public AccountController(
    UserManager<AppUser> userManager,
    SignInManager<AppUser> signInManager,
    TokenService tokenService)
    {
      _tokenService = tokenService;
      _userManager = userManager;
      _signInManager = signInManager;
    }

    [HttpPost("login")]
    public async Task<ActionResult<UserDto>> Login(LoginDto loginDto)
    {
      var user = await _userManager.FindByEmailAsync(loginDto.Email);

      if (user == null) return Unauthorized();

      var result = await _signInManager.CheckPasswordSignInAsync(user, loginDto.Password, false);

      if (result.Succeeded)
      {
        return CreateUserObject(user);
      }

      return Unauthorized();
    }

    [HttpPost("register")]
    public async Task<ActionResult<UserDto>> Register(RegisterDto registerDto)
    {
      if (await _userManager.Users.AnyAsync(x => x.Email == registerDto.Email))
      {
        return BadRequest("Email taken");
      }

      if (await _userManager.Users.AnyAsync(x => x.UserName == registerDto.UserName))
      {
        return BadRequest("User name taken");
      }

      var user = new AppUser
      {
        Email = registerDto.Email,
        //DisplayName = registerDto.DisplayName,
        UserName = registerDto.UserName
      };

      var result = await _userManager.CreateAsync(user, registerDto.Password);

      if (result.Succeeded)
      {
        return CreateUserObject(user);
      }

      return BadRequest("failed regist");
    }

    [Authorize]
    [HttpGet]
    public async Task<ActionResult<UserDto>> GetCurrentUser()
    {
      var user = await _userManager.FindByEmailAsync(User.FindFirstValue(ClaimTypes.Email));

      return CreateUserObject(user);
    }

    private UserDto CreateUserObject(AppUser user)
    {
      user.Email = user.UserName;
      return new UserDto
      {
        Id = user.Id,
        Iamge = null,
        Token = _tokenService.CreateToken(user),
        UserName = user.UserName
      };
    }
  }
}
0
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
4