mvn clean test
MP REST JWT com Public key do MP Config
Este é um exemplo de como configurar e usar o MicroProfile JWT 1.1 no TomEE.
Executando o teste
Este projeto inclui uma aplicação de exemplo e um teste do Arquillian para mostrar o controle de acesso baseado em função (RBAC) com JWT no MicroProfile. Para executar o cenário, você pode executar o seguinte comando:
A aplicação representa um recurso REST de livraria com alguns endpoints. Todos esperam que o cliente forneça um JSON Web Token (JWT) válido, representando um usuário com determinadas funções. O teste do Arquillian é responsável por gerar os JWTs e anexá-los os requests.
Configuração no TomEE
Para ativar o JWT, você precisa anotar sua classe de aplicação REST com a anotação org.eclipse.microprofile.auth.LoginConfig
.
Neste exemplo, a classe é ApplicationConfig
.
Outra coisa que precisa ser feita é configurar a public key
para verificar a assinatura do JWT anexada no cabeçalho Authorization'.
É assinado na criação com a `private key
do issuer.
Isso é feito para evitar adulteração do token enquanto ele trafega do chamador até o endpoint.
Geralmente, a emissão do JWT ocorre em um módulo ou microsserviço especial responsável pela autenticação dos usuários.
Neste projeto de exemplo, isso acontece no BookstoreTest
.
Cada ambiente de execução que suporta o MicroProfile JWT deveria ser capaz de verificar se a assinatura está correta e se o conteúdo assinado não é alterado ao longo do caminho.
Para fazer isso, ele precisa ter acesso a uma public key
.
Essa public key
pode estar no formato PKCS#8 PEM, JWK ou JWKS
.
Desde o MP JWT 1.1 (que é suportado pelo TomEE), a chave pode ser fornecida como uma string na propriedade de configuração mp.jwt.verify.publickey
ou como um local ou URL de arquivo especificado em mp.jwt.verify.publickey.location
na propriedade de configuração.
Neste projeto de exemplo, você pode ver a primeira opção.
O arquivo src/main/resource/META-INF/microprofile-config.properties
contém a seguinte entrada:
mp.jwt.verify.publickey=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB
Trabalhando com JWT
A classe BookResource
neste projeto de exemplo mostra dois casos em que você pode usar a especificação MP JWT: obter o valor de uma declaração JWT e o controle de acesso baseado em função dos endpoints REST.
Obtenção de valores de claim
O JSON Web Token (JWT) anexado no cabeçalho HTTP Authorization
é essencialmente um objeto JSON que contém vários atributos.
Esses atributos são chamados claims.
Você pode obter o valor de cada claim dentro de um bean CDI injetando-o e qualificando-o com a anotação @Claim
.
Por exemplo, se você deseja recuperar a claim de nome de usuário preferida, fazendo o seguinte:
@Inject
@Claim(standard = Claims.preferred_username)
private String userName;
Observe que você não pode injetar claims dessa maneira em uma classe de recurso REST que também contém endpoints não autenticados. No entanto, o TomEE tentará extrair a claim do JWT. Portanto, se não houver JWT ou se a claim não estiver lá, o request falhará. |
Controle de acesso baseado em função (RBAC)
Uma das claims padrão definidas na especificação MP JWT é groups
.
Ele contém uma lista de Strings, que representam os grupos aos quais o chamador pertence.
A especificação não distingue funções e grupos de usuários.
Portanto, a declaração groups
também pode conter as funções atribuídas a um determinado usuário.
Nesse sentido, o MP JWT possui grande integração com os mecanismos de segurança Java EE existentes, como a anotação @RolesAllowed
.
Portanto, o seguinte método BookResource
pode ser chamado pelos usuários que estão na função reader
ou manager
(ou em ambos):
@GET
@Path("/{id}")
@RolesAllowed({"manager", "reader"})
public Book getBook(@PathParam("id") int id) {
return booksBean.getBook(id);
}
No entanto, o método abaixo resultará no código HTTP 403 se chamado por um usuário que não possui a função manager
em sua declaração groups
:
@POST
@RolesAllowed("manager")
public void addBook(Book newBook) {
booksBean.addBook(newBook);
}
O teste do bookstore
O projeto de exemplo contém um teste Arquillian (org.superbiz.bookstore.BookstoreTest
) usado por alguns motivos:
-
Geração do JSON Web Token (JWT)
-
Apresentação do comportamento do TomEE em diferentes situações
-
Recuperando um valor de Claim
-
Chamando endpoints REST com funções apropriadas
-
Chamando um endpoint REST com uma função incorreta (resultando no código de status HTTP 403)
-
Chamando um endpoint REST sem JWT (resultando no código de status HTTP 401)
-