└── APIProdutos ├── .vscode ├── launch.json └── tasks.json ├── APIProdutos.csproj ├── Business └── ProdutoService.cs ├── Controllers ├── LoginController.cs └── ProdutosController.cs ├── Data ├── ApplicationDbContext.cs └── CatalogoDbContext.cs ├── Models ├── ApplicationUser.cs ├── IdentityInitializer.cs ├── Produto.cs └── Resultado.cs ├── Program.cs ├── Properties └── launchSettings.json ├── Security ├── AccessManager.cs ├── Classes.cs ├── JwtSecurityExtension.cs └── SigningConfigurations.cs ├── Startup.cs ├── appsettings.Development.json └── appsettings.json /APIProdutos/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/APIProdutos.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach", 33 | "processId": "${command:pickProcess}" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /APIProdutos/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/APIProdutos.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/APIProdutos.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/APIProdutos.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /APIProdutos/APIProdutos.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /APIProdutos/Business/ProdutoService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using APIProdutos.Data; 5 | using APIProdutos.Models; 6 | 7 | namespace APIProdutos.Business 8 | { 9 | public class ProdutoService 10 | { 11 | private CatalogoDbContext _context; 12 | 13 | public ProdutoService(CatalogoDbContext context) 14 | { 15 | _context = context; 16 | } 17 | 18 | public Produto Obter(string codigoBarras) 19 | { 20 | codigoBarras = codigoBarras?.Trim().ToUpper(); 21 | if (!String.IsNullOrWhiteSpace(codigoBarras)) 22 | { 23 | return _context.Produtos.Where( 24 | p => p.CodigoBarras == codigoBarras).FirstOrDefault(); 25 | } 26 | else 27 | return null; 28 | } 29 | 30 | public IEnumerable ListarTodos() 31 | { 32 | return _context.Produtos 33 | .OrderBy(p => p.Nome).ToList(); 34 | } 35 | 36 | public Resultado Incluir(Produto dadosProduto) 37 | { 38 | Resultado resultado = DadosValidos(dadosProduto); 39 | resultado.Acao = "Inclusão de Produto"; 40 | 41 | if (resultado.Inconsistencias.Count == 0 && 42 | _context.Produtos.Where( 43 | p => p.CodigoBarras == dadosProduto.CodigoBarras).Count() > 0) 44 | { 45 | resultado.Inconsistencias.Add( 46 | "Código de Barras já cadastrado"); 47 | } 48 | 49 | if (resultado.Inconsistencias.Count == 0) 50 | { 51 | _context.Produtos.Add(dadosProduto); 52 | _context.SaveChanges(); 53 | } 54 | 55 | return resultado; 56 | } 57 | 58 | public Resultado Atualizar(Produto dadosProduto) 59 | { 60 | Resultado resultado = DadosValidos(dadosProduto); 61 | resultado.Acao = "Atualização de Produto"; 62 | 63 | if (resultado.Inconsistencias.Count == 0) 64 | { 65 | Produto produto = _context.Produtos.Where( 66 | p => p.CodigoBarras == dadosProduto.CodigoBarras).FirstOrDefault(); 67 | 68 | if (produto == null) 69 | { 70 | resultado.Inconsistencias.Add( 71 | "Produto não encontrado"); 72 | } 73 | else 74 | { 75 | produto.Nome = dadosProduto.Nome; 76 | produto.Preco = dadosProduto.Preco; 77 | _context.SaveChanges(); 78 | } 79 | } 80 | 81 | return resultado; 82 | } 83 | 84 | public Resultado Excluir(string codigoBarras) 85 | { 86 | Resultado resultado = new Resultado(); 87 | resultado.Acao = "Exclusão de Produto"; 88 | 89 | Produto produto = Obter(codigoBarras); 90 | if (produto == null) 91 | { 92 | resultado.Inconsistencias.Add( 93 | "Produto não encontrado"); 94 | } 95 | else 96 | { 97 | _context.Produtos.Remove(produto); 98 | _context.SaveChanges(); 99 | } 100 | 101 | return resultado; 102 | } 103 | 104 | private Resultado DadosValidos(Produto produto) 105 | { 106 | var resultado = new Resultado(); 107 | if (produto == null) 108 | { 109 | resultado.Inconsistencias.Add( 110 | "Preencha os Dados do Produto"); 111 | } 112 | else 113 | { 114 | if (String.IsNullOrWhiteSpace(produto.CodigoBarras)) 115 | { 116 | resultado.Inconsistencias.Add( 117 | "Preencha o Código de Barras"); 118 | } 119 | if (String.IsNullOrWhiteSpace(produto.Nome)) 120 | { 121 | resultado.Inconsistencias.Add( 122 | "Preencha o Nome do Produto"); 123 | } 124 | if (produto.Preco <= 0) 125 | { 126 | resultado.Inconsistencias.Add( 127 | "O Preço do Produto deve ser maior do que zero"); 128 | } 129 | } 130 | 131 | return resultado; 132 | } 133 | } 134 | } -------------------------------------------------------------------------------- /APIProdutos/Controllers/LoginController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Authorization; 3 | using APIProdutos.Security; 4 | 5 | namespace APIProdutos.Controllers 6 | { 7 | [ApiController] 8 | [Route("[controller]")] 9 | public class LoginController : ControllerBase 10 | { 11 | [AllowAnonymous] 12 | [HttpPost] 13 | public object Post( 14 | [FromBody]AccessCredentials credenciais, 15 | [FromServices]AccessManager accessManager) 16 | { 17 | if (accessManager.ValidateCredentials(credenciais)) 18 | { 19 | return accessManager.GenerateToken(credenciais); 20 | } 21 | else 22 | { 23 | return new 24 | { 25 | Authenticated = false, 26 | Message = "Falha ao autenticar" 27 | }; 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /APIProdutos/Controllers/ProdutosController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Authorization; 4 | using APIProdutos.Business; 5 | using APIProdutos.Models; 6 | 7 | namespace APIProdutos.Controllers 8 | { 9 | [ApiController] 10 | [Route("[controller]")] 11 | [Authorize("Bearer")] 12 | public class ProdutosController : ControllerBase 13 | { 14 | private ProdutoService _service; 15 | 16 | public ProdutosController(ProdutoService service) 17 | { 18 | _service = service; 19 | } 20 | 21 | [HttpGet] 22 | public IEnumerable Get() 23 | { 24 | return _service.ListarTodos(); 25 | } 26 | 27 | [HttpGet("{codigoBarras}")] 28 | public ActionResult Get(string codigoBarras) 29 | { 30 | var produto = _service.Obter(codigoBarras); 31 | if (produto != null) 32 | return produto; 33 | else 34 | return NotFound(); 35 | } 36 | 37 | [HttpPost] 38 | public Resultado Post([FromBody]Produto produto) 39 | { 40 | return _service.Incluir(produto); 41 | } 42 | 43 | [HttpPut] 44 | public Resultado Put([FromBody]Produto produto) 45 | { 46 | return _service.Atualizar(produto); 47 | } 48 | 49 | [HttpDelete("{codigoBarras}")] 50 | public Resultado Delete(string codigoBarras) 51 | { 52 | return _service.Excluir(codigoBarras); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /APIProdutos/Data/ApplicationDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore; 3 | using APIProdutos.Models; 4 | 5 | namespace APIProdutos.Data 6 | { 7 | public class ApplicationDbContext : IdentityDbContext 8 | { 9 | public ApplicationDbContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | } 13 | 14 | protected override void OnModelCreating(ModelBuilder builder) 15 | { 16 | base.OnModelCreating(builder); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /APIProdutos/Data/CatalogoDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using APIProdutos.Models; 3 | 4 | namespace APIProdutos.Data 5 | { 6 | public class CatalogoDbContext : DbContext 7 | { 8 | public CatalogoDbContext( 9 | DbContextOptions options) : base(options) 10 | { } 11 | 12 | public DbSet Produtos { get; set; } 13 | 14 | protected override void OnModelCreating(ModelBuilder modelBuilder) 15 | { 16 | modelBuilder.Entity() 17 | .HasKey(p => p.CodigoBarras); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /APIProdutos/Models/ApplicationUser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | 3 | namespace APIProdutos.Models 4 | { 5 | public class ApplicationUser : IdentityUser 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /APIProdutos/Models/IdentityInitializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Identity; 3 | using APIProdutos.Data; 4 | using APIProdutos.Models; 5 | 6 | namespace APIProdutos.Security 7 | { 8 | public class IdentityInitializer 9 | { 10 | private readonly ApplicationDbContext _context; 11 | private readonly UserManager _userManager; 12 | private readonly RoleManager _roleManager; 13 | 14 | public IdentityInitializer( 15 | ApplicationDbContext context, 16 | UserManager userManager, 17 | RoleManager roleManager) 18 | { 19 | _context = context; 20 | _userManager = userManager; 21 | _roleManager = roleManager; 22 | } 23 | 24 | public void Initialize() 25 | { 26 | if (_context.Database.EnsureCreated()) 27 | { 28 | if (!_roleManager.RoleExistsAsync(Roles.ROLE_API_PRODUTOS).Result) 29 | { 30 | var resultado = _roleManager.CreateAsync( 31 | new IdentityRole(Roles.ROLE_API_PRODUTOS)).Result; 32 | if (!resultado.Succeeded) 33 | { 34 | throw new Exception( 35 | $"Erro durante a criação da role {Roles.ROLE_API_PRODUTOS}."); 36 | } 37 | } 38 | 39 | CreateUser( 40 | new ApplicationUser() 41 | { 42 | UserName = "admin_apiprodutos", 43 | Email = "admin-apiprodutos@teste.com.br", 44 | EmailConfirmed = true 45 | }, "AdminAPIProdutos01!", Roles.ROLE_API_PRODUTOS); 46 | 47 | CreateUser( 48 | new ApplicationUser() 49 | { 50 | UserName = "usrinvalido_apiprodutos", 51 | Email = "usrinvalido-apiprodutos@teste.com.br", 52 | EmailConfirmed = true 53 | }, "UsrInvAPIProdutos01!"); 54 | } 55 | } 56 | private void CreateUser( 57 | ApplicationUser user, 58 | string password, 59 | string initialRole = null) 60 | { 61 | if (_userManager.FindByNameAsync(user.UserName).Result == null) 62 | { 63 | var resultado = _userManager 64 | .CreateAsync(user, password).Result; 65 | 66 | if (resultado.Succeeded && 67 | !String.IsNullOrWhiteSpace(initialRole)) 68 | { 69 | _userManager.AddToRoleAsync(user, initialRole).Wait(); 70 | } 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /APIProdutos/Models/Produto.cs: -------------------------------------------------------------------------------- 1 | namespace APIProdutos.Models 2 | { 3 | public class Produto 4 | { 5 | private string _codigoBarras; 6 | public string CodigoBarras 7 | { 8 | get => _codigoBarras; 9 | set => _codigoBarras = value?.Trim().ToUpper(); 10 | } 11 | 12 | private string _nome; 13 | public string Nome 14 | { 15 | get => _nome; 16 | set => _nome = value?.Trim(); 17 | } 18 | 19 | public double Preco { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /APIProdutos/Models/Resultado.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace APIProdutos.Models 4 | { 5 | public class Resultado 6 | { 7 | public string Acao { get; set; } 8 | 9 | public bool Sucesso 10 | { 11 | get { return Inconsistencias == null || Inconsistencias.Count == 0; } 12 | } 13 | 14 | public List Inconsistencias { get; } = new List(); 15 | } 16 | } -------------------------------------------------------------------------------- /APIProdutos/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace APIProdutos 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /APIProdutos/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:42599", 8 | "sslPort": 44326 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "weatherforecast", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "APIProdutos": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "weatherforecast", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /APIProdutos/Security/AccessManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IdentityModel.Tokens.Jwt; 3 | using System.Security.Claims; 4 | using System.Security.Principal; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.IdentityModel.Tokens; 7 | using Microsoft.Extensions.Caching.Distributed; 8 | using Newtonsoft.Json; 9 | using APIProdutos.Models; 10 | 11 | namespace APIProdutos.Security 12 | { 13 | public class AccessManager 14 | { 15 | private UserManager _userManager; 16 | private SignInManager _signInManager; 17 | private SigningConfigurations _signingConfigurations; 18 | private TokenConfigurations _tokenConfigurations; 19 | private IDistributedCache _cache; 20 | 21 | public AccessManager( 22 | UserManager userManager, 23 | SignInManager signInManager, 24 | SigningConfigurations signingConfigurations, 25 | TokenConfigurations tokenConfigurations, 26 | IDistributedCache cache) 27 | { 28 | _userManager = userManager; 29 | _signInManager = signInManager; 30 | _signingConfigurations = signingConfigurations; 31 | _tokenConfigurations = tokenConfigurations; 32 | _cache = cache; 33 | } 34 | 35 | public bool ValidateCredentials(AccessCredentials credenciais) 36 | { 37 | bool credenciaisValidas = false; 38 | if (credenciais != null && !String.IsNullOrWhiteSpace(credenciais.UserID)) 39 | { 40 | if (credenciais.GrantType == "password") 41 | { 42 | // Verifica a existência do usuário nas tabelas do 43 | // ASP.NET Core Identity 44 | var userIdentity = _userManager 45 | .FindByNameAsync(credenciais.UserID).Result; 46 | if (userIdentity != null) 47 | { 48 | // Efetua o login com base no Id do usuário e sua senha 49 | var resultadoLogin = _signInManager 50 | .CheckPasswordSignInAsync(userIdentity, credenciais.Password, false) 51 | .Result; 52 | if (resultadoLogin.Succeeded) 53 | { 54 | // Verifica se o usuário em questão possui 55 | // a role Acesso-APIProdutos 56 | credenciaisValidas = _userManager.IsInRoleAsync( 57 | userIdentity, Roles.ROLE_API_PRODUTOS).Result; 58 | } 59 | } 60 | } 61 | else if (credenciais.GrantType == "refresh_token") 62 | { 63 | if (!String.IsNullOrWhiteSpace(credenciais.RefreshToken)) 64 | { 65 | RefreshTokenData refreshTokenBase = null; 66 | 67 | string strTokenArmazenado = 68 | _cache.GetString(credenciais.RefreshToken); 69 | if (!String.IsNullOrWhiteSpace(strTokenArmazenado)) 70 | { 71 | refreshTokenBase = JsonConvert 72 | .DeserializeObject(strTokenArmazenado); 73 | } 74 | 75 | credenciaisValidas = (refreshTokenBase != null && 76 | credenciais.UserID == refreshTokenBase.UserID && 77 | credenciais.RefreshToken == refreshTokenBase.RefreshToken); 78 | 79 | // Elimina o token de refresh já que um novo será gerado 80 | if (credenciaisValidas) 81 | _cache.Remove(credenciais.RefreshToken); 82 | } 83 | } 84 | } 85 | 86 | return credenciaisValidas; 87 | } 88 | 89 | public Token GenerateToken(AccessCredentials credenciais) 90 | { 91 | ClaimsIdentity identity = new ClaimsIdentity( 92 | new GenericIdentity(credenciais.UserID, "Login"), 93 | new[] { 94 | new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")), 95 | new Claim(JwtRegisteredClaimNames.UniqueName, credenciais.UserID) 96 | } 97 | ); 98 | 99 | DateTime dataCriacao = DateTime.Now; 100 | DateTime dataExpiracao = dataCriacao + 101 | TimeSpan.FromSeconds(_tokenConfigurations.Seconds); 102 | 103 | var handler = new JwtSecurityTokenHandler(); 104 | var securityToken = handler.CreateToken(new SecurityTokenDescriptor 105 | { 106 | Issuer = _tokenConfigurations.Issuer, 107 | Audience = _tokenConfigurations.Audience, 108 | SigningCredentials = _signingConfigurations.SigningCredentials, 109 | Subject = identity, 110 | NotBefore = dataCriacao, 111 | Expires = dataExpiracao 112 | }); 113 | var token = handler.WriteToken(securityToken); 114 | 115 | var resultado = new Token() 116 | { 117 | Authenticated = true, 118 | Created = dataCriacao.ToString("yyyy-MM-dd HH:mm:ss"), 119 | Expiration = dataExpiracao.ToString("yyyy-MM-dd HH:mm:ss"), 120 | AccessToken = token, 121 | RefreshToken = Guid.NewGuid().ToString().Replace("-", String.Empty), 122 | Message = "OK" 123 | }; 124 | 125 | // Armazena o refresh token em cache através do Redis 126 | var refreshTokenData = new RefreshTokenData(); 127 | refreshTokenData.RefreshToken = resultado.RefreshToken; 128 | refreshTokenData.UserID = credenciais.UserID; 129 | 130 | 131 | // Calcula o tempo máximo de validade do refresh token 132 | // (o mesmo será invalidado automaticamente pelo Redis) 133 | TimeSpan finalExpiration = 134 | TimeSpan.FromSeconds(_tokenConfigurations.FinalExpiration); 135 | 136 | DistributedCacheEntryOptions opcoesCache = 137 | new DistributedCacheEntryOptions(); 138 | opcoesCache.SetAbsoluteExpiration(finalExpiration); 139 | _cache.SetString(resultado.RefreshToken, 140 | JsonConvert.SerializeObject(refreshTokenData), 141 | opcoesCache); 142 | 143 | return resultado; 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /APIProdutos/Security/Classes.cs: -------------------------------------------------------------------------------- 1 | namespace APIProdutos.Security 2 | { 3 | public class AccessCredentials 4 | { 5 | public string UserID { get; set; } 6 | public string Password { get; set; } 7 | public string RefreshToken { get; set; } 8 | public string GrantType { get; set; } 9 | } 10 | 11 | public static class Roles 12 | { 13 | public const string ROLE_API_PRODUTOS = "Acesso-APIProdutos"; 14 | } 15 | 16 | public class TokenConfigurations 17 | { 18 | public string Audience { get; set; } 19 | public string Issuer { get; set; } 20 | public int Seconds { get; set; } 21 | public int FinalExpiration { get; set; } 22 | } 23 | 24 | public class Token 25 | { 26 | public bool Authenticated { get; set; } 27 | public string Created { get; set; } 28 | public string Expiration { get; set; } 29 | public string AccessToken { get; set; } 30 | public string RefreshToken { get; set; } 31 | public string Message { get; set; } 32 | } 33 | 34 | public class RefreshTokenData 35 | { 36 | public string RefreshToken { get; set; } 37 | public string UserID { get; set; } 38 | } 39 | } -------------------------------------------------------------------------------- /APIProdutos/Security/JwtSecurityExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Authentication.JwtBearer; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using APIProdutos.Security; 6 | 7 | namespace APIProdutos.Security 8 | { 9 | public static class JwtSecurityExtension 10 | { 11 | public static IServiceCollection AddJwtSecurity( 12 | this IServiceCollection services, 13 | SigningConfigurations signingConfigurations, 14 | TokenConfigurations tokenConfigurations) 15 | { 16 | services.AddAuthentication(authOptions => 17 | { 18 | authOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 19 | authOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 20 | }).AddJwtBearer(bearerOptions => 21 | { 22 | var paramsValidation = bearerOptions.TokenValidationParameters; 23 | paramsValidation.IssuerSigningKey = signingConfigurations.Key; 24 | paramsValidation.ValidAudience = tokenConfigurations.Audience; 25 | paramsValidation.ValidIssuer = tokenConfigurations.Issuer; 26 | 27 | // Valida a assinatura de um token recebido 28 | paramsValidation.ValidateIssuerSigningKey = true; 29 | 30 | // Verifica se um token recebido ainda é válido 31 | paramsValidation.ValidateLifetime = true; 32 | 33 | // Tempo de tolerância para a expiração de um token (utilizado 34 | // caso haja problemas de sincronismo de horário entre diferentes 35 | // computadores envolvidos no processo de comunicação) 36 | paramsValidation.ClockSkew = TimeSpan.Zero; 37 | }); 38 | 39 | // Ativa o uso do token como forma de autorizar o acesso 40 | // a recursos deste projeto 41 | services.AddAuthorization(auth => 42 | { 43 | auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder() 44 | .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌​) 45 | .RequireAuthenticatedUser().Build()); 46 | }); 47 | 48 | return services; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /APIProdutos/Security/SigningConfigurations.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Microsoft.IdentityModel.Tokens; 3 | 4 | namespace APIProdutos.Security 5 | { 6 | public class SigningConfigurations 7 | { 8 | public SecurityKey Key { get; } 9 | public SigningCredentials SigningCredentials { get; } 10 | 11 | public SigningConfigurations() 12 | { 13 | using (var provider = new RSACryptoServiceProvider(2048)) 14 | { 15 | Key = new RsaSecurityKey(provider.ExportParameters(true)); 16 | } 17 | 18 | SigningCredentials = new SigningCredentials( 19 | Key, SecurityAlgorithms.RsaSha256Signature); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /APIProdutos/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Identity; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using Microsoft.Extensions.Options; 8 | using Microsoft.EntityFrameworkCore; 9 | using APIProdutos.Data; 10 | using APIProdutos.Models; 11 | using APIProdutos.Security; 12 | using APIProdutos.Business; 13 | 14 | namespace APIProdutos 15 | { 16 | public class Startup 17 | { 18 | public Startup(IConfiguration configuration) 19 | { 20 | Configuration = configuration; 21 | } 22 | 23 | public IConfiguration Configuration { get; } 24 | 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | // Ativando o uso de cache via Redis 28 | services.AddDistributedRedisCache(options => 29 | { 30 | options.Configuration = 31 | Configuration.GetConnectionString("ConexaoRedis"); 32 | options.InstanceName = "APIProdutos"; 33 | }); 34 | 35 | // Configurando o acesso a dados de produtos 36 | services.AddDbContext(options => 37 | options.UseInMemoryDatabase("InMemoryDatabase")); 38 | services.AddScoped(); 39 | 40 | // Configurando o uso da classe de contexto para 41 | // acesso às tabelas do ASP.NET Identity Core 42 | services.AddDbContext(options => 43 | options.UseInMemoryDatabase("InMemoryDatabase")); 44 | 45 | // Ativando a utilização do ASP.NET Identity, a fim de 46 | // permitir a recuperação de seus objetos via injeção de 47 | // dependências 48 | services.AddIdentity() 49 | .AddEntityFrameworkStores() 50 | .AddDefaultTokenProviders(); 51 | 52 | // Configurando a dependência para a classe de validação 53 | // de credenciais e geração de tokens 54 | services.AddScoped(); 55 | 56 | var signingConfigurations = new SigningConfigurations(); 57 | services.AddSingleton(signingConfigurations); 58 | 59 | var tokenConfigurations = new TokenConfigurations(); 60 | new ConfigureFromConfigurationOptions( 61 | Configuration.GetSection("TokenConfigurations")) 62 | .Configure(tokenConfigurations); 63 | services.AddSingleton(tokenConfigurations); 64 | 65 | // Aciona a extensão que irá configurar o uso de 66 | // autenticação e autorização via tokens 67 | services.AddJwtSecurity( 68 | signingConfigurations, tokenConfigurations); 69 | 70 | services.AddCors(); 71 | services.AddControllers(); 72 | } 73 | 74 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, 75 | ApplicationDbContext context, 76 | UserManager userManager, 77 | RoleManager roleManager) 78 | { 79 | if (env.IsDevelopment()) 80 | { 81 | app.UseDeveloperExceptionPage(); 82 | } 83 | 84 | // Criação de estruturas, usuários e permissões 85 | // na base do ASP.NET Identity Core (caso ainda não 86 | // existam) 87 | new IdentityInitializer(context, userManager, roleManager) 88 | .Initialize(); 89 | 90 | app.UseHttpsRedirection(); 91 | 92 | app.UseRouting(); 93 | 94 | app.UseAuthorization(); 95 | 96 | app.UseEndpoints(endpoints => 97 | { 98 | endpoints.MapControllers(); 99 | }); 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /APIProdutos/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /APIProdutos/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "ConexaoRedis": "localhost,port: 6379,password=Redis2019!" 4 | }, 5 | "TokenConfigurations": { 6 | "Audience": "Clients-APIProdutos", 7 | "Issuer": "APIProdutos", 8 | "Seconds": 60, 9 | "FinalExpiration": 120 10 | }, 11 | "Logging": { 12 | "LogLevel": { 13 | "Default": "Information", 14 | "Microsoft": "Warning", 15 | "Microsoft.Hosting.Lifetime": "Information" 16 | } 17 | }, 18 | "AllowedHosts": "*" 19 | } --------------------------------------------------------------------------------