Desenvolvimento Back End em Java Com Spring Boot Ebook
Desenvolvimento Back End em Java Com Spring Boot Ebook
Spring Framework.................................................................................................. 11
Entidades ........................................................................................................... 16
@OneToOne ...................................................................................................... 17
2
@OneToMany e @ManyToOne......................................................................... 18
@ManyToMany .................................................................................................. 18
Anotação @Query.............................................................................................. 22
Service ................................................................................................................... 28
Spring Tests........................................................................................................... 30
Recursos ............................................................................................................ 36
JSON.................................................................................................................. 36
HATEOAS .......................................................................................................... 36
3
Códigos de respostas HTTP .............................................................................. 38
@RestController ................................................................................................. 40
@RequestMapping ............................................................................................ 41
@GetMapping .................................................................................................... 41
@PostMapping................................................................................................... 42
@PutMapping .................................................................................................... 42
@DeleteMapping ............................................................................................... 42
@Autowired........................................................................................................ 42
OAuth 2.0............................................................................................................... 46
Dependências .................................................................................................... 48
4
Configuração do Spring Security ........................................................................ 49
Referências .............................................................................................................. 59
5
Capítulo 1 - INTRODUÇÃO AO SPRING BOOT
Para provar isso, antes de começar qualquer introdução à esta ferramenta, vamos
criar uma aplicação simples com ela.
Spring Initializr
Ferramenta que oferece uma maneira rápida de configurar uma aplicação com tudo
que ela precisa. Você seleciona os recursos que deseja utilizar e faz o download da
estrutura inicial, pronta para ser importada no seu IDE. Para o primeiro exemplo vamos
precisar apenas da dependência Spring Web.
6
4. Descompacte este arquivo em algum local do seu computador que seja de fácil
acesso.
5. No eclipse, clique em File > Import > Maven > Existing Maven Projects. Clique
em Next.
Fonte: O autor
@GetMapping("/helloworld")
public String hello() {
return "Hello World!";
}
7
Em seu navegador acesse localhost:8080/helloworld:
Fonte: O autor
Simples e fácil, não? Mas o que aconteceu aqui? O que são as anotações () que foram
adicionadas à classe? E o principal, como nossa aplicação está respondendo à
requisição que fizemos no browser? São estas respostas que serão respondidas no
decorrer do curso.
Se desejar, você pode criar os projetos diretamente de uma IDE com suporte para
desenvolvimento de projetos Spring: https://spring.io/tools.
8
Já os clientes são os consumidores dessas aplicações. Um exemplo comum de cliente
são os navegadores web, como o Chrome ou Firefox. Estes navegadores são capazes
de executar códigos em Javascript que fazem a comunicação com os servidores.
Neste cenário temos basicamente duas aplicações distintas. Uma sendo executando
no servidor, desenvolvido com uma linguagem robusta, contendo toda lógica de
negócio. A outra sendo executada no próprio navegador, utilizando uma linguagem
dinâmica como o JS, que é capaz de enviar e receber dados do servidor e manipular
páginas em html para apresentar o conteúdo de forma interativa.
Protocolo HTTP
Request e Response
Status Code: número inteiro que indica se a requisição teve sucesso ou não.Códigos
mais conhecidos são o 200 (Success), 404 (Not Found) e 403 (Access Forbiden).
Content Type: text, html, image, pdf, etc. Também são chamados de MIME type.
9
Content: dados que devem ser renderizados pelo Web Client.
200 OK
Date: Fri, 01 Jul 2016 07:59:39 GMT
Server: gws
Content-Type: text/html;charset=UTF-8I
Entendendo a URL
URL é acrônimo para Universal Resource Locator e é usado para localizar um servidor
e recursos. Todo recurso na web tem seu próprio endereço. Vamos ver as partes de
uma URL:
http://localhost:8080/Resource
Resource - Recurso requisitado do servidor (html, xml, json, pdf, imagens, etc).
A visão geral de uma aplicação web apresentada aqui considera casos onde um
usuário esteja acessando algum site estático, onde as páginas, imagens e outros
recursos são fornecidas do servidor para o cliente sem a necessidade de um
processamento de dados por parte do servidor.
Mas vamos considerar um outro exemplo, onde uma aplicação tenha funcionalidade
de cadastrar usuários em uma base de dados. Esta aplicação é então publicada em
um servidor onde poderá ser acessada por outras aplicações (clientes) que desejam
cadastrar usuários na mesma base de dados. Como estes clientes poderão acessar
esta funcionalidade?
10
a troca de dados entre eles. Ou seja, uma API é uma forma onde um sistema pode
fornecer uma funcionalidade sua para ser utilizada por outro sistema. Esta troca de
informação é realizada através de rotas e protocolos definidos, e os dados trafegados
normalmente são em formato JSON e XML.
Um Web Service (WS) funciona praticamente da mesma maneira de uma API, porém
a comunicação ocorre utilizando-se o protocolo HTTP. Um WS pode utilizar REST,
SOAP ou XML-RPC como meios de comunicação. Aqui focaremos na implementação
de um WS utilizando o estilo REST, que será introduzido mais a frente. Uma
curiosidade é que todo Web Service é uma API, mas nem toda API é um Web Service.
Spring Framework
Como você deve ter imaginado, criar uma aplicação para funcionar na Web não deve
ser tarefa fácil. Como fazer por exemplo que uma aplicação seja capaz de responder
requisições através de uma URL digitada no navegador? Como fazer para lidar com
todas particularidades de infraestrutura e protocolos? Como fazer para lidar com
requisições que enviam dados em formato XML ou JSON e que precisam ser
persistido em um banco de dados? E por falar em banco de dados, como fazer a
comunicação com eles? É tanta coisa que deveríamos nos preocupar que o mais
importante, a lógica da nossa aplicação ficaria de lado. Para nossa sorte existem
frameworks que lidam com toda essas dificuldades para nós.
Aplicações Web Java que não usam o Spring devem ser executadas a partir de um
servidor de aplicação, como o Apache Tomcat. Já o Spring traz seu próprio container
alternativo que é responsável em gerenciar todos objetos que estão dentro da
aplicação. Um change gamer importante neste cenário de aplicações corporativas.
Spring MVC
É um módulo do Spring Framework, que contém tudo o que é necessário para facilitar
o desenvolvimento de aplicações Web sob o protocolo HTTP usando o padrão MVC
(Model-View-Controller). O padrão MVC dita como devem ser separadas as lógicas
de entrada através dos Controladores, as lógicas de negócios através dos Modelos e
das lógicas de Visualizações.
12
Foi projetado em cima da Servlet API e usa o padrão de projeto Front Controller, onde
um Servlet principal, nomeado de DispatcherServlet é responsável em receber toda
requisição e direcionar para os controladores responsáveis em lidar com ela.
Os métodos das classes com estas anotações podem usar anotações específicas
para mapear os métodos HTTP, como:
GET - @GetMapping;
POST - @PostMapping;
PUT - @PutMapping;
DELETE - @DeleteMapping
Ele também traz suporte à biblioteca Jackson JSON, que possibilita a conversão de
dados em formatos JSON para classes Java e vice-versa. Esta biblioteca é muito útil
pois converte automaticamente os dados de entrada e saída dos métodos dos
Controllers.
Listei aqui apenas alguns dos recursos oferecidos pelo Spring MVC, há muitos outros
disponíveis responsáveis em auxiliar alguma parte específica do desenvolvimento
para web. Mas toda esta gama trouxe também um problema, configurar cada um
destes recursos em uma aplicação ficou complicado, pois era necessário escrever
13
cada vez mais arquivos xmls para fazer uma aplicação simples rodar. Isso fez com
que surgisse uma ferramenta para facilitar nesta tarefa.
Spring Boot
É uma ferramenta interativa para você criar suas aplicações de maneira rápida,
prontas para serem usadas em produção com a mínima ou nenhuma necessidade de
configurações através de arquivos xml. As aplicações criadas a partir desta ferramenta
podem ser executadas como se fossem aplicações Stand-Alone através do comando
“java -jar” ou a partir de pacotes WAR tradicionais.
Web Starter
Test Starter
Este starter adiciona as bibliotecas Spring Test, JUnit, Hamcrest, Mockito, etc, que são
utilizadas para desenvolver testes unitários e de integração.
14
JPA Starter
Validation Starter
Links Úteis
http://spring.io
http://start.spring.io
https://spring.io/tools
Considerações Finais
15
Capítulo 2 - PERSISTÊNCIA DE DADOS
Java Persistence API, ou JPA, é a especificação que dita como deve ser feito o
Mapeamento Objeto Relacional (ORM em inglês), para armazenamento, acesso e
gerenciamento de objetos Java em banco de dados relacionais.
No banco de dados, usamos fereign keys e join tables para definir o relacionamento
entre as tabelas. Nas entidades usamos anotações específicas para determinar estes
relacionamentos.
Entidades
Uma classe POJO pode ser definida como entidade através da anotação @Entity. Por
exemplo:
@Entity
public class Tarefa {
}
Desta maneira, a classe Tarefa será mapeada como uma tabela chamada tarefa no
banco de dados. Caso queira definir um nome alternativo para a tabela que será
criada, utilize a anotação @Table. Esta possui propriedades para customizar a criação
da tabela no banco de dados.
@Entity
@Table(name = "tarefas")
public class Tarefa {
16
Primary Key
Para determinar o atributo da classe que será utilizado como chave primária na tabela,
fazemos uso da anotação @Id. Normalmente utilizamos Integer, Long ou String como
tipo deste atributo.
@Entity
@Table(name = "tarefas")
public class Tarefa {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
Os atributos desta classe serão mapeados como colunas da tabela criada. Você
também pode utilizar a anotação @Column para customizar detalhes da coluna:
@Entity
@Table(name = "tarefas")
public class Tarefa {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "codigo")
private Long id;
@OneToOne
Utilizada para definir relações do tipo um para um, onde uma entidade tem outra
entidade como um de seus atributos. Por exemplo, uma entidade Tarefa pode fazer
ter um atributo do tipo Arquivo para armazenar uma foto no banco:
@Entity
@Table(name = "tarefas")
public class Tarefa {
@Id
17
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "codigo")
private Long id;
@OneToOne(cascade = CascadeType.REMOVE)
@JoinColumn(name = "arquivo_id")
private Arquivo arquivo;
A opção cascade informa que o registro de Arquivo relacionado com o Tarefa deve
ser apagado caso o curso seja apagado.
@OneToMany e @ManyToOne
Estas duas anotações são utilizadas para relacionamentos do tipo um para muitos.
Considere o exemplo onde uma entidade Tarefa tenha apenas um Responsavel, mas
o Responsavel pode ter vários Cursos. Este relacionamento seria mapeado assim:
@Entity
@Table(name = "tarefas")
public class Tarefa {
// ...
@ManyToOne
@JoinColumn(name = "autor_id")
private Responsavel responsavel;
Classe Responsavel:
@Entity
public class Responsavel {
@OneToMany(mappedBy = "responsavel")
private List<Tarefa> tarefas;
@ManyToMany
Esta anotação é utilizada para relações do tipo muitos para muitos. Considere o
exemplo onde várias tarefas poderiam ter vários responsáveis.
@Entity
@Table(name = "tarefas")
public class Tarefa {
@ManyToMany
@JoinTable(name = "tarefa_responsavel",
joinColumns = @JoinColumn(name="tarefa_id"),
inverseJoinColumns = @JoinColumn(name="responsavel_id"))
18
private List<Responsavel> responsavel;
// ...
}
Classe Responsavel:
@Entity
public class Responsavel {
@ManyToMany(mappedBy = "responsavel")
private List<Tarefa> tarefas;
Neste caso, a anotação @JoinTable faz com que uma tabela chamada
tarefa_responsavel seja criada com duas colunas, tarefa_id e responsavel_id. As
opções joinColumns e inverseJoinColumns diz como devem ser feitas as restrições
das chaves estrangeiras.
Spring Data JPA é um sub-projeto do Spring Data, que torna fácil a implementação de
repositórios Spring utilizando JPA. Ele é destinado para implementação da camada
de acesso a dados de uma aplicação. Estas camadas normalmente ficam poluídas
com muito código para executar operações como paginação, auditoria, além de
carregar um boilerplate de códigos referente a infraestrutura.
O Spring Data vem para resolver este problema introduzindo o conceito do padrão de
projeto Repository.
O objetivo principal dos repositórios do Spring Data é trazer uma abstração para
reduzir significativamente a quantidade de códigos que precisam ser escritos para
desenvolver a camada de acesso aos dados de uma aplicação.
Nós não precisamos implementar nada para esta interface para já sair usando. O
Spring irá gerar uma implementação padrão em tempo de execução que fornecerá
uma gama de métodos que podemos utilizar pela aplicação. A interface
CrudRepository, por exemplo, provê as funcionalidades básicas das operações de
19
CRUD. Outra interface interessante é a JPARepository, que herda os comportamentos
da interface CrudRepository, trazendo uma especialização deste repositório para
trabalhar com JPA.
Query Methods
As interface mencionadas acima trazem uma gama enorme de métodos que podemos
utilizar sem escrever uma linha de código sequer. Contudo, quando estamos
desenvolvendo aplicações CRUD, precisaremos em alguma hora utilizar um método
de consulta mais especializado. Com SpringData podemos escrever assinaturas de
métodos em nossas interfaces que seguem um padrão de nomenclatura para executar
operações no banco de dados.
Por exemplo, para buscar uma tarefa dado uma descrição, escrevemos a assinatura
do método na interface:
public interface TarefaRepository
extends JpaRepository<Tarefa, Long> {
20
Before findByStartDateBefore … where x.startDate < ?1
Fonte: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference
Este padrão já resolve boa parte das operações que precisamos fazer no banco de
dados. Mas há momentos que serão necessários criar queries mais complexas. Para
isso, o Spring Data traz a possibilidade de escrever suas próprias queries.
Por exemplo, vamos criar uma criar que recupera tarefas pelo seu status:
@Entity
@NamedQuery(name = "Tarefa.findByStatus",
query = "select t from Tarefa where t.status = ?1")
public class Tarefa extends BaseEntity {
//...
}
21
public interface TarefaRepository extends JpaRepository<Tarefa, Long> {
Anotação @Query
Usar consultas nomeadas nas entidades funciona bem para um pequeno número de
consultas. Mas existe uma outra possibilidade de declarar estas queries diretamente
na classe Repository com a anotação @Query.
Query by example
Existe uma outra maneira de criar queries de forma mais amigável através da técnica
Query by Example, que permite a criação de queries dinâmicas a partir de um objeto
de exemplo, sem a necessidade de escrever a query em si.
22
padrão, campos com valores nulos são ignorados. Já para Strings precisamos definir
alguns critérios de como ele fará a pesquisa.
Tarefa tarefa = new Tarefa();
tarefa.setDescricao("estudar java");
Estes objetos podem ser passados para o método de pesquisa padrão findAll contido
na interface JpaRepository:
return repositorio.findAll(example);
Esta interface contém uma sobrecarga do método que recebe como parâmetro o
exemplo que criamos e retorna uma lista de objetos da Entidade.
Existem outros recursos além dos que foram apresentados aqui e todos estão
documentados na documentação oficial. O link para a documentação está na seção
Links úteis deste capítulo e recomendo fortemente a leitura dela.
Os exemplos mostrados até agora pode ser executados no banco de dados H2, que
é um banco que já vem embarcado no Spring Boot. Ele é muito útil no momento que
estamos desenvolvendo nossa aplicação, mas quando a colocarmos para executar
em produção, precisaremos utilizar um banco de dados mais robusto, com mais
recursos. Um exemplo de banco muito utilizado é o MySql. Os passos a seguir
mostram como configurar a conexão com um banco deste tipo.
23
banco de dados externo. As propriedades abaixo são para o banco Mysql, mas você
pode alterar para outro banco de dados.
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_example
spring.datasource.username=usuario
spring.datasource.password=senha
Links Úteis
https://docs.spring.io/spring-data/jpa/docs/current/reference/html
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods
https://spring.io/guides/gs/accessing-data-mysql/
Considerações Finais
● Como podemos obter grande facilidade nas tarefas que envolvam a camada de
acesso a dados através do uso do Spring Data JPA.
24
Capítulo 3 - ISOLANDO REGRAS DE NEGÓCIO
Bean Validations
Depois de definida as restrições dos atributos, o objeto é testado por um validador que
verifica se as restrições impostas aos atributos são satisfeitas. Caso algum valor de
algum atributo não esteja válido, uma exceção será lançada.
● O corpo da requisição.
● Variáveis do path.
● Query parameters.
25
Validando corpo da requisição
Em métodos POST ou PUT, os objetos que são enviados pelos clientes podem ser
verificados assim que chegam ao Controller. Então, podemos adicionar anotações nos
atributos das classes que representam os recursos que são enviados, especificando
as restrições que tornam aquele objeto válido. Por exemplo:
@Entity
public class Tarefa {
@NotBlank
private String descricao;
...
}
A anotação @NotBlank informa que o atributo nome não pode ser nulo e deve conter
ao menos um caractere sem espaços vazios no início ou final do texto.
Na classe Controller, basta adicionar @Validated, para indicar que os métodos dela
devem ter os parâmetros validados antes de serem executados.
@RestController
@RequestMapping("/tarefa")
@Validated
public class TarefaController {
...
}
E cada método que se deseja validar precisa ter a anotação @Valid antes dos
parâmetros.
@PostMapping
public ResponseEntity<?> novaTarefa(
@RequestBody @Valid TarefaRequest tarefaReq) {
...
}
Desta maneira, o Spring fará a validação dos dados do objeto antes de fazer qualquer
outra coisa dentro do método.
Este tipo de validação é um pouco diferente. Neste caso estaremos validando objetos
simples, como inteiros ou strings. Não temos uma classe onde anotar um atributo,
então adicionamos a anotação de restrição direto no parâmetro na classe Controller.
Neste exemplo, a anotação @Min não permitirá valores menores que 1.
26
@GetMapping("/{id}")
public EntityModel<TarefaResponse> umaTarefa(
@PathVariable @Min(1) Long id) {
Tarefa tarefa = service.getTarefaPorId(id);
return assembler.toModel(tarefa);
}
Para fazer isso, podemos implementar um método que seja capaz de tratar este tipo
de exceção e registrá-lo como ExceptionHandler no classe anotada com
@ControllerAdvice (Mais a frente veremos com mais detalhes a utilização de classes
deste tipo. Por hora você precisa entender que são classes especiais que são capazes
de interceptar requisições que resultaram em erro). Neste caso fazemos uma herança
da nossa classe com a classe ResponseEntityExceptionHandler, que contém alguns
métodos que podemos sobrescrever para tratar melhor alguns erros. Para tratar erros
de validação, sobrescreveremos o método handleMethodArgumentNotValid:
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
27
Service
É uma boa prática definir que classes do tipo Controllers não devem ter regras de
negócio. Elas precisam ser simples, contendo apenas métodos para manipular dados
de entrada das requisições, delegar ação para outra classe, que irá de fato executar
as regras de negócio, e depois tratar o resultado obtido e formatar uma resposta a ser
devolvida para o cliente.
Esta outra classe em questão são os chamados Services, que são classes especiais
que desenvolvemos para centralizar as regras de negócio nelas. O Spring fornece a
anotação @Service para transformar uma classe em um componente que poderá ser
injetado em outras classes posteriormente.
Fonte: O autor
Dividir a aplicação desta maneira é uma boa prática pois fornece maior isolamento
entre as camadas, facilitando a manutenção do código e favorecendo o
desenvolvimento de testes de unidade.
28
@Autowired
private TarefaRepository repository;
if (!TarefaStatus.EM_ANDAMENTO.equals(tarefa.getStatus())) {
tarefa.setStatus(TarefaStatus.CANCELADA);
return salvar(tarefa);
}
if (!TarefaStatus.EM_ANDAMENTO.equals(tarefa.getStatus())) {
throw new TarefaStatusException(
"Não é possível completar uma tarefa cancelada");
}
tarefa.setStatus(TarefaStatus.TERMINADA);
return salvar(tarefa);
}
@RestController
@RequestMapping("/tarefa")
@Validated
public class TarefaController {
@Autowired
private TarefaService service;
29
// ...
}
Spring Tests
No Spring, podemos realizar estes testes utilizando as bibliotecas para testes que já
são importadas no projeto no momento em que geramos o projeto pelo Spring Initializr.
O Spring traz recursos para que você possa testar seu código de maneira unitária
(Unit Test), e testes de integração, testando códigos da camada Web (@WebMvcTest)
e da camada de persistência (@DataJpaTest).
O teste mais básico que podemos fazer nesta camada serve para verificar se os
componentes estão sendo carregados de forma correta durante a inicialização da
aplicação. Este teste também é conhecido como Teste de Sanidade (Sanity Check).
Apesar de simples, é extremamente útil, dando feedback rápido caso façamos algo no
código que ocasione erro durante o deploy.
@SpringBootTest
class MinhasTarefasApplicationTests {
@Autowired
private TarefaController controller;
@Test
void contextLoads() {
Assertions.assertThat(controller).isNotNull();
30
}
}
Além deste teste, você pode escrever testes que verificam o comportamento da
aplicação escrevendo um código que faça uma requisição HTTP e verificando se a
resposta retornada é a esperada. O teste abaixo simula uma requisição com o método
DELETE para o endpoint /tarefa/1.
@WebMvcTest(TarefaController.class)
public class TarefaControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private TarefaService service;
@MockBean
private TarefaModelAssembler assembler;
@Test
void quandoRequestForValidoRetornaSucesso() throws Exception {
mvc.perform(
MockMvcRequestBuilders.delete("/tarefa/1"))
.andExpect(
MockMvcResultMatchers.status().isNoContent());
}
@MockBean: cria e injeta um mock da classe Service na classe Controller. Sem esta
anotação o contexto da aplicação não pode ser iniciado.
31
inicializar a classe. Como não queremos testar de fato o funcionamento destas
dependências, as anotamos com @MockBean para que seja atribuído apenas um
objeto fake.
Se seu teste exige que todo contexto do Spring seja inicializado, neste caso você
estará escrevendo um teste de integração entre as camadas Web, Service e
Repository, por exemplo, então a anotação a ser utilizada é a @SpringBootTest. Esta
anotação irá carregar todo contexto da aplicação, criando instância para cada
componente anotado em seu código com @Service, @Component, @Repository,
@Configuration, etc.
Agora você pode querer testar apenas a camada Web, onde ficam os controllers.
Neste caso a anotação a ser utilizada é a @WebMvcTest. Com esta anotação o
contexto será parcialmente carregado pelo Spring. Os componentes que são anotados
com @Service, @Component, @Repository, @Configuration, etc. são ignorados. Por
isso a necessidade de utilizar um recurso de Mock, para criar instâncias fakes destes
componentes para o contexto do Spring injetar na classe que está sendo testada.
Outra observação importante a ser feita é quanto aos tipos de testes que devem ser
escritos. No geral não faz muito sentido ficar escrevendo código de teste para testar
operações como salvar entidade ou recuperar entidades do banco de dados utilizando
os métodos padrões que são fornecidos pelo framework Spring Data por exemplo.
Estas são responsabilidades dos próprio frameworks e seus desenvolvedores já
fizeram toda esta carga de teste para certificar que operação de salvar estão
funcionando corretamente. Você deve se atentar aos códigos que lidam com as regras
de negócio do seu sistema, garantindo que eles continuem funcionando mesmo
depois de alterações drásticas no código-fonte.
32
Isolar Entidades através de DTO’s
Para ilustrar, imagine uma entidade que representa os dados de um usuário. Digamos
que esta entidade possua um atributo do tipo booleano que informa se ele é um
administrador do sistema. Essa informação não deve ser exposta para o cliente, pois
revela como a estrutura de dados está arquitetada. Se alguém souber desta
informação, pode realizar requisições diretas para um endpoint tentando alterar o tipo
de seu usuário.
Para resolver este problema, criamos duas classes DTO’s, uma chamada
UsuarioRequest e outra UsuarioResponse. Estas classes terão apenas os dados que
podem ser recebidas e enviadas para o cliente.
public class UsuarioRequest {
@NotBlank
private String username;
@NotBlank
private String password;
...
@NotBlank
private String username;
@NotBlank
private String token;
...
33
Links úteis
https://spring.io/guides/gs/testing-web/
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/testing.html#testing-introduction
Considerações Finais
No próximo capítulos veremos como criar Web Services Restful com Spring MVC.
34
Capítulo 4 - RESTful COM SPRING MVC
Este estilo lista uma série de princípios que determinam uma maneira estruturada e
sustentável para desenvolvimento de softwares para a chamada Web moderna.
Utilizando REST, você tira proveito de toda infraestrutura já bem definida da Internet,
fazendo melhor uso das especificações do protocolo HTTP, URI e métodos
condizentes com cada operação que se deseja realizar. Essa padronização traz uma
série de vantatens para os sistemas desenvolvidos.
URI e URL
Note que, toda URL pode ser considerado um URI, já que ele identifica um recurso,
ou um local onde esteja o recurso. Mas nem toda URI pode ser considerada uma URL.
Isso porque podem ser utilizadas para definir “namespaces” em arquivos XML por
exemplo.
35
Recursos
Em REST tudo deve conter uma identificação, um ID. Como vimos no capítulo anterior,
o mecanismo de identificação utilizado em sistemas Web é o URI. Portanto todas as
coisas, ou recursos que serão disponibilizados, deverão ser alcançados através de
uma URI.
JSON
Note que tanto a chave quanto o valor são envoltos por aspas.
HATEOAS
E o que é Hypermedia? As páginas dos sites possuem textos e muitos outros links de
recursos que são carregados automaticamente pelo navegador, como arquivos com
scripts JavaScript, folhas de estilos CSS, imagens, sons, outras páginas, etc.
Estes links ditam para o navegador como esta página HTML deve ser renderizada,
informando onde ele deve buscar os recursos necessários, como o ícone da página e
a folha de estilo.
Figura 2.1 - Links em uma página html
36
Fonte: O autor
Estes links podem também serem utilizados nas respostas de seus serviços, para ditar
aos clientes quais os próximos passos disponíveis para navegarem. Imagine que o
cliente faça uma busca por um determinado livro (em um Web Service que retorna
livros) a resposta do serviço irá trazer além dos dados do livro um link para a próxima
ação disponível, que pode ser ou uma URI para que o usuário possa fazer a compra
do livro, ou uma URI para que o usuário seja avisado quando o livro estiver disponível
caso o exemplar já tenha sido esgotado. Este princípio traz muita dinâmica para a
comunicação cliente servidor, pois o cliente não precisa saber de antemão quais
passos ele pode seguir, o serviço irá informar.
Este princípio do REST diz para utilizar a interface de métodos já disponibilizada pelo
protocolo HTTP para fazer interações com seus serviços. Os quatro métodos mais
utilizados são:
Esses são os mais utilizados pois mapeiam muito bem as 4 operações básicas do
banco de dados, ou CRUD (Create, Retrieve, Update e Delete).
Os métodos POST e PUT carregam em seu corpo de requisição os dados que serão
inseridos ou atualizados no servidor. Os tipos de dados mais utilizados para este fim
são o XML e o JSON. Todos os métodos retornam um response.
37
Códigos de respostas HTTP
O protocolo HTTP possui estas séries distintas de código para que o cliente saiba
como lidar com a resposta do serviço. Cada série possui seu significado e deve ser
utilizado de acordo com o resultado produzido em cada ação realizada.
Série 2xx
Códigos desta série indicam sucesso na requisição realizada. Os mais comuns são:
200 - OK
201 - Created
Retornado após um POST, indica que o recurso foi criado corretamente. Retorna-se
um cabeçalho Location juntamente com a resposta com a URI do recurso criado.
202 - Accepted
204 - No content
Série 3xx
Esta série indica que deve se redirecionar a outro local para obter o recurso.
Indica que o recurso procurado foi movido para outra URI. É retornado o cabeçalho
Location indicando o local atualizado.
Indica que um processo assíncrono foi ou está sendo realizado e o recurso deve ser
obtido em outro local. É retornado o cabeçalho Location indicando o local atualizado.
38
304 - Not modified
Parecido com o 301, mas indica que o recurso foi movido temporariamente.
Série 4xx
Códigos desta série indicam que o cliente enviou dados errados na requisição.
Código genérico indicando que ocorreu erro no processamento devido a qualquer erro
nos dados enviados do cliente para o serviço.
401 - Unauthorized
403 - Forbidden
409 - Conflict
Retornado em métodos POST, quando se tenta criar um recurso mas este recurso já
existe. Retorna o cabeçalho Location indicando o local do recurso.
39
Série 5xx
Códigos deste tipo referem-se a erros ocorridos por causa de problemas no servidor.
Indica que o servidor está funcionando mas o serviço específico não está respondendo
corretamente.
Uma lista completa dos códigos disponíveis no protocolo HTTP pode ser consultado
através do link: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
O uso de estilo se popularizou devido aos benefícios trazidos pelo protocolo HTTP,
como as ações bem definidas através de seus métodos (GET, POST, PUT, DELETE),
sua segurança (criptografia e autenticação), desacoplamento entre cliente e servidor,
entre outros.
Agora vamos ver como aplicar estes princípios na prática desenvolvendo um Web
Service RESTful com Spring MVC. Será um serviço simples de controle de tarefas a
fazer onde será possível demonstrar todos os princípios de RESTful escrito aqui.
O Spring MVC traz suporte para o desenvolvimento de Web Services RESTful através
de componentes especializados. Nas próximas seções vamos ver cada um deles em
ação no desenvolvimento de um serviço na prática.
@RestController
RestController é uma anotação fornecido pelo Spring MVC para facilitar a criação de
Web Services RESTful. Ela é adicionada no topo da declaração de uma classe Java
e faz com que a classe passe a ter a capacidade de responder à requisições feitas
pelo protocolo HTTP.
Nesta classe, cada método precisa ser anotado com outras anotações para indicar
qual método do HTTP o respectivo método será responsável em tratar. Cada
40
requisição tratada pelos métodos de um RestController serializam objetos
automaticamente e retornam um HttpResponse.
@RestController
@RequestMapping("/tarefa")
public class TarefaController {
@Autowired
private TarefaService service;
@PostMapping
public Tarefa salvar(@RequestBody Tarefa tarefa) {
return service.salvar(tarefa);
}
@GetMapping
public List<Tarefa> pesquisa(Tarefa filtro) {
return service.filtraPor(filtro);
}
@PutMapping("{id}")
public Tarefa atualizar(@PathVariable Integer id,
@RequestBody Tarefa tarefa) {
return service.atualizar(id, tarefa);
}
@DeleteMapping("{id}")
public void excluir(@PathVariable Integer id) {
service.excluir(id);
}
}
O código acima mostra um exemplo de uma classe que foi anotada com
@RestController tornando-a então um componente do Spring capaz de responder
requisições como mencionado anteriormente.
@RequestMapping
@GetMapping
Anotação que indica que o método da classe irá mapear requisições do tipo HTTP
GET.
41
@PostMapping
Anotação que indica que o método da classe irá mapear requisições do tipo HTTP
POST.
@PutMapping
Anotação que indica que o método da classe irá mapear requisições do tipo HTTP
PUT.
@DeleteMapping
Anotação que indica que o método da classe irá mapear requisições do tipo HTTP
DELETE.
@Autowired
Anotação que permite ao Spring MVC identificar e injetar (em tempo de execução)
instâncias de objetos em outros através do recurso de Injeção de Dependência.
Esta inversão de controle pode ser realizada através de outro conceito conhecido
como Injeção de Dependência.
A Injeção de Dependência pode ser entendido como a ação de conectar objetos com
outros. Em uma classe normalmente temos relações com diversas outras classes,
cada uma com sua responsabilidade. Normalmente compomos classes dependentes
de outras para aproveitar a funcionalidade disponível em cada uma delas. Para utilizar
estas classes relacionadas precisamos em algum momento instanciá-las, caso
contrário teremos um erro ao acessar objeto nulo.
42
atributo de nossa classe. No exemplo abaixo temos a representação de como ficaria
a injeção de um objeto do tipo CursoService dentro do controller apresentado
anteriormente.
public class CursoController {
}
Neste exemplo CursoService pode ser uma interface, que aceita qualquer instância
de sua implementação. O problema aqui é que quem for utilizar a classe
CursoController, precisa antes de tudo instanciar um objeto do tipo CursoService para
passar no construtor de CursoController no momento de sua utilização. Um trabalho
um pouco mais complicado.
O Spring traz uma série de anotações que facilitam muito a configuração da aplicação
em vários aspectos, como por exemplo, tratar exceções ocorridas para mapear
códigos de respostas mais adequados.
ControllerAdvice é uma anotação que faz uma classe compartilhar seus métodos com
outras classes anotadas com @Controller.
43
criados diversos métodos, cada um responsável em fornecer um tratamento adequado
para cada exceção ocorrida na aplicação toda.
@ControllerAdvice
public class CustomGlobalExceptionHandler
extends ResponseEntityExceptionHandler {
@ResponseBody
@ExceptionHandler(EntityNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
String entityNotFoundHandler(EntityNotFoundException ex) {
return "Recurso não encontrado";
}
@ResponseBody
@ExceptionHandler(TarefaStatusException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
ResponseEntity<?> alteraStatusTarefaHandler(TarefaStatusException ex) {
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
.header(HttpHeaders.CONTENT_TYPE,
MediaTypes.HTTP_PROBLEM_DETAILS_JSON_VALUE)
.body(Problem.create().withTitle("Método não permitido")
.withDetail("Você não pode realizar esta operação: " +
ex.getMessage()));
}
}
A anotação @ResponseBody indica que o retorno do método deve ser associado ao
corpo da resposta de uma requisição web. Ou seja, o objeto retornado será serializado
em um tipo adequado para o cliente que fez a requisição a este recurso.
Links úteis
https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
44
Dissertação de Roy Fielding introduzindo os princípios REST
https://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf
Considerações Finais
45
Capítulo 5 - SPRING SECURITY
Autenticação e Autorização
OAuth 2.0
OAuth é um protocolo aberto que permite fazer a segurança de aplicações web, mobile
e desktop de maneira simplificada. Cria uma camada de segurança que separar a
responsabilidade do cliente e do servidor no fluxo de autorização.
46
token de acesso para o cliente, que deverá utilizá-lo toda vez que requisitar um recurso
no serviço
JSON Web Token (JWT) é um padrão aberto que define como deve ser transmitida
informações entre duas partes de maneira segura utilizando objeto JSON. As
informações contidas dentro destes objetos são confiáveis pois são assinadas
digitalmente através de um algoritmo seguro.
Fonte: https://jwt.io/introduction/
Fluxo de autenticação
47
Fonte: O autor
Nesse fluxo, o cliente fica responsável em manter o token obtido do servidor e enviar
em cada requisição feita para algum recurso REST. O servidor não mantém o estado
da sessão.
Dependências
<dependency>
<groupId>io.jsonwebtoken</groupId>
48
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
@Autowired
UserDetailsServiceImpl userDetailsService;
@Autowired
private AuthEntryPointJwt unauthorizedHandler;
@Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
@Override
protected void configure(
AuthenticationManagerBuilder authenticationManagerBuilder)
throws Exception {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
49
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**")
.permitAll()
.antMatchers("/tarefa/**")
.hasAnyRole("USER", "ADMIN")
.antMatchers("/h2-console/**")
.permitAll()
.antMatchers("/api/test/**")
.permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(authenticationJwtTokenFilter(),
UsernamePasswordAuthenticationFilter.class);
}
}
@EnableWebSecuriy: Spring encontra e aplica a classe como configuração global.
@Service
public class UserDetailsServiceImpl
implements UserDetailsService {
@Autowired
UsuarioRepository userRepository;
@Transactional
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
50
"Usuário não encontrado"));
return UserDetailsImpl.build(user);
}
}
Para buscar o usuário no repositório vamos implementar a clase UsuarioRepository
com o método findByUsername.
@Repository
public interface UsuarioRepository
extends JpaRepository<Usuario, Integer> {
@Id
@GeneratedValue
private Integer id;
@NotBlank
@Size(max = 20)
private String username;
@NotBlank
@Size(max = 120)
private String password;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<Role>();
public Usuario() {}
// ...
}
Também vamos criar a entidade Role para armazenar as funções do usuário.
@Entity
@Table(name = "roles")
public class Role {
51
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Enumerated(EnumType.STRING)
@Column(length = 20)
private ERole name;
public Role() {}
// ...
}
O Enum ERole:
public enum ERole {
ROLE_USER, ROLE_ADMIN
}
Voltando para classe UserDetailsServiceImpl, o método loadUserByUsername precisa
retornar um objeto do tipo UserDetails. Então vamos criar a classe UserDetailsImpl
que utilizará os dados do usuário que foi recuperado do banco de dados.
public class UserDetailsImpl implements UserDetails {
@JsonIgnore
private String password;
52
// ...
}
@Value("${app.jwt.SecretKey}")
private String jwtSecret;
@Value("${app.jwt.ExpirationMs}")
private Integer jwtExpirationMs;
return Jwts.builder()
.setSubject(userPrincipal.getUsername())
.setIssuedAt(currentTime)
.setExpiration(expirationTime)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
app.jwt.SecretKey= ChaveSecreta
app.jwt.ExpirationMs= 28800000
53
Precisamos agora criar um filtro Servlet que irá interceptar toda requisição realizada.
Vamos chamá-lo de AuthTokenFilter e deve estender de OncePerRequestFilter.
public class AuthTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource()
.buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
return null;
}
}
Para finalizar a configuração do Spring Security, vamos adicionar a classe
AuthEntryPointJwt que será um ExceptionHandler responsável em interceptar uma
requisição de usuário não autenticado e mudar o HttpStatus para 401.
@Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint {
54
@Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException)
throws IOException, ServletException {
response.sendError(
HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
}
}
As classes a seguir são para tratar do cadastro e autenticação do usuário.
UsuarioController:
@RestController
@RequestMapping("/api/auth")
public class UsuarioController {
@Autowired
private UsuarioService authService;
@PostMapping("/signin")
public ResponseEntity<?> signin(
@Valid @RequestBody LoginRequest loginRequest) {
return ResponseEntity.ok(jwtResponse);
}
@PostMapping("/signup")
public ResponseEntity<?> signup(
@Valid @RequestBody SignupRequest signupRequest) {
authService.registerUser(
signupRequest.getUsername(),
signupRequest.getPassword(),
signupRequest.getConfirmPassword(),
signupRequest.getRoles());
}
Os DTO’s:
public class LoginRequest {
@NotBlank
private String username;
@NotBlank
private String password;
// ...
}
55
private String username;
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UsuarioRepository userRepository;
@Autowired
RoleRepository roleRepository;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
JwtUtils jwtUtils;
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
56
authentication.getPrincipal();
if (userRepository.existsByUsername(username)) {
throw new EntityExistsException("Usuário já existe");
}
if (strRoles == null) {
Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new EntityNotFoundException(
"Role não encontrada"));
roles.add(userRole);
} else {
strRoles.forEach(role -> {
switch (role) {
case "admin":
Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN)
.orElseThrow(() -> new EntityNotFoundException(
"Role não encontrada"));
roles.add(adminRole);
break;
default:
Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new EntityNotFoundException(
"Role não encontrada"));
roles.add(userRole);
}
});
}
user.setRoles(roles);
userRepository.save(user);
}
57
Configurar o Spring Security em uma aplicação REST não é tão simples como
podemos ver, mas é um passo essencial para garantir a segurança.
Links úteis
Padrão OAuth:
https://oauth.net/2/
https://jwt.io/
https://spring.io/projects/spring-security
Considerações Finais
58
Referências
BOAGLIO, Fernando. Spring Boot - Acelere o desenvolvimento de microsserviços.
São Paulo: Casa do Código, 2018. 154p (Livros para o programador).
FIELDING, Roy Thomas. Architectural Styles and the Design of Network-based
Software Architectures. 2000. Disponível em:
https://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf.
WEISSMANN, Henrique Lobo. Vire o jogo com Spring Framework. São Paulo: Casa
do Código, 2018. 314p (Livros para o programador).
ACCESSING DATA WITH JPA. Spring.io, 2020. Disponível em:
https://spring.io/guides/gs/accessing-data-jpa. Acesso em: 17 out. 2020.
SPRING BOOT - REFERENCE DOCUMENTATION. Spring.io, 2020. Disponível em:
https://docs.spring.io/spring-boot/docs/current/reference/html/. Acesso em: 17 out.
2020.
BUILDING REST SERVICES WITH SPRING. Spring.io, 2020. Disponível em:
https://spring.io/guides/tutorials/rest/. Acesso em: 17 out. 2020.
EXCEPTION HANDLING IN SPRING MVC Spring.io, 2020. Disponível em:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc. Acesso em: 17
out. 2020.
SPRING DATA JPA - REFERENCE DOCUMENTATION. Spring.io, 2020. Disponível
em: https://docs.spring.io/spring-data/jpa/docs/current/reference/html. Acesso em: 17
out. 2020.
TESTING THE WEB LAYER. Spring.io, 2020. Disponível em:
https://spring.io/guides/gs/testing-web. Acesso em: 17 out. 2020.
WEB ON SERVLET STACK. Spring.io, 2020. Disponível em:
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/web.html. Acesso em: 17 out. 2020.
WORKING WITH SPRING DATA REPOSITORIES. Spring.io, 2020. Disponível
em:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.
Acesso em: 17 out. 2020.
59