Ir para o conteúdo

Autenticação e autorização ​

← Índice da documentação da API

Tudo nesta página foi conferido em config/SecurityConfig.java, config/JwtAuthenticationFilter.java, config/JwtSecurityConfig.java, config/LoginRateLimitingFilter.java, realtime/WebSocketConfig.java, mes/MesAccess.java, mes/MesEdgeSecurity.java, service/AuthenticationService.java e nas anotações @PreAuthorize de cada controller.

1. Visão geral ​

A API é stateless: não há sessão de servidor (SessionCreationPolicy.STATELESS) e o CSRF está desligado, coerente com um cliente que envia o token em toda chamada. Existem duas cadeias de segurança independentes:

CadeiaAlcanceComo autentica
Principal (SecurityConfig)toda a API, exceto /api/mes/edge/**JWT no cabeçalho Authorization: Bearer <token>
Edge do MES (MesEdgeSecurity, @Order(1))/api/mes/edge/**chave compartilhada no cabeçalho X-Edge-Key

São cadeias separadas de propósito: o coletor de chão de fábrica não é uma pessoa e não faz login.

2. Fluxo de autenticação (usuários) ​

Cliente                                   API
  | POST /api/auth/login {login, senha}    |
  |--------------------------------------->|  normaliza login (trim + minúsculas)
  |                                        |  busca usuário (case-insensitive)
  |                                        |  se inativo → 403 "Usuário inativo. Procure um administrador."
  |                                        |  confere senha (BCrypt); falha → 401 "Usuário ou senha inválidos."
  |   200 UsuarioResponseDTO {token, perfilResumo, ...}
  |<---------------------------------------|
  | GET /api/qualquer-rota                 |
  |   Authorization: Bearer <token>        |
  |--------------------------------------->|  JwtAuthenticationFilter:
  |                                        |   1. extrai e valida assinatura/expiração
  |                                        |   2. recarrega o usuário do banco; exige ativo = true
  |                                        |   3. recarrega o PERFIL do banco; exige perfil ativo
  |                                        |   4. monta as authorities (ver seção 4)
  |   resposta                             |
  |<---------------------------------------|

Pontos confirmados no código:

  • Rotas de login: POST /api/auth/login e POST /api/usuarios/login (mesma operação, dois caminhos).
  • Corpo do login: {"login": "...", "senha": "..."} (LoginDTO, validado com @Valid).
  • Sessão atual: GET /api/auth/me devolve o usuário autenticado sem token novo, com perfilResumo.
  • Token: JWT assinado com HS256. O subject é o login do usuário. Não há claims de perfil nem de permissões no token. Validade padrão: 28800000 ms (8 horas), configurável pela variável SECURITY_JWT_EXPIRATION_MS.
  • Segredo: SECURITY_JWT_SECRET (ou JWT_SECRET); o serviço não sobe sem ele nem com menos de 32 caracteres. A chave efetiva é o SHA-256 do segredo informado.
  • Não existe refresh token, logout no servidor, revogação de token nem troca de senha obrigatória. "Sair" é apenas descartar o token no cliente (ver documento interno de lacunas).
  • Efeito imediato de mudanças: como usuário e perfil são relidos do banco a cada requisição, inativar um usuário, inativar um perfil ou mudar as telas de um perfil vale na próxima chamada, sem invalidar tokens.
  • Perfil ausente, inativo ou desconhecido gera zero authorities: o usuário continua autenticado, mas toda rota que exige uma permissão específica responde 403.

3. Rotas públicas (sem token) ​

Declaradas em SecurityConfig:

RotaObservação
OPTIONS /**pré-voo de CORS
POST /api/auth/login, POST /api/usuarios/loginlogin (limitado a 10 tentativas por minuto por IP)
POST /api/usuariosautocadastro (limitado a 5 por minuto por IP) — ver seção 6
/swagger-ui.html, /swagger-ui/**, /v3/api-docs/**, /api-docs/**só respondem se SWAGGER_ENABLED=true (padrão false)
/actuator/**, /errorliberados na cadeia; o que o Actuator expõe depende da configuração de deploy (não verificada)
/ws/**, /ws-native/**handshake de WebSocket liberado; a autenticação é exigida no comando STOMP CONNECT (seção 7)

Todo o resto cai em anyRequest().authenticated() (sem token → 401).

4. Autorização: perfil, telas e tipo de acesso ​

O modelo é o de perfil (perfil): cada usuário tem um código de perfil (campo perfil do usuário, texto, não é chave estrangeira) que aponta para um registro com:

  • tipoAcesso: FULL_ACCESS (pode alterar) ou READ_ONLY (só consulta);
  • telasPermitidas: lista de "telas" (ex.: WAREHOUSE, PRODUCTS, MES_PCP);
  • ativo.

O filtro JWT transforma isso em duas famílias de authorities:

AuthorityOrigem
ROLE_FULL_ACCESS ou ROLE_READ_ONLYtipoAcesso do perfil
SCREEN_<TELA>uma por item de telasPermitidas (maiúsculas)

As anotações @PreAuthorize combinam os dois eixos. O padrão que se repete é:

  • leitura: exige só SCREEN_<tela>;
  • criar / alterar / excluir: exige SCREEN_<tela> e hasRole('FULL_ACCESS').

Exemplo real (AreaEstoqueController): GET /api/areas aceita SCREEN_DASHBOARD ou SCREEN_WAREHOUSE; POST /api/areas e DELETE /api/areas/{id} exigem SCREEN_WAREHOUSE e FULL_ACCESS.

4.1 Telas (SCREEN_*) e o que liberam ​

AuthorityTela / uso no frontendO que o backend exige
SCREEN_DASHBOARDDashboardleitura de setores (/api/areas) e do estoque consolidado (/api/estoque/itens, /api/estoque/itens-consolidado), sempre "ou" SCREEN_WAREHOUSE
SCREEN_WAREHOUSEArmazém, Setores, estruturas, itens de estoque, listas (encerto)leitura com a tela; criar/alterar/excluir com a tela e FULL_ACCESS
SCREEN_PRODUCTSProdutos e categorias; Moldesleitura com a tela; escrita com a tela e FULL_ACCESS
SCREEN_MOLDSMoldesaceita-se SCREEN_MOLDS ou SCREEN_PRODUCTS nas rotas de moldes
SCREEN_USERSUsuárioslistar/consultar com a tela; alterar com a tela e FULL_ACCESS
SCREEN_PROFILESPerfiscriar/alterar/ativar/inativar com a tela e FULL_ACCESS; listar também aceita SCREEN_USERS
SCREEN_HISTORYHistóricoconsulta; o histórico por entidade também aceita SCREEN_USERS, SCREEN_PROFILES e SCREEN_PRODUCTS
SCREEN_REPORTSRelatórios de estoque (tela desativada no menu atual)consulta
SCREEN_ALERTSAlertas (tela desativada no menu atual)consulta
SCREEN_MOVEMENTSMovimentações (tela desativada no menu atual)consulta; registrar exige também FULL_ACCESS

Cada ficha de endpoint em modulos/ traz a expressão exata. A lista dos perfis iniciais está na seção 5.

4.2 MES / PCP: outro esquema ​

MesController não usa @PreAuthorize("SCREEN_...") direto. Usa o bean mesAccess (mes/MesAccess.java), que olha quatro authorities de tela e o papel FULL_ACCESS:

VerificaçãoRegra no código
readqualquer uma entre SCREEN_MES_ADMIN, SCREEN_MES_OPERATOR, SCREEN_MES_MECHANIC, SCREEN_MES_PCP
adminFULL_ACCESS e SCREEN_MES_ADMIN
operateadmin ou (FULL_ACCESS e SCREEN_MES_OPERATOR)
maintainadmin ou (FULL_ACCESS e SCREEN_MES_MECHANIC)
planadmin ou (FULL_ACCESS e SCREEN_MES_PCP)

Consequência importante: um perfil READ_ONLY com MES_PCP só consulta — não planeja. As fichas do módulo producao.md indicam qual verificação cada rota usa; GET /api/mes/permissions devolve {read, operate, maintain, plan, admin} do usuário logado, e é o que o frontend usa para mostrar/ocultar ações.

4.3 Autenticação do Edge (coletor industrial) ​

MesEdgeSecurity cria uma cadeia própria para /api/mes/edge/** que compara o cabeçalho X-Edge-Key com a variável de ambiente MES_EDGE_KEY (comparação em tempo constante, MessageDigest.isEqual):

SituaçãoResposta
MES_EDGE_KEY ausente ou com menos de 16 bytes503
cabeçalho ausente ou diferente da chave configurada401
chave corretasegue para o controller (principal mes-edge, authority MES_EDGE)

O corpo é {"message":"Integracao edge indisponivel ou credencial invalida"} nos dois casos — deliberadamente igual, para não revelar qual deles ocorreu. JWT de usuário não substitui a chave, e a chave não abre nenhuma rota fora de /api/mes/edge/**. A chave é global (não por máquina).

5. Perfis iniciais (migração V2__insert_default_data.sql) ​

CódigoTipoTelas
ADMINISTRADORFULL_ACCESSDASHBOARD, ALERTS, MOVEMENTS, REPORTS, WAREHOUSE, PRODUCTS, USERS, HISTORY, PROFILES
OPERADORFULL_ACCESSDASHBOARD, ALERTS, MOVEMENTS, REPORTS, WAREHOUSE, PRODUCTS, HISTORY
CONSULTORREAD_ONLYDASHBOARD, ALERTS, REPORTS, WAREHOUSE, PRODUCTS, HISTORY

Os perfis podem ser editados depois pela API/tela de Perfis (/api/perfis); o banco de produção pode estar diferente da semente. Essa semente não contém MOLDS nem as telas MES_*; elas passam a existir quando um perfil é criado ou editado com elas. A tela MOLDS foi criada depois: o frontend libera Moldes por MOLDS ou PRODUCTS, e o backend também aceita SCREEN_PRODUCTS ou SCREEN_MOLDS nas rotas de moldes.

6. Autocadastro: POST /api/usuarios ​

A rota é pública (usada pela tela "Criar conta"), mas o campo perfil do corpo só é respeitado quando quem chama é um usuário autenticado com SCREEN_USERS e FULL_ACCESS (chamadorPodeEscolherPerfil). Nos demais casos o perfil é forçado para CONSULTOR (PERFIL_PADRAO_AUTOCADASTRO), qualquer que seja o valor enviado. Por isso a pergunta "um anônimo consegue se criar como administrador?" tem resposta não no código atual.

7. WebSocket / STOMP ​

Endpoints /ws (com SockJS) e /ws-native (WebSocket puro). O handshake é sempre aceito, mas o comando STOMP CONNECT exige JWT válido (cabeçalho Authorization/authorization/X-Authorization/token do frame STOMP, ou parâmetro ?token= no handshake); sem isso a conexão é rejeitada com SecurityException. Usuário inativo ou token inválido também são rejeitados. Detalhes dos tópicos em integracoes.md.

8. CORS ​

CorsConfig aceita apenas origens de navegador autorizadas (o frontend publicado, ambientes locais de desenvolvimento e hosts internos da Wester).

Métodos permitidos: GET, POST, PUT, PATCH, DELETE, OPTIONS; cabeçalhos permitidos apenas Authorization e Content-Type; credenciais habilitadas; cache de pré-voo de 3600 s. Um cliente de servidor (cURL, TUTOM, coletor) não é afetado por CORS; um navegador em outra origem só consegue chamar a API se a origem estiver nessa lista.

X-Edge-Key não está na lista de cabeçalhos permitidos: o coletor deve chamar de servidor/dispositivo, não de navegador.

9. Limites de taxa ​

LoginRateLimitingFilter (em memória, por IP, janela deslizante):

RotaLimite
POST /api/auth/login10 por minuto
POST /api/usuarios/login10 por minuto
POST /api/usuarios5 por minuto

Excedido: 429 com {"status":429,"error":"Too Many Requests","message":"Muitas tentativas. Aguarde um momento antes de tentar novamente."}. O IP vem do primeiro valor de X-Forwarded-For quando existe, senão do endereço remoto. O contador não sobrevive a reinício e não é compartilhado entre instâncias.

10. Exemplos ​

bash
# 1) login — devolve o token em .token
curl -s -X POST "${BASE_URL}/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"login":"usuario@exemplo.com","senha":"<SENHA>"}'

# 2) chamada autenticada
curl -s "${BASE_URL}/api/auth/me" -H "Authorization: Bearer ${TOKEN}"

# 3) chamada do coletor (não usa JWT)
curl -s "${BASE_URL}/api/mes/edge/config?machineId=<TAG_DA_INJETORA>" -H "X-Edge-Key: ${EDGE_KEY}"

Resposta típica de login (estrutura de UsuarioResponseDTO; valores ilustrativos):

json
{
  "id": 12,
  "login": "usuario@exemplo.com",
  "nome": "Usuário Exemplo",
  "perfil": "OPERADOR",
  "ativo": true,
  "token": "<JWT>",
  "perfilResumo": {
    "id": 2,
    "code": "OPERADOR",
    "description": "Operador",
    "type": "FULL_ACCESS",
    "allowedScreens": ["DASHBOARD", "ALERTS", "MOVEMENTS", "REPORTS", "WAREHOUSE", "PRODUCTS", "HISTORY"],
    "active": true
  }
}

Exemplos acima não carregam credenciais nem endereços reais de produção.

Manual do usuário — Sistema Wester. Conteúdo descreve as telas conforme a versão indicada na página inicial.