└── 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 | }
--------------------------------------------------------------------------------