Sejam bem vindos novamente marujos, bora continuar remando rumo à mais um módulo desse projeto incrível.
Código Módulo 2 -> Repositório GitHub
Nesse módulo irei abordar a evolução do projeto da API de Login com autorização através do JWT Token. Modulo 1
Migrando .Net 5 para .Net 6
1 - No arquivo .csproj altere o TargertFramework
para net6.0
e dentro do mesmo <PropertyGroup>
adicione <Nullable>enable</Nullable>
e <ImplicitUsings>enable</ImplicitUsings>
e o resultado deve ficar assim:
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
2 - Atualize os pacotes Nugets
3 - Migrando dados da Startup.cs para Program.cs
No .net 6 você não precisa mais dos métodos ConfigureServices()
e do Configure()
, você precisa apenas estanciar o WebApplication Builder
na classe program
para poder ter acesso aos métodos de configuração.
Então no nosso programa o método ConfigureServices()
ficou assim:
IMPORTANTE: Mudei o JWTkey para o nosso secrets, para não deixar exposto a nossa chave de criptografia.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<TokenService>();
builder.Services.AddDbContext<DataContext>();
builder.Services.AddControllers();
//JWTConfig
var key = Encoding.ASCII.GetBytes(builder.Configuration["Key:JwtKey"].ToString());
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false
};
});
//Configuração do Swagger para adicionar o Bearer Token na auth
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWTAuthAuthentication2", Version = "v1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JWT Authorization header using the Bearer scheme",
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
E o método Configure()
vai ficar assim:
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWTAuthAuthentication2 v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
////JWTConfig - Adicionar sempre Authentication antes de Authorization
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();
Repare que não fiz grandes alterações, só passei as configurações da startup para a classe Program.cs
e agora você pode excluir a classe Startup.cs
adotoando os padrões do .net 6
Crud de Roles
Create Read Update Delete - CRUD
Como vamos fazer um crud completo da entidade Roles, é de boa prática criar uma Controller só pra isso, separando as responsabilidades.
Nessa Controller só terá autorização quem tiver acesso de Admin.
Regas de negócio que aplicamos:
- 1 - Usuário não pode alterar sua própria role
- 2 - Precisa ter ao menos 1 usuário admin no banco de dados
- 3 - Não pode haver duas Roles com o mesmo nome
E assim ficou a RolesController
Post
[HttpPost("AddRole")]
public async Task<IActionResult> AddRole(RoleViewModel roleRequest,
[FromServices] DataContext context)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
if (context.Roles.Any(x => x.Name.Equals(roleRequest.Name))) return BadRequest("Role duplicated");
var roleModel = new Role
{
Name = roleRequest.Name,
};
await context.Roles.AddAsync(roleModel);
await context.SaveChangesAsync();
return Ok(roleModel);
}
Patch Change User Role
[HttpPatch("ChangeUserRole")]
public async Task<IActionResult> ChangeUserRole(int UserId,
int NewRoleId,
[FromServices] DataContext context)
{
if(!ModelState.IsValid) return BadRequest(ModelState);
//Consultas
var userModel = await context
.Users
.FirstOrDefaultAsync(x => x.Id == UserId);
var roleModel = await context
.Roles
.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == NewRoleId);
//Validações
if (userModel == null || roleModel == null)
return StatusCode(401, "Invalid Id");
//Verificando quantos usuarios admins temos no sistema
var userList = await context.Users.ToListAsync();
int adminUsers = 0;
foreach (var item in userList)
{
if (item.RolesId == 2) adminUsers++;
}
//Verificando se sobrará pelo menos 1 admin no sistema
if (userModel.RolesId == 2 && adminUsers <= 1) return StatusCode(400, "You must have at least 1 admin user in the database");
userModel.Roles = roleModel;
//Admin não pode mudar sua própria role
if (User.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value.Equals(userModel.Id.ToString())) return StatusCode(400, "You cannot change your own role");
await context.SaveChangesAsync();
return Ok(userModel.Name + " Role changed to " + roleModel.Name);
}
Get Mostrar todos os Roles
[HttpGet("GetRoles")]
public async Task<IEnumerable<Role>> GetRoles([FromServices] DataContext context)
{
return await context.Roles.ToListAsync();
}
Patch Edit Role
[HttpPatch("EditRole")]
public async Task<IActionResult> EditRole([FromServices] DataContext context,
Role roleRequest)
{
if(!ModelState.IsValid) return BadRequest(ModelState);
var roleModel = await context.Roles.AsNoTracking().FirstOrDefaultAsync(x => x.Id == roleRequest.Id);
//Validações
if (roleModel == null) return NotFound("Role not found");
if (context.Roles.Any(x => x.Name.Equals(roleRequest.Name))) return BadRequest("Role duplicated");
context.Roles.Update(roleRequest);
await context.SaveChangesAsync();
return Ok(roleRequest);
}
Delete Roles
[HttpDelete("DeleteRoles")]
public async Task<IActionResult> DeleteRoles(int roleId,
[FromServices] DataContext context)
{
if(!ModelState.IsValid) return BadRequest(ModelState);
var roleModel = await context.Roles.FirstOrDefaultAsync(x => x.Id == roleId);
//Validações
if (roleModel == null) return NotFound("Role not found");
if (roleModel.Name.Contains("admin")) return BadRequest("Cannot delete admin role");
context.Roles.Remove(roleModel);
await context.SaveChangesAsync();
return Ok("Role " + roleModel.Name + " Deleted");
}
O código está inteiro comentado e separei os blocos por #region para facilitar leitura e busca dos métodos.
Qualquer dúvida, é só comentar ou entrar em contato comigo.
No proximo módulo eu adicionarei:
- CRUD de Usuários
- introdução do Repository Pattern
- Divisão em 3 Projetos (Infra - Domain - API) Obrigado por lerem mais um módulo da série dessa API.
Top comments (0)