Интеграция ASP.NET Core Identity с веб-службами и пользовательским поставщиком удостоверенийC#

Место общения программистов C#
Anonymous
Интеграция ASP.NET Core Identity с веб-службами и пользовательским поставщиком удостоверений

Сообщение Anonymous »

Это долго, и в некоторых вещах я не уверен на 100 %.
Я написал для клиента собственный основной поставщик удостоверений asp .net. Их пользовательской системе уже не менее 20 лет, и она глубоко укоренилась в их продуктах, поэтому ее не собираются менять на основную учетную запись пользователя asp .net. В любом случае, там все работает нормально. Мне нужно расширить это, чтобы разрешить удаленный вход в систему через мобильное приложение. Я работаю над набором веб-сервисов. У меня есть рабочий логин, который возвращает токен jwt, дату истечения срока действия и идентификатор пользователя. Я могу декодировать через jwt.io и увидеть ожидаемые данные. Вот мой контроллер входа. Пока у меня есть ролевой менеджер, я им не пользуюсь. FSMUser — мой собственный пользовательский класс. Я могу позвонить сюда из мобильного приложения или через почтальона и получить данные обратно.
[Route("api/[controller]")]
[ApiController]
public class LoginController : ControllerBase
{
private readonly SignInManager _signInManager;
private readonly UserManager _userManager;
private readonly RoleManager _roleManager;
private readonly IConfiguration _configuration;
public LoginController(
SignInManager signInManager,
UserManager userManager,
RoleManager roleManager,
IConfiguration configuration)
{
_signInManager = signInManager;
_userManager = userManager;
_roleManager = roleManager;
_configuration = configuration;
}

[HttpPost]
public async Task Login([FromBody] Login model)
{
var user = await _userManager.FindByNameAsync(model.Username);
if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
{
//var userRoles = await _userManager.GetRolesAsync(user);
var authClaims = new List
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};

authClaims.Add(new Claim("UserId", user.Id));
var token = GetToken(authClaims);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo,
Id = user.Id.Trim(),
});
}
return Unauthorized();
}
private JwtSecurityToken GetToken(List authClaims)
{
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(s: _configuration["Jwt:Key"]));

var token = new JwtSecurityToken(
issuer: _configuration["Jwt:ValidIssuer"],
audience: _configuration["Jwt:ValidAudience"],
expires: DateTime.Now.AddYears(30),
claims: authClaims,
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
);

return token;
}
}

Вот мой вызов веб-службы для получения информации о пользователе. К сожалению, я получаю код ошибки 401 Unauthorized http. Вот тут я совсем запутался. Я перепробовал кучу вещей, но так и не понял, что происходит. Размышляя об этом, я как бы решил, что проблема в том, что в моем пользовательском провайдере есть метод, который мне нужно реализовать. Есть ли что-нибудь, что можно придумать в этой области?
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class UserInfoController : ControllerBase
{
public UserInfoController()
{
}
[HttpGet]
public async Task Get()
{
var identity = HttpContext.User.Identity as ClaimsIdentity;
if (identity == null)
{
return BadRequest("User ID not found in claims.");
}
var userClaims = identity.Claims;
var userName = userClaims.FirstOrDefault()?.Value;
var da = new UserManagement();
var user = await da.UserInfoAsync(userName);
return Ok(new
{
UserId = user?.Userid,
CustNo = user?.Custno.Trim(),
CustName = user?.Custname.Trim(),
FirstName = user?.Firstname.Trim(),
LastName = user?.Lastname.Trim(),
Initial = user?.Initial.Trim(),
Honorific = user?.Honorific.Trim(),
});
}
}

Вот мои пользовательские классы поставщиков
public class FSMUser : IdentityUser
{
#pragma warning disable CS8764 // Nullability of return type doesn't match overridden member (possibly because of nullability attributes).
public override string? Id { get; set; }
#pragma warning restore CS8764 // Nullability of return type doesn't match overridden member (possibly because of nullability attributes).
public override string? UserName { get; set; }
public override string? NormalizedUserName { get; set; }
public string? Password { get; set; }
public override string? PasswordHash { get; set; }
}

public class FSMUserStore : IUserStore,
IUserPasswordStore
{
private bool disposedValue;

public async Task CreateAsync(FSMUser user, CancellationToken cancellationToken)
{
var ctx = new PoaCsmContext();
var fsmu = new Arcustmr();
fsmu.Email = user?.UserName;
fsmu.Userid = user?.UserName;
fsmu.Code = user?.PasswordHash;
ctx.Arcustmrs.Add(fsmu);
await ctx.SaveChangesAsync();
return IdentityResult.Success;
}

public async Task DeleteAsync(FSMUser user, CancellationToken cancellationToken)
{
var ctx = new PoaCsmContext();
var users = await (from u in ctx.Arcustmrs where u.Email == user.UserName select u).ToListAsync();
ctx.Arcustmrs.RemoveRange(users);
await ctx.SaveChangesAsync();
return IdentityResult.Success;
}

public async Task FindByIdAsync(string userId, CancellationToken cancellationToken)
{
var ctx = new PoaCsmContext();
var user = await (from c in ctx.Arcustmrs where c.Custno == userId select new FSMUser() { Id = c.Custno, UserName = c.Email, NormalizedUserName = c.Email, PasswordHash = c.Code }).FirstOrDefaultAsync();
return user;
}

public async Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
{
var ctx = new PoaCsmContext();
var user = await (from c in ctx.Arcustmrs where c.Email == normalizedUserName select new FSMUser() { Id = c.Custno, NormalizedUserName = c.Email, UserName = c.Email, PasswordHash = c.Code }).FirstOrDefaultAsync();
return user;
}

public Task GetNormalizedUserNameAsync(FSMUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.NormalizedUserName);
}

public Task GetUserIdAsync(FSMUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Id);
}

public Task GetUserNameAsync(FSMUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.UserName);
}

public Task SetNormalizedUserNameAsync(FSMUser user, string? normalizedName, CancellationToken cancellationToken)
{
user.NormalizedUserName = normalizedName;
return Task.CompletedTask;
}

public Task SetUserNameAsync(FSMUser user, string? userName, CancellationToken cancellationToken)
{
user.UserName = userName;
return Task.CompletedTask;
}

public Task UpdateAsync(FSMUser user, CancellationToken cancellationToken)
{
var ctx = new PoaCsmContext();
return Task.FromResult(IdentityResult.Success);
}

protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects)
}

// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
disposedValue = true;
}
}

// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
// ~FSMUserStore()
// {
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
// Dispose(disposing: false);
// }

public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}

public Task SetPasswordHashAsync(FSMUser user, string? passwordHash, CancellationToken cancellationToken)
{
var ctx = new PoaCsmContext();
var us = (from u in ctx.Arcustmrs where u.Email == user.UserName select u).SingleOrDefault();
if (us != null)
{
us.Code = passwordHash;
ctx.SaveChanges();
}
return Task.CompletedTask;
}

public Task GetPasswordHashAsync(FSMUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.PasswordHash);
}

public async Task HasPasswordAsync(FSMUser user, CancellationToken cancellationToken)
{
var result = false;
var ctx = new PoaCsmContext();
var email = user.UserName;
var use = await (from u in ctx.Arcustmrs where u.Email.Contains(email) select u).SingleOrDefaultAsync(cancellationToken: cancellationToken);
if (use != null)
{
if((use.Code == null) ||
(String.IsNullOrEmpty(use.Code)))
{
result = false;
}
else
{
result = true;
}
}
else
{
result = false;
}
return result;
}
public async Task GetRolesAsync(FSMUser user)
{
var rl = new List();
rl.Add("User");
return rl;
}
}

Вот мой файл program.cs для моего проекта основных веб-служб asp .net.
var builder = WebApplication.CreateBuilder(args);
ConfigurationManager configuration = builder.Configuration;
// Add services to the container.
// For Entity Framework
builder.Services.AddDbContext(options => options.UseSqlServer(configuration.GetConnectionString("FSMDatabaseConnection")));

builder.Services.AddScoped();

builder.Services.AddIdentity()
.AddEntityFrameworkStores()
.AddDefaultTokenProviders();

// Adding Authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
// Adding Jwt Bearer
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = configuration["Jwt:ValidAudience"],
ValidIssuer = configuration["Jwt:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"]))
};
});

builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Вернуться в «C#»