{"id":19538067,"url":"https://github.com/augustomello09/cursospringbackend","last_synced_at":"2026-05-02T22:32:10.007Z","repository":{"id":63867425,"uuid":"563815428","full_name":"AugustoMello09/cursoSpringBackend","owner":"AugustoMello09","description":"Estudo sobre uma aplicação Backend. (Curso Projeto full stack completo! Java Spring Boot no back end e Ionic no front end do Prof. Dr. Nelio Alves)","archived":false,"fork":false,"pushed_at":"2023-01-10T12:38:16.000Z","size":182,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-08T18:15:58.350Z","etag":null,"topics":["aws-s3","h2-database","java","jwt","mysql","spring-boot"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AugustoMello09.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-11-09T11:54:57.000Z","updated_at":"2022-11-26T13:24:09.000Z","dependencies_parsed_at":"2023-02-08T18:45:44.269Z","dependency_job_id":null,"html_url":"https://github.com/AugustoMello09/cursoSpringBackend","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AugustoMello09%2FcursoSpringBackend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AugustoMello09%2FcursoSpringBackend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AugustoMello09%2FcursoSpringBackend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AugustoMello09%2FcursoSpringBackend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AugustoMello09","download_url":"https://codeload.github.com/AugustoMello09/cursoSpringBackend/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240793184,"owners_count":19858620,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-s3","h2-database","java","jwt","mysql","spring-boot"],"created_at":"2024-11-11T02:31:10.272Z","updated_at":"2026-05-02T22:32:09.966Z","avatar_url":"https://github.com/AugustoMello09.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Backend\n\n## CRUD\n\n### Objetivo geral:\n\nImplementar operações de CRUD e de casos de uso conforme boas práticas de Engenharia de Software.\nCompetências:\n- Implementar requisições POST, GET, PUT e DELETE para inserir, obter, atualizar e deletar\nentidades, respectivamente, seguindo boas práticas REST e de desenvolvimento em camadas.\n- Trabalhar com DTO (Data Transfer Object)\n- Trabalhar com paginação de dados\n- Trabalhar com validação de dados com Bean Validation (javax.validation)\n- Criar validações customizadas\n- Fazer tratamento adequado de exceções (incluindo integridade referencial e validação)\n- Efetuar consultas personalizadas ao banco de dados\nNota: ressaltamos novamente que CRUD's também são casos de uso, mas estamos chamando de casos de uso os\nusos do sistema correspondentes a processos de negócio que não se enquadram em CRUD's comuns\n\n### Anotações de estudo\n\n- Trabalhar com DTO (Data Transfer Object),Ele vai ser o objeto para definir os dados que você quer trafegar quando for fazer operações básicas\n\n## Apresentando o caso de uso\n\n![registrar pedido](https://user-images.githubusercontent.com/101072311/201928965-44a519fd-8cf1-4a1a-93c6-0dbc6fccd283.png)\n\n![cenários adversos](https://user-images.githubusercontent.com/101072311/201928939-1f2606af-d7f7-4922-823e-21168aa3af0b.png)\n\n\n#### Criando o método POST\n\n(Resource)\n~~~JAVA\n@RequestMapping(method = RequestMethod.POST)\n\tpublic ResponseEntity\u003cVoid\u003e insert(@RequestBody Categoria obj) {\n\t\tobj = service.insert(obj);\n    // chamada que pega a URI do novo recurso que foi inserido\n    URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path(\"/{id}\").buildAndExpand(obj.getId()).toUri();\n\n\t\treturn ResponseEntity.created(uri).build();\n\t}\n~~~\n(Service)\n~~~JAVA\npublic Categoria insert(Categoria obj) {\n\t\tobj.setId(null);\n\t\treturn repo.save(obj);\n\t}\n~~~\n\nTestando no Postmam (categorias/{id} POST )\n\n![POST1](https://user-images.githubusercontent.com/101072311/200845019-62876c92-eedb-4332-b0a2-308d0479db14.png)\n\n![POST2](https://user-images.githubusercontent.com/101072311/200845032-25f4b4e8-2a56-4154-88f7-e032e92cb95a.png)\n\n#### Criando o método PUT\n(Resource)\n~~~JAVA\n@RequestMapping(value = \"/{id}\", method = RequestMethod.PUT)\n\tpublic ResponseEntity\u003cVoid\u003e update(@RequestBody Categoria obj, @PathVariable Integer id){\n\t\tobj.setId(id);\n\t\tobj = service.update(obj);\n\t\treturn ResponseEntity.noContent().build();\n\t}\n~~~\n(Service)\n~~~JAVA\npublic Categoria update(Categoria obj) {\n\t\tfind(obj.getId());\n\t\treturn repo.save(obj);\n\t}\n~~~\n\n#### Criando o método DELETE\n(Resource)\n~~~JAVA\n@RequestMapping(value = \"/{id}\", method = RequestMethod.DELETE)\n\tpublic ResponseEntity\u003cVoid\u003e delete(@PathVariable Integer id) {\n\t\tservice.delete(id);\n\n\t\treturn ResponseEntity.noContent().build();\n\t}\n~~~\n##### Tratamento de Erro e mensagem personalizada\n\n~~~JAVA\npublic class DataIntegrityException  extends RuntimeException{\n\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic DataIntegrityException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic DataIntegrityException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n~~~\n\n~~~JAVA\n@ExceptionHandler(DataIntegrityException.class)\n\tpublic ResponseEntity\u003cStandardError\u003e dataIntegrity(DataIntegrityException e, HttpServletRequest request){\n\n\t\tStandardError err = new StandardError(HttpStatus.BAD_REQUEST.value(), e.getMessage(), System.currentTimeMillis());\n\t\treturn ResponseEntity.status(HttpStatus.BAD_REQUEST).body(err);\n\t}\n~~~\n\n![deleteerro](https://user-images.githubusercontent.com/101072311/200844990-570c1830-b46e-4764-985e-b7fa2e24d807.png)\n\n(Service)\n~~~JAVA\npublic void delete(Integer id) {\n\t\tfind(id);\n\t\ttry {\n\t\t\trepo.deleteById(id);\n\t\t}\n\t\t catch (DataIntegrityViolationException e) {\n\t\t\tthrow new DataIntegrityException(\"Não é possível excluir uma categoria que possui produto\");\n\t\t}\n\t}\n~~~\n\nTestando no Postmam (categorias/{id} DELETE )\n\n![POST2](https://user-images.githubusercontent.com/101072311/200845032-25f4b4e8-2a56-4154-88f7-e032e92cb95a.png)\n\n![deletado](https://user-images.githubusercontent.com/101072311/200844975-78a60321-2e24-4b10-ba71-86591062f151.png)\n\n#### Criando a CategoriaDTO\nEle vai ser o objeto para definir os dados que você quer trafegar quando for fazer operações básicas de Categoria.\nObs:Nela também acompanha  Getters e Setters, mas não foi colocado na parte da documentação para não atrapalhar a visualização.\n\n(DTO)\n~~~JAVA\npublic class CategoriaDTO implements Serializable {\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate Integer id;\n\tprivate String nome;\n\n\tpublic CategoriaDTO() {\n\t}\n\n  public CategoriaDTO(Categoria obj) {\n\t\tid = obj.getId();\n\t\tnome = obj.getNome();\n\t}\n}  \n~~~\n\n#### método GET que lista todas as categorias\n\n(Resource)\n~~~JAVA\n@RequestMapping( method = RequestMethod.GET)\n\tpublic ResponseEntity\u003cList\u003cCategoriaDTO\u003e\u003e findAll() {\n\t// busca as listas de categoria do Banco\n\tList\u003cCategoria\u003e list = service.findAll();\n\t// vai ter que percorrer a list e para cada elemento da lista vai instanciar o DTO correspondente(converter para DTO)\n\tList\u003cCategoriaDTO\u003e\tlistDto = list.stream().map(obj -\u003e new CategoriaDTO(obj)).collect(Collectors.toList());\n\treturn ResponseEntity.ok().body(listDto);\n\t}\n~~~\n\nTestando no Postman categorias/\n\n![getcategorias](https://user-images.githubusercontent.com/101072311/200922718-01292407-7fbd-47bc-b98d-e68985b268a7.png)\n\n#### paginação com parâmetros opcionais na requisição\n\n(Resouce)\n~~~JAVA\n@RequestMapping(value=\"/pages\", method = RequestMethod.GET)\n\tpublic ResponseEntity\u003cPage\u003cCategoriaDTO\u003e\u003e findPage(\n\t\t\t@RequestParam(value =\"page\", defaultValue=\"0\")Integer page,\n\t\t\t@RequestParam(value =\"linesPerPage\", defaultValue=\"24\")Integer linesPerPage,\n\t\t\t@RequestParam(value =\"orderBy\", defaultValue=\"nome\")String orderBy,\n\t\t\t@RequestParam(value =\"direction\", defaultValue=\"ASC\")String direction) {\n\t// busca as páginas de categoria do Banco\n\tPage\u003cCategoria\u003e list = service.findPage(page, linesPerPage, orderBy, direction);\n\t// vai ter que percorrer as páginas e para cada elemento da vai instanciar o DTO correspondente(converter para DTO)\n\tPage\u003cCategoriaDTO\u003e\tlistDto = list.map(obj -\u003e new CategoriaDTO(obj));\n\treturn ResponseEntity.ok().body(listDto);\n\n\t}\n~~~\n(Service)\n~~~JAVA\npublic Page\u003cCategoria\u003e findPage(Integer page, Integer linesPerPage, String orderBy, String direction) {\n\t\tPageRequest pageRequest = PageRequest.of(page, linesPerPage, Direction.valueOf(direction),\n\t\t\t\torderBy);\n\t\treturn repo.findAll(pageRequest);\n\t}\n~~~\n\ntestando no Postmam (categorias/pages?linesPerPage=3)\n\n![linesPerPagestest](https://user-images.githubusercontent.com/101072311/201099282-b5049464-dade-4680-a49d-aa16a74ceaf0.png)\n\n### Validações de dados\n\n- __Sem acesso a dados__\n\t* __Sintáticas__\n\t\t- Campo não pode ser vazio\n\t\t- Valor numérico mínimo  e máximo\n\t\t- Comprimento de string mínimo e máximo\n\t\t- Somente dígitos\n\t\t- Padrão (expressão regular): (##)-####-####\n\t* __Outros__\n\t\t- Data futura / passada\n\n\t* __Mais de um campo__\n\t\t-\t Confirmação de senha igual à senha\n\n- __Com acesso a dados__\t\t\t\n\t* Email não pode ser repedito\n\t* Cada cliente pode cadastrar no máximo três cupons por mês\n\n\n#### validação sintática bom Bean Validation\n(CategoriaDTO)\n~~~JAVA\n@NotEmpty(message=\"Preenchimento obrigatório\")\n\t@Length(min=5, max=80, message=\"O tamanho deve ser entre 5 e 80 caracteres\")\n\tprivate String nome;\n~~~\n(CategoriaService)\n~~~JAVA\n// método auxiliar que instancia uma categoria através de um DTO\n\tpublic Categoria fromDTO(CategoriaDTO objDto) {\n\t\treturn new Categoria(objDto.getId(), objDto.getNome());\n\t}\n~~~\n\n(CategoriaResource) Atualização no POST\n~~~JAVA\n@RequestMapping(method = RequestMethod.POST)\n\n\tpublic ResponseEntity\u003cVoid\u003e insert(@Valid @RequestBody CategoriaDTO objDto) {\n\t\tCategoria obj = service.fromDTO(objDto);\n\t\tobj = service.insert(obj);\n\t\tURI uri = ServletUriComponentsBuilder.fromCurrentRequest().path(\"/{id}\").buildAndExpand(obj.getId()).toUri();\n\n\t\treturn ResponseEntity.created(uri).build();\n\t}\n}\n~~~\n(CategoriaResource) Atualização no PUT\n~~~JAVA\n@RequestMapping(value = \"/{id}\", method = RequestMethod.PUT)\n\tpublic ResponseEntity\u003cVoid\u003e update(@Valid @RequestBody CategoriaDTO objDto, @PathVariable Integer id) {\n\t\tCategoria obj = service.fromDTO(objDto);\n\t\tobj.setId(id);\n\t\tobj = service.update(obj);\n\t\treturn ResponseEntity.noContent().build();\n}\t\t\n~~~\nTestando o Preenchimento obrigatório\n\n![vazio](https://user-images.githubusercontent.com/101072311/201107061-e7c8ade5-d0c9-433b-884b-33f3784516c5.png)\n\n![Putcom erro](https://user-images.githubusercontent.com/101072311/201099228-2e3e0630-55eb-4203-bd49-7448390f459b.png)\n\nTestando o limite de criação\n\n![até4](https://user-images.githubusercontent.com/101072311/201107051-02718fde-a940-47f7-b869-d3c67998a0a7.png)\n\n\n![putcomerro](https://user-images.githubusercontent.com/101072311/201099243-6e1c9ee1-0eb3-450d-b574-73f64692e0f5.png)\n\n##### Personalizando a validação\n\n(Criando a FielMessage)\n~~~JAVA\npublic class FieldMessage implements Serializable {\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate String fieldName;\n\tprivate String message;\n\n\tpublic FieldMessage() {\n\n\t}\n\n\tpublic FieldMessage(String fieldName, String message) {\n\t\tsuper();\n\t\tthis.fieldName = fieldName;\n\t\tthis.message = message;\n\t}\n\n\tpublic String getFielName() {\n\t\treturn fieldName;\n\t}\n\n\tpublic void setFielName(String fieldName) {\n\t\tthis.fieldName = fieldName;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\tthis.message = message;\n\t}\n\n}\n~~~\n(Criando validationError)\n~~~JAVA\npublic class ValidationError extends StandardError {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\n\tprivate List\u003cFieldMessage\u003e errors  = new ArrayList\u003c\u003e();\n\n\n\tpublic ValidationError(Integer status, String msg, Long timeStamp) {\n\t\tsuper(status, msg, timeStamp);\n\n\t}\n\n\n\tpublic List\u003cFieldMessage\u003e getErrors() {\n\t\treturn errors;\n\t}\n\n\n\tpublic void addError(String fieldName, String messagem) {\n\t\terrors.add(new FieldMessage(fieldName, messagem));\n\t}\n\n}\n~~~\n\n#### Criando a ClienteDTO\nEle vai ser o objeto para definir os dados que você quer trafegar quando for fazer operações básicas de cliente e com algumas validações.\nObs:Nela também acompanha  Getters e Setters, mas não foi colocado na parte da documentação para não atrapalhar a visualização.\n\n(DTO)\n~~~JAVA\npublic class ClienteDTO implements Serializable {\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate Integer id;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\t@Length(min=5, max=120, message=\"O tamanho deve ser entre 5 e 120 caracteres\")\n\tprivate String nome;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\t@Email(message=\"Email inválido\")\n\tprivate String email;\n}\t  \n~~~\n\n#### método GET/PUT e DELETE para cliente\n\n(Resource)\n~~~JAVA\n@RequestMapping(value = \"/{id}\", method = RequestMethod.PUT)\n\tpublic ResponseEntity\u003cVoid\u003e update(@Valid @RequestBody ClienteDTO objDto, @PathVariable Integer id) {\n\t\tCliente obj = service.fromDTO(objDto);\n\t\tobj.setId(id);\n\t\tobj = service.update(obj);\n\t\treturn ResponseEntity.noContent().build();\n\t}\n\n\t@RequestMapping(value = \"/{id}\", method = RequestMethod.DELETE)\n\tpublic ResponseEntity\u003cVoid\u003e delete(@PathVariable Integer id) {\n\t\tservice.delete(id);\n\n\t\treturn ResponseEntity.noContent().build();\n\n\t}\n\n\t@RequestMapping( method = RequestMethod.GET)\n\tpublic ResponseEntity\u003cList\u003cClienteDTO\u003e\u003e findAll() {\n\t// busca as listas de clientes do Banco\n\tList\u003cCliente\u003e list = service.findAll();\n\t// vai ter que percorrer a list e para cada elemento da lista vai instanciar o DTO correspondente(converter para DTO)\n\tList\u003cClienteDTO\u003e\tlistDto = list.stream().map(obj -\u003e new ClienteDTO(obj)).collect(Collectors.toList());\n\treturn ResponseEntity.ok().body(listDto);\n\n\t}\n~~~\n\n#### paginação com parâmetros opcionais na requisição\n\n(Resouce)\n~~~JAVA\n@RequestMapping(value=\"/pages\", method = RequestMethod.GET)\n\tpublic ResponseEntity\u003cPage\u003cClienteDTO\u003e\u003e findPage(\n\t\t\t@RequestParam(value =\"page\", defaultValue=\"0\")Integer page,\n\t\t\t@RequestParam(value =\"linesPerPage\", defaultValue=\"24\")Integer linesPerPage,\n\t\t\t@RequestParam(value =\"orderBy\", defaultValue=\"nome\")String orderBy,\n\t\t\t@RequestParam(value =\"direction\", defaultValue=\"ASC\")String direction) {\n\t// busca as paginas de clientes do Banco\n\tPage\u003cCliente\u003e list = service.findPage(page, linesPerPage, orderBy, direction);\n\t// vai ter que percorrer as páginas e para cada elemento da vai instanciar o DTO correspondente(converter para DTO)\n\tPage\u003cClienteDTO\u003e\tlistDto = list.map(obj -\u003e new ClienteDTO(obj));\n\treturn ResponseEntity.ok().body(listDto);\n\n\t}\n~~~\n(Service)\n~~~JAVA\npublic Cliente update(Cliente obj) {\n\t\tCliente newObj = find(obj.getId());\n\t\tupdateData(newObj, obj);\n\n\t\treturn repo.save(newObj);\n\t}\n\n\tpublic void delete(Integer id) {\n\t\tfind(id);\n\t\ttry {\n\t\t\trepo.deleteById(id);\n\t\t}\n\t\t catch (DataIntegrityViolationException e) {\n\t\t\tthrow new DataIntegrityException(\"Não é possível excluir porque há entidades relacionadas\");\n\t\t}\n\t}\n\n\tpublic List\u003cCliente\u003e findAll() {\n\t\treturn repo.findAll();\n\t}\n\n\tpublic Page\u003cCliente\u003e findPage(Integer page, Integer linesPerPage, String orderBy, String direction) {\n\t\tPageRequest pageRequest = PageRequest.of(page, linesPerPage, Direction.valueOf(direction),\n\t\t\t\torderBy);\n\t\treturn repo.findAll(pageRequest);\n\t}\n\n\t// método auxiliar que instancia uma cliente através de um DTO\n\tpublic Cliente fromDTO(ClienteDTO objDto) {\n\t\treturn new Cliente(objDto.getId(), objDto.getNome(), objDto.getEmail(), null, null);\n\t}\n\n\tprivate void updateData(Cliente newObj, Cliente obj) {\n\t\tnewObj.setNome(obj.getNome());\n\t\tnewObj.setEmail(obj.getEmail());\n\t}\n~~~\n\n#### Corrigindo erro Null no domain cliente e pagamento\n\n~~~JAVA\nthis.tipo = (tipo==null) ? null:tipo.getCod();\n~~~\n\n~~~JAVA\nthis.estado = (estado==null) ? null:estado.getCod();\n~~~\n\n#### Inserindo um novo cliente com POST\n\n##### Criando um DTO especifico para enviar todos os dados\nPara um cliente ter acesso o banco de dales ele precisa de um endereço e um telefone\nObs:Nela também acompanha  Getters e Setters, mas não foi colocado na parte da documentação para não atrapalhar a visualização.\n\n(ClienteNewDTO)\n~~~JAVA\npublic class ClienteNewDTO implements Serializable {\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate String nome;\n\tprivate String email;\n\tprivate String cpfOuCnpj;\n\tprivate Integer tipo;\n\n\tprivate String logradouro;\n\tprivate String numero;\n\tprivate String complemento;\n\tprivate String bairro;\n\tprivate String cep;\n\n\tprivate String telefone1;\n\tprivate String telefone2;\n\tprivate String telefone3;\n\n\tprivate Integer cidadeId;\n\n\tpublic ClienteNewDTO() {\n\t}\n}\n~~~\n\n##### Criando o Endpoint de POST no ClienteResource/ClienteService\n\n(ClienteService)\n~~~JAVA\n@Autowired\n\tprivate EnderecoRepository enderecoRepository;\n\n@Transactional\n\tpublic Cliente insert(Cliente obj) {\n\t\tobj.setId(null);\n\t\tobj = repo.save(obj);\n\t\tenderecoRepository.saveAll(obj.getEnderecos());\n\t\treturn obj;\n}\n\npublic Cliente fromDTO(ClienteNewDTO objDto) {\n\t\tCliente cli = new Cliente(null, objDto.getNome(), objDto.getEmail(), objDto.getCpfOuCnpj(), TipoCliente.toEnum(objDto.getTipo()));\n\t\tCidade cid = new Cidade(objDto.getCidadeId(), null, null);\n\t\tEndereco end = new Endereco(null, objDto.getLogradouro(), objDto.getNumero(), objDto.getComplemento(), objDto.getBairro(), objDto.getCep(), cli, cid);\n\t\tcli.getEnderecos().add(end);\n\t\tcli.getTelefones().add(objDto.getTelefone1());\n\t\tif (objDto.getTelefone2()!=null) {\n\t\t\tcli.getTelefones().add(objDto.getTelefone2());\n\t\t}\n\t\tif (objDto.getTelefone3()!=null) {\n\t\t\tcli.getTelefones().add(objDto.getTelefone3());\n\t\t}\n\t\treturn cli;\n\t}\n~~~\n\n(ClienteResource)\n~~~JAVA\n@RequestMapping(method=RequestMethod.POST)\n\tpublic ResponseEntity\u003cVoid\u003e insert(@Valid @RequestBody ClienteNewDTO objDto) {\n\t\tCliente obj = service.fromDTO(objDto);\n\t\tobj = service.insert(obj);\n\t\tURI uri = ServletUriComponentsBuilder.fromCurrentRequest()\n\t\t\t\t.path(\"/{id}\").buildAndExpand(obj.getId()).toUri();\n\t\treturn ResponseEntity.created(uri).build();\n\t}\n\n~~~\n\n#### Validação customizada: CPF ou CNPJ na inserção de Cliente\nFazendo a validação com as Anotações\n\n(ClienteNewDTO)\n~~~JAVA\n@ClienteInsert\npublic class ClienteNewDTO implements Serializable {\n\tprivate static final long serialVersionUID = 1L;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\t@Length(min=5, max=120, message=\"O tamanho deve ser entre 5 e 120 caracteres\")\n\tprivate String nome;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\t@Email(message=\"Email inválido\")\n\tprivate String email;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\tprivate String cpfOuCnpj;\n\n\tprivate Integer tipo;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\tprivate String logradouro;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\tprivate String numero;\n\n\tprivate String complemento;\n\n\tprivate String bairro;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\tprivate String cep;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\tprivate String telefone1;\n}\n~~~\n\n##### Criando a anotação ClienteInsert\n\n(ClienteInsert)\n~~~JAVA\n@Constraint(validatedBy = ClienteInsertValidator.class)\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface ClienteInsert {\n\tString message() default \"Erro de validação\";\n\n\tClass\u003c?\u003e[] groups() default {};\n\n\tClass\u003c? extends Payload\u003e[] payload() default {};\n}\n~~~\n\n##### Criando o ClienteInsertValidator\n\n(ClienteInsertValidatornsert)\n~~~JAVA\npublic class ClienteInsertValidator implements ConstraintValidator\u003cClienteInsert, ClienteNewDTO\u003e {\n\t@Override\n\tpublic void initialize(ClienteInsert ann) {\n\t}\n\n\t@Override\n\tpublic boolean isValid(ClienteNewDTO objDto, ConstraintValidatorContext context) {\n\n\t\tList\u003cFieldMessage\u003e list = new ArrayList\u003c\u003e();\n\n\t\tif(objDto.getTipo().equals(TipoCliente.PESSOAFISICA.getCod()) \u0026\u0026 !BR.isValidCPF(objDto.getCpfOuCnpj())) {\n\t\t\tlist.add(new FieldMessage(\"cpfOuCnpj\", \"CPF inválido\"));\n\t\t}\n\n\t\tif(objDto.getTipo().equals(TipoCliente.PESSOAJURIDICA.getCod()) \u0026\u0026 !BR.isValidCNPJ(objDto.getCpfOuCnpj())) {\n\t\t\tlist.add(new FieldMessage(\"cpfOuCnpj\", \"CNPJ inválido\"));\n\t\t}\n\n\t\tfor (FieldMessage e : list) {\n\t\t\tcontext.disableDefaultConstraintViolation();\n\t\t\tcontext.buildConstraintViolationWithTemplate(e.getMessage()).addPropertyNode(e.getFieldName())\n\t\t\t\t\t.addConstraintViolation();\n\t\t}\n\t\treturn list.isEmpty();\n\t}\n}\n~~~\n\n##### Validação de CPF e CNPJ\n\n~~~JAVA\npublic class BR {\n\tprivate static final int[] weightSsn = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2};\n\n    // CNPJ\n    private static final int[] weightTin = {6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};\n\n    private static int calculate(final String str, final int[] weight) {\n        int sum = 0;\n        for (int i = str.length() - 1, digit; i \u003e= 0; i--) {\n            digit = Integer.parseInt(str.substring(i, i + 1));\n            sum += digit * weight[weight.length - str.length() + i];\n        }\n        sum = 11 - sum % 11;\n        return sum \u003e 9 ? 0 : sum;\n    }\n\n    /**\n     * Valida CPF\n     *\n     * @param ssn\n     * @return\n     */\n    public static boolean isValidCPF(final String ssn) {\n        if ((ssn == null) || (ssn.length() != 11) || ssn.matches(ssn.charAt(0) + \"{11}\")) return false;\n\n        final Integer digit1 = calculate(ssn.substring(0, 9), weightSsn);\n        final Integer digit2 = calculate(ssn.substring(0, 9) + digit1, weightSsn);\n        return ssn.equals(ssn.substring(0, 9) + digit1.toString() + digit2.toString());\n    }\n\n    /**\n     * Valida CNPJ\n     *\n     * @param tin\n     * @return\n     */\n    public static boolean isValidCNPJ(final String tin) {\n        if ((tin == null) || (tin.length() != 14) || tin.matches(tin.charAt(0) + \"{14}\")) return false;\n\n        final Integer digit1 = calculate(tin.substring(0, 12), weightTin);\n        final Integer digit2 = calculate(tin.substring(0, 12) + digit1, weightTin);\n        return tin.equals(tin.substring(0, 12) + digit1.toString() + digit2.toString());\n    }\n\n}\n~~~\n\n#### Validação customizada:  Email não repetido na inserção Cliente\n\n(Cliente)\n~~~JAVA\n@Column(unique=true)\n\tprivate String email;\n~~~\n\n(ClienteRepository)\n~~~JAVA\n@Transactional(readOnly=true)\n\tCliente findByEmail(String email);\n~~~\n\n(ClienteInsertValidator)\n~~~JAVA\n@Autowired\n\tprivate ClienteRepository repo;\n\n\tCliente aux = repo.findByEmail(objDto.getEmail());\n\t\tif (aux != null) {\n\t\t\tlist.add(new FieldMessage(\"email\", \"Email já existe\"));\n\t\t}\n~~~\n\n#### Busca de produtos por nome e categorias\n\n(ProdutoDTO)\n~~~JAVA\npublic class ProdutoDTO implements Serializable {\n\tprivate static final long serialVersionUID = 1L;\n\tprivate Integer id;\n\n\tprivate String nome;\n\tprivate Double preco;\n\n\tpublic ProdutoDTO() {\n\n\t}\n}\n~~~\n\n(ProdutoRepository)\n~~~JAVA\n@Repository\npublic interface ProdutoRepository extends JpaRepository\u003cProduto, Integer\u003e {\n\n\n\tPage\u003cProduto\u003e findDistinctByNomeContainingAndCategoriasIn( String nome, List\u003cCategoria\u003e categorias, Pageable pageRequest);\n}\n~~~\n\n(utils)\n~~~JAVA\npublic class URL {\n\n\tpublic static String decodeParam(String s) {\n\t\ttry {\n\t\t\treturn URLDecoder.decode(s, \"UTF-8\");\n\t\t} catch (UnsupportedEncodingException e) {\n\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tpublic static List\u003cInteger\u003e decodeIntList(String s) {\n\t\tString[] vet = s.split(\",\");\n\t\tList\u003cInteger\u003e list = new ArrayList\u003c\u003e();\n\t\tfor (int i = 0; i \u003c vet.length; i++) {\n\t\t\tlist.add(Integer.parseInt(vet[i]));\n\t\t}\n\t\treturn list;\n\t}\n}\n~~~\n\n(ProdutoResource)\n~~~JAVA\n@RestController\n@RequestMapping(value = \"/produtos\")\npublic class ProdutoResource {\n\n\t@Autowired\n\tprivate ProdutoService service;\n\n\t@RequestMapping(value = \"/{id}\", method = RequestMethod.GET)\n\tpublic ResponseEntity\u003cProduto\u003e find(@PathVariable Integer id) {\n\t\tProduto obj = service.find(id);\n\t\treturn ResponseEntity.ok().body(obj);\n\n\t}\n\n\t@RequestMapping(method=RequestMethod.GET)\n\tpublic ResponseEntity\u003cPage\u003cProdutoDTO\u003e\u003e findPage(\n\t\t\t@RequestParam(value=\"nome\", defaultValue=\"\") String nome,\n\t\t\t@RequestParam(value=\"categorias\", defaultValue=\"\") String categorias,\n\t\t\t@RequestParam(value=\"page\", defaultValue=\"0\") Integer page,\n\t\t\t@RequestParam(value=\"linesPerPage\", defaultValue=\"24\") Integer linesPerPage,\n\t\t\t@RequestParam(value=\"orderBy\", defaultValue=\"nome\") String orderBy,\n\t\t\t@RequestParam(value=\"direction\", defaultValue=\"ASC\") String direction) {\n\t\tString nomeDecoded = URL.decodeParam(nome);\n\t\tList\u003cInteger\u003e ids = URL.decodeIntList(categorias);\n\t\tPage\u003cProduto\u003e list = service.search(nomeDecoded, ids, page, linesPerPage, orderBy, direction);\n\t\tPage\u003cProdutoDTO\u003e listDto = list.map(obj -\u003e new ProdutoDTO(obj));  \n\t\treturn ResponseEntity.ok().body(listDto);\n\t}\n\n}\n~~~\n\n(ProdutoService)\n~~~JAVA\n@Service\npublic class ProdutoService {\n\n\t@Autowired\n\tprivate CategoriaRepository categoriaRepository;\n\n\t@Autowired\n\tprivate ProdutoRepository repo;\n\n\tpublic Produto find(Integer id) {\n\t\t Optional\u003cProduto\u003e obj = repo.findById(id);\n\t\treturn obj.orElseThrow(() -\u003e new ObjectNotFoundException(\n\t\t \"Objeto não encontrado! Id: \" + id + \", Tipo: \" + Produto.class.getName()));\n\t\t}\n\n\tpublic Page\u003cProduto\u003e search(String nome, List\u003cInteger\u003e ids,Integer page, Integer linesPerPage, String orderBy, String direction) {\n\t\tPageRequest pageRequest = PageRequest.of(page, linesPerPage, Direction.valueOf(direction), orderBy);\n\t\tList\u003cCategoria\u003e categorias = categoriaRepository.findAllById(ids);\n\t\treturn repo.findDistinctByNomeContainingAndCategoriasIn(nome, categorias, pageRequest);\n\n\t}\n}\n~~~\n\n## Serviço de email\n\n#### Objetivo geral:\n __Criar um serviço de email__\n- Criar uma operação de envio de confirmação de pedido\n\n __Implementar o serviço em modo de desenvolvimento e produção__\n- Criar o MockEmailService com Logger\n- Criar o SmtpEmailService com SMTP do Google\n__Demonstrar uma implementação flexível e elegante com padrões de projeto (Strategy e Template\nMethod)__\n\n### Craindo um serviço de email\n(TestConfig)\n~~~JAVA\n@Bean\n\tpublic EmailService emailService() {\n\t\treturn new MockEmailService();\n\t}\n~~~\n(AbstractEmailService)\n~~~JAVA\npublic abstract class  AbstractEmailService implements EmailService {\n\n\n\t@Value(\"${default.sender}\")\n\tprivate String sender;\n\n\t@Override\n\tpublic void sendOrderConfirmationEmail(Pedido obj) {\n\t\tSimpleMailMessage sm = prepareSimpleMailMessageFromPedido(obj);\n\t\tsendEmail(sm);\n\t}\n\n\tprotected SimpleMailMessage prepareSimpleMailMessageFromPedido(Pedido obj) {\n\t\tSimpleMailMessage sm = new SimpleMailMessage();\n\t\tsm.setTo(obj.getCliente().getEmail());\n\t\tsm.setFrom(sender);\n\t\tsm.setSubject(\"Pedido confirmado! Código: \" + obj.getId());\n\t\tsm.setSentDate(new Date(System.currentTimeMillis()));\n\t\tsm.setText(obj.toString());\n\t\treturn sm;\n\t}\n}\n~~~\n(EmailService)\n~~~JAVA\npublic interface EmailService {\n\n\tvoid sendOrderConfirmationEmail(Pedido obj);\n\n\tvoid sendEmail(SimpleMailMessage msg);\n}\n~~~\n(MockEmailService)\n~~~JAVA\npublic class MockEmailService extends AbstractEmailService {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MockEmailService.class);\n\n\t@Override\n\tpublic void sendEmail(SimpleMailMessage msg) {\n\t\tLOG.info(\"Simulando envio de email...\");\n\t\tLOG.info(msg.toString());\n\t\tLOG.info(\"Email enviado\");\n\t}\n\n}\n~~~\n\n(PedidoService) // alteração do pedidoService no insert\n~~~JAVA\n@Autowired\n\tprivate EmailService emailService;\n\n\t@Transactional\n\tpublic Pedido insert(Pedido obj) {\n\t\tobj.setId(null);\n\t\tobj.setInstante(new Date());\n\t\tobj.setCliente(clienteService.find(obj.getCliente().getId()));\n\t\tobj.getPagamento().setEstado(EstadoPagamento.PENDENTE);\n\t\tobj.getPagamento().setPedido(obj);\n\t\tif (obj.getPagamento() instanceof PagamentoComBoleto) {\n\t\t\tPagamentoComBoleto pagto = (PagamentoComBoleto) obj.getPagamento();\n\t\t\tboletoService.preencherPagamentoComBoleto(pagto, obj.getInstante());\n\t\t}\n\t\tobj = repo.save(obj);\n\t\tpagamentoRepository.save(obj.getPagamento());\n\t\tfor (ItemPedido ip : obj.getItens()) {\n\t\t\tip.setDesconto(0.0);\n\t\t\tip.setProduto(produtoService.find(ip.getProduto().getId()));\n\t\t\tip.setPreco(ip.getProduto().getPreco());\n\t\t\tip.setPedido(obj);\n\t\t}\n\t\titemPedidoRepository.saveAll(obj.getItens());\n\t\t// alteração do pedidoService\n\t\temailService.sendOrderConfirmationEmail(obj);\n\t\treturn obj;\n\t}\n}\n~~~\n\n### Implementando SmtpEmailService com servidor google\n\n(DevConfig)\n~~~JAVA\n@Bean\n\tpublic EmailService emailService() {\n\t\treturn new SmtpEmailService();\n\t}\n~~~\n(SmtpEmailService)\n~~~JAVA\npublic class SmtpEmailService extends AbstractEmailService {\n\n\t@Autowired\n\tprivate MailSender mailSender;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SmtpEmailService.class);\n\n\t@Override\n\tpublic void sendEmail(SimpleMailMessage msg) {\n\t\tLOG.info(\"Simulando envio de email...\");\n\t\tmailSender.send(msg);\n\t\tLOG.info(\"Email enviado\");\n\n\t}\n\n}\n~~~\n\n### envio de email com html\n\n(AbstractEmailService)\n~~~JAVA\n@Autowired\n\tprivate JavaMailSender javaMailSender;\n\n\n\t@Autowired\n\tprivate TemplateEngine templateEngine;\n\n\tprotected String htmlFromTemplatePedido(Pedido obj) {\n\t\tContext context = new Context();\n\t\tcontext.setVariable(\"pedido\", obj);\n\t\treturn templateEngine.process(\"email/confirmacaoPedido\", context);\n\t}\n\n\t@Override\n\tpublic void sendOrderConfirmationHtmlEmail(Pedido obj) {\n\t\ttry {\n\t\t\tMimeMessage mm = prepareMimeMessageFromPedido(obj);\n\t\t\tsendHtmlEmail(mm);\n\t\t}\n\t\tcatch(MessagingException e) {\n\t\t\tsendOrderConfirmationEmail(obj);\n\t\t}\n\n\t}\n\n\tprotected MimeMessage prepareMimeMessageFromPedido(Pedido obj) throws MessagingException {\n\t\tMimeMessage mimeMessage = javaMailSender.createMimeMessage();\n\t\tMimeMessageHelper mmh = new MimeMessageHelper(mimeMessage, true);\n\t\tmmh.setTo(obj.getCliente().getEmail());\n\t\tmmh.setFrom(sender);\n\t\tmmh.setSubject(\"Pedido confirmado! Código: \" + obj.getId());\n\t\tmmh.setSentDate(new Date(System.currentTimeMillis()));\n\t\tmmh.setText(htmlFromTemplatePedido(obj), true);\n\n\t\treturn mimeMessage;\n\t}\n~~~\n(EmailService)\n~~~JAVA\n\tvoid sendOrderConfirmationHtmlEmail(Pedido obj);\n\n\tvoid sendHtmlEmail(MimeMessage msg);\n~~~\n(MockEmailService)\n~~~JAVA\n@Override\n\tpublic void sendHtmlEmail(MimeMessage msg) {\n\t\tLOG.info(\"Simulando envio de email HTML...\");\n\t\tLOG.info(msg.toString());\n\t\tLOG.info(\"Email enviado\");\n\n\t}\n~~~\n(SmtpEmailService)\n~~~JAVA\n@Autowired\n\tprivate JavaMailSender javaMailSender;\n\n\t@Override\n\tpublic void sendHtmlEmail(MimeMessage msg) {\n\t\tLOG.info(\"Simulando envio de email...\");\n\t\tjavaMailSender.send(msg);\n\t\tLOG.info(\"Email enviado\");\n\n\t}\n~~~\n(confirmacaoPedido)\n~~~JAVA\n\u003c!DOCTYPE html\u003e\n\u003chtml xmlns:th=\"http://www.thymeleaf.org\"\u003e\n\n\u003chead\u003e\n\t\u003ctitle th:remove=\"all\"\u003eOrder Confirmation\u003c/title\u003e\n\t\u003cmeta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /\u003e\n\u003c/head\u003e\n\n\u003cbody\u003e\n\t\u003cdiv\u003e\n\t\t\u003ch1\u003e\n\t\t\tPedido número: \u003cspan th:text=\"${pedido.id}\"\u003e \u003c/span\u003e\n\t\t\u003c/h1\u003e\n\t\t\u003cp\u003e\n\t\t\tInstante: \u003cspan th:text=\"${#dates.format(pedido.instante, 'dd/MM/yyyy hh:mm')}\"\u003e\u003c/span\u003e\n\t\t\u003c/p\u003e\n\t\t\u003cp\u003e\n\t\t\tCliente: \u003cspan th:text=\"${pedido.cliente.nome}\"\u003e\u003c/span\u003e\n\t\t\u003c/p\u003e\n\t\t\u003cp\u003e\n\t\t\tSituação do pagamento: \u003cspan th:text=\"${pedido.pagamento.estado.descricao}\"\u003e\u003c/span\u003e\n\t\t\u003c/p\u003e\n\t\t\u003ch3\u003eDetalhes do pedido:\u003c/h3\u003e\n\t\t\u003ctable border=\"1\"\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\u003cth\u003eProduto\u003c/th\u003e\n\t\t\t\t\u003cth\u003eQuantidade\u003c/th\u003e\n\t\t\t\t\u003cth\u003ePreço unitário\u003c/th\u003e\n\t\t\t\t\u003cth\u003eSubtotal\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr th:each=\"item : ${pedido.itens}\"\u003e\n\t\t\t\t\u003ctd th:text=\"${item.produto.nome}\"\u003enome\u003c/td\u003e\n\t\t\t\t\u003ctd th:text=\"${item.quantidade}\"\u003equantidade\u003c/td\u003e\n\t\t\t\t\u003ctd th:text=\"${#numbers.formatDecimal(item.preco, 0, 'POINT', 2,\n'COMMA')}\"\u003epreco\u003c/td\u003e\n\t\t\t\t\u003ctd th:text=\"${#numbers.formatDecimal(item.subTotal, 0, 'POINT', 2,\n'COMMA')}\"\u003esubTotal\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\u003c/table\u003e\n\t\t\u003cp\u003e\n\t\t\tValor total: \u003cspan th:text=\"${#numbers.formatDecimal(pedido.valorTotal, 0,\n'POINT', 2, 'COMMA')}\"\u003e\u003c/span\u003e\n\t\t\u003c/p\u003e\n\t\u003c/div\u003e\n\u003c/body\u003e\n\n\u003c/html\u003e\n~~~\n\n![emailHtml](https://user-images.githubusercontent.com/101072311/202929430-0733b7b1-42fe-4dbe-8f98-cf58b4c57246.png)\n\n## Autenticação e Autorização com tokens JWT\n\n#### Objetivo geral:\n\n- Compreender o mecanismo de funcionamento do Spring Security\n- Implementar autenticação e autorização com JWT\n- Controlar conteúdo e acesso aos endpoints\n\n### Implementando autenticacao e geracao do token JWT\n\n(SecurityConfig)\n~~~JAVA\n@Autowired\n\tprivate UserDetailsService userDetailsService;\n\n\t@Autowired\n\tprivate JWTUtil jwtUtil;\n\n\t@Override\n\tpublic void configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\tauth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());\n\t}\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\tif (Arrays.asList(env.getActiveProfiles()).contains(\"test\")) {\n\t\t\thttp.headers().frameOptions().disable();\n\t\t}\n\n\t\thttp.cors().and().csrf().disable();\n\t\thttp.authorizeHttpRequests()\n\t\t.antMatchers(HttpMethod.POST, PUBLIC_MATCHERS_POST).permitAll()\n\t\t.antMatchers(HttpMethod.GET, PUBLIC_MATCHERS_GET).permitAll()\n\t\t.antMatchers(PUBLIC_MATCHERS).permitAll().anyRequest().authenticated();\n\t\thttp.addFilter(new JWTAuthenticationFilter(authenticationManager(), jwtUtil));\n\t\t/*http.addFilter(new JWTAuthorizationFilter(authenticationManager(), jwtUtil, userDetailsService));*/\n\thttp.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);\n\t}\n~~~\n(CredenciaisDTO)\n~~~JAVA\npublic class CredenciaisDTO  implements Serializable {\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate String email;\n\tprivate String senha;\n\n\tpublic CredenciaisDTO() {\n\n\t}\n\n\tpublic String getEmail() {\n\t\treturn email;\n\t}\n\tpublic void setEmail(String email) {\n\t\tthis.email = email;\n\t}\n\tpublic String getSenha() {\n\t\treturn senha;\n\t}\n\tpublic void setSenha(String senha) {\n\t\tthis.senha = senha;\n\t}\n\n}\n~~~\n(JWTAuthenticationFilter)\n~~~JAVA\npublic class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate JWTUtil jwtUtil;\n\n\tpublic JWTAuthenticationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) {\n\t\tsetAuthenticationFailureHandler(new JWTAuthenticationFailureHandler());\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.jwtUtil = jwtUtil;\n\t}\n\n\t@Override\n\tpublic Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)\n\t\t\tthrows AuthenticationException {\n\n\t\ttry {\n\t\t\tCredenciaisDTO creds = new ObjectMapper().readValue(req.getInputStream(), CredenciaisDTO.class);\n\n\t\t\tUsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(creds.getEmail(),\n\t\t\t\t\tcreds.getSenha(), new ArrayList\u003c\u003e());\n\n\t\t\tAuthentication auth = authenticationManager.authenticate(authToken);\n\t\t\treturn auth;\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t}\n\n\t@Override\n\tprotected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,\n\t\t\tAuthentication auth) throws IOException, ServletException {\n\t\tString username = ((UserSS) auth.getPrincipal()).getUsername();\n\t\tString token = jwtUtil.generateToken(username);\n\t\tres.addHeader(\"Authorization\", \"Bearer \" + token);\n\n\t}\n\n\tprivate class JWTAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n\t\t@Override\n\t\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAuthenticationException exception) throws IOException, ServletException {\n\t\t\tresponse.setStatus(401);\n\t\t\tresponse.setContentType(\"application/json\");\n\t\t\tresponse.getWriter().append(json());\n\t\t}\n\n\t\tprivate String json() {\n\t\t\tlong date = new Date().getTime();\n\t\t\treturn \"{\\\"timestamp\\\": \" + date + \", \" + \"\\\"status\\\": 401, \" + \"\\\"error\\\": \\\"Não autorizado\\\", \"\n\t\t\t\t\t+ \"\\\"message\\\": \\\"Email ou senha inválidos\\\", \" + \"\\\"path\\\": \\\"/login\\\"}\";\n\t\t}\n\t}\n}\n~~~\n(JTWUtil)\n~~~JAVA\n@Component\npublic class JWTUtil {\n\n\t@Value(\"${jwt.secret}\")\n\tprivate String secret;\n\n\t@Value(\"${jwt.expiration}\")\n\tprivate Long expiration;\n\n\tpublic String generateToken(String username) {\n\t\treturn Jwts.builder()\n\t\t\t\t.setSubject(username)\n\t\t\t\t.setExpiration(new Date(System.currentTimeMillis() + expiration))\n\t\t\t\t.signWith(SignatureAlgorithm.HS512, secret.getBytes())\n\t\t\t\t.compact();\n\t}\n}\n~~~\n(UserSS)\n~~~JAVA\npublic class UserSS implements UserDetails{\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate Integer id;\n\tprivate String email;\n\tprivate String senha;\n\tprivate Collection\u003c? extends GrantedAuthority\u003e authorities;\n\n\tpublic UserSS() {\n\n\t}\n\n\tpublic UserSS(Integer id, String email, String senha, Set\u003cPerfil\u003e perfis) {\n\t\tsuper();\n\t\tthis.id = id;\n\t\tthis.email = email;\n\t\tthis.senha = senha;\n\t\tthis.authorities = perfis.stream().map(x -\u003e new SimpleGrantedAuthority(x.getDescricao())).collect(Collectors.toList());\n\t}\n\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\n\t@Override\n\tpublic Collection\u003c? extends GrantedAuthority\u003e getAuthorities() {\n\n\t\treturn  authorities;\n\t}\n\n\t@Override\n\tpublic String getPassword() {\n\t\t// TODO Auto-generated method stub\n\t\treturn senha;\n\t}\n\n\t@Override\n\tpublic String getUsername() {\n\t\t// TODO Auto-generated method stub\n\t\treturn email;\n\t}\n\n\t@Override\n\tpublic boolean isAccountNonExpired() {\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isAccountNonLocked() {\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isCredentialsNonExpired() {\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isEnabled() {\n\n\t\treturn true;\n\t}\n\n}\n~~~\n\n### Implementando autorização\n\n(SecurityConfig)\n~~~JAVA\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\tif (Arrays.asList(env.getActiveProfiles()).contains(\"test\")) {\n\t\t\thttp.headers().frameOptions().disable();\n\t\t}\n\n\t\thttp.cors().and().csrf().disable();\n\t\thttp.authorizeHttpRequests()\n\t\t.antMatchers(HttpMethod.POST, PUBLIC_MATCHERS_POST).permitAll()\n\t\t.antMatchers(HttpMethod.GET, PUBLIC_MATCHERS_GET).permitAll()\n\t\t.antMatchers(PUBLIC_MATCHERS).permitAll().anyRequest().authenticated();\n\t\thttp.addFilter(new JWTAuthenticationFilter(authenticationManager(), jwtUtil));\n\t\thttp.addFilter(new JWTAuthorizationFilter(authenticationManager(), jwtUtil, userDetailsService));\n\thttp.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);\n\t}\n~~~\n(JWTAuthorizationFilter)\n~~~JAVA\npublic class JWTAuthorizationFilter extends BasicAuthenticationFilter {\n\n\tprivate JWTUtil jwtUtil;\n\n\tprivate UserDetailsService userDetailsService;\n\n\tpublic JWTAuthorizationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil,\n\t\t\tUserDetailsService userDetailsService) {\n\t\tsuper(authenticationManager);\n\t\tthis.jwtUtil = jwtUtil;\n\t\tthis.userDetailsService = userDetailsService;\n\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\n\tString header = request.getHeader(\"Authorization\");\n\n\t\tif(header != null \u0026\u0026 header.startsWith(\"Bearer \")) {\n\t\t\tUsernamePasswordAuthenticationToken auth = getAuthentication(header.substring(7));\n\t\t\tif(auth != null) {\n\t\t\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\t\t}\n\t\t}\n\t\tchain.doFilter(request, response);\n\n\t}\n\n\tprivate UsernamePasswordAuthenticationToken getAuthentication(String token) {\n\t\tif(jwtUtil.tokenValido(token)) {\n\t\t\tString username = jwtUtil.getUsername(token);\n\t\t\tUserDetails user = userDetailsService.loadUserByUsername(username);\n\t\t\treturn new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());\n\t\t}\n\t\treturn null;\n\t}\n}\n~~~\n(JWTUtil)\n~~~JAVA\npublic String generateToken(String username) {\t\t\n\t\treturn Jwts.builder().setSubject(username).setExpiration(new Date(System.currentTimeMillis() + expiration))\n\t\t\t\t.signWith(SignatureAlgorithm.HS512, secret.getBytes()).compact();\n\t}\n\n\tpublic boolean tokenValido(String token) {\n\t\tClaims claims = getClaims(token);\n\t\tif(claims != null) {\n\t\t\tString username = claims.getSubject();\n\t\t\tDate expirationDate = claims.getExpiration();\n\t\t\tDate now = new Date(System.currentTimeMillis());\n\t\t\tif(username != null \u0026\u0026 expirationDate != null \u0026\u0026 now.before(expirationDate)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t}\n\t\treturn false;\n\t}\n\n\n\tpublic String getUsername(String token) {\n\t\tClaims claims = getClaims(token);\n\t\tif(claims != null) {\n\t\t\treturn claims.getSubject();\n\t\t}\n\t\treturn null;\n\t}\n\n\n\tprivate Claims getClaims(String token) {\n\t\ttry {\n\t\treturn Jwts.parser().setSigningKey(secret.getBytes()).parseClaimsJws(token).getBody();\n\t\t}  \n\t\tcatch(Exception e) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n~~~\n\n### Autorizando endpoints para perfis específicos\n\n(SecurityConfig)\n~~~JAVA\n\tprivate static final String[] PUBLIC_MATCHERS_GET = { \"/produtos/**\", \"/categorias/**\"};\n\n\tprivate static final String[] PUBLIC_MATCHERS_POST = { \"/clientes/**\"};\n~~~\n(CategoriaResource) Anotação nos métodos POST/DELETE/PUT\n~~~JAVA\n@PreAuthorize(\"hasAnyRole('ADMIN')\")\n~~~\n(ResourceExceptionHandler)\n~~~JAVA\n@ExceptionHandler(AuthorizationException.class)\n\tpublic ResponseEntity\u003cStandardError\u003e authorization(AuthorizationException e, HttpServletRequest request) {\n\n\t\tStandardError err = new StandardError(HttpStatus.FORBIDDEN.value(), e.getMessage(), System.currentTimeMillis());\n\t\treturn ResponseEntity.status(HttpStatus.FORBIDDEN).body(err);\n\t}\n~~~\n(UserSS)\n~~~JAVA\npublic boolean hasRole(Perfil perfil) {\n\t\treturn getAuthorities().contains(new SimpleGrantedAuthority(perfil.getDescricao()));\n\t}\n~~~\n(clienteService)\n~~~JAVA\npublic Cliente find(Integer id) {\n\t\t Optional\u003cCliente\u003e obj = repo.findById(id);\n\t\tUserSS user = UserService.authenticated();\n\t\tif(user == null || !user.hasRole(Perfil.ADMIN) \u0026\u0026 !id.equals(user.getId())) {\n\t\t\tthrow new AuthorizationException(\"Acesso negado\");\n\t\t}\n\n\t\tOptional\u003cCliente\u003e obj = repo.findById(id);\n\t\treturn obj.orElseThrow(() -\u003e new ObjectNotFoundException(\n\t\t \"Objeto não encontrado! Id: \" + id + \", Tipo: \" + Cliente.class.getName()));\n\t\t}\n~~~\n(UserService)\n~~~JAVA\npublic class UserService {\n\tpublic static UserSS authenticated() {\n\t\ttry {\n\t\t\treturn (UserSS) SecurityContextHolder.getContext().getAuthentication().getPrincipal();\n\t\t} catch (Exception e) {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n~~~\n(AuthorizationException)\n~~~JAVA\npublic class AuthorizationException  extends RuntimeException{\n\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic AuthorizationException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic AuthorizationException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n~~~\n(PedidoRepository)\n~~~JAVA\n@Transactional(readOnly=true)\n\tPage\u003cPedido\u003e findByCliente(Cliente cliente, Pageable pageRequest);\n~~~\n(PedidoResource)\n~~~JAVA\n@RequestMapping(method = RequestMethod.GET)\n\tpublic ResponseEntity\u003cPage\u003cPedido\u003e\u003e findPage(\n\t\t\t@RequestParam(value = \"page\", defaultValue = \"0\") Integer page,\n\t\t\t@RequestParam(value = \"linesPerPage\", defaultValue = \"24\") Integer linesPerPage,\n\t\t\t@RequestParam(value = \"orderBy\", defaultValue = \"instante\") String orderBy,\n\t\t\t@RequestParam(value = \"direction\", defaultValue = \"DESC\") String direction) {\n\n\t\tPage\u003cPedido\u003e list = service.findPage(page, linesPerPage, orderBy, direction);\n\n\t\treturn ResponseEntity.ok().body(list);\n\n\t}\n~~~\n(PedidoService)\n~~~JAVA\npublic Page\u003cPedido\u003e findPage(Integer page, Integer linesPerPage, String orderBy, String direction) {\n\t\tUserSS user = UserService.authenticated();\n\t\tif(user == null) {\n\t\t\tthrow new AuthorizationException(\"Acesso negado\");\n\t\t}\n\t\tPageRequest pageRequest = PageRequest.of(page, linesPerPage, Direction.valueOf(direction),orderBy);\n\t\tCliente cliente =  clienteService.find(user.getId());\n\t\treturn repo.findByCliente(cliente, pageRequest);\n\t}\n~~~\n\n### Refresh token\n\n(AuthResource)\n~~~JAVA\n@RestController\n@RequestMapping(value= \"/auth\")\npublic class AuthResource {\n\n\t@Autowired\n\tprivate JWTUtil jwtUtil;\n\n\t@RequestMapping(value=\"/refresh_token\", method= RequestMethod.POST)\n\tpublic ResponseEntity\u003cVoid\u003e refreshToken(HttpServletResponse response) {\n\tUserSS user = UserService.authenticated();\n\tString token = jwtUtil.generateToken(user.getUsername());\n\tresponse.addHeader(\"Authorization\", \"Bearer \" + token);\n\treturn ResponseEntity.noContent().build();\n\t}\n\n}\n~~~\n(SecurityConfig)\n~~~JAVA\nprivate static final String[] PUBLIC_MATCHERS_POST = { \"/clientes/**\", \"/auth/forgot**\"};\n~~~\n(EmailDTO)\n~~~JAVA\npublic class EmailDTO implements Serializable {\n\tprivate static final long serialVersionUID = 1L;\n\n\t@NotEmpty(message=\"Preenchimento obrigatório\")\n\t@Email(message=\"Email inválido\")\n\tprivate String Email;\n\n\tpublic EmailDTO() {\n\n\t}\n\n\tpublic String getEmail() {\n\t\treturn Email;\n\t}\n\n\tpublic void setEmail(String email) {\n\t\tEmail = email;\n\t}\n\n\n}\n~~~\n(AuthResource)\n~~~JAVA\n@Autowired\n\tprivate AuthService service;\n\n\t@RequestMapping(value=\"/forgot\", method= RequestMethod.POST)\n\tpublic ResponseEntity\u003cVoid\u003e forgot(@Valid @RequestBody EmailDTO objDto) {\n\t\tservice.sendNewPassword(objDto.getEmail());\n\n\treturn ResponseEntity.noContent().build();\n\t}\n~~~\n(AbstractEmailService)\n~~~JAVA\n@Override\n\tpublic void sendNewPasswordEmail(Cliente cliente, String newPass) {\n\t\tSimpleMailMessage sm = prepareNewPasswordEmail(cliente, newPass);\n\t\tsendEmail(sm);\n\t}\n\n\tprotected SimpleMailMessage prepareNewPasswordEmail(Cliente cliente, String newPass) {\n\t\tSimpleMailMessage sm = new SimpleMailMessage();\n\t\tsm.setTo(cliente.getEmail());\n\t\tsm.setFrom(sender);\n\t\tsm.setSubject(\"Solicitação de nova senha\");\n\t\tsm.setSentDate(new Date(System.currentTimeMillis()));\n\t\tsm.setText(\"Nova senha: \" + newPass);\n\t\treturn sm;\n\n\t}\n~~~\n(AuthService)\n~~~JAVA\n@Service\npublic class AuthService {\n\n\t@Autowired\n\tprivate ClienteRepository clienteRepository;\n\n\t@Autowired\n\tprivate BCryptPasswordEncoder pe;\n\n\t@Autowired\n\tprivate EmailService emailService;\n\n\tprivate Random rand = new Random();\n\n\tpublic void sendNewPassword(String email) {\n\n\t\tCliente cliente = clienteRepository.findByEmail(email);\n\t\tif (cliente == null) {\n\t\t\tthrow new ObjectNotFoundException(\"Email não encontrado\");\n\t\t}\n\n\t\tString newPass = newPassword();\n\t\tcliente.setSenha(pe.encode(newPass));\n\n\t\tclienteRepository.save(cliente);\n\t\temailService.sendNewPasswordEmail(cliente, newPass);\n\t}\n\n\tprivate String newPassword() {\n\t\tchar[] vet = new char[10];\n\t\tfor (int i=0; i\u003c10; i++) {\n\t\t\tvet[i] = randomChar();\n\t\t}\n\t\treturn new String(vet);\n\t}\n\n\tprivate char randomChar() {\n\t\tint opt = rand.nextInt(3);\n\t\tif (opt == 0) { // gera um digito\n\t\t\treturn (char) (rand.nextInt(10) + 48);\n\t\t}\n\t\telse if (opt == 1) { // gera letra maiuscula\n\t\t\treturn (char) (rand.nextInt(26) + 65);\n\t\t}\n\t\telse { // gera letra minuscula\n\t\t\treturn (char) (rand.nextInt(26) + 97);\n\t\t}\n\t}\n}\n~~~\n## Ferramentas e Tecnologias usadas nesse repositório 🧱\n\u003cdiv style=\"display: inline_block\"\u003e\u003cbr\u003e\n\n\n\u003cimg align=\"center\" alt=\"Augusto-Java\" height=\"60\" width=\"60\" src=https://github.com/devicons/devicon/blob/master/icons/java/java-original.svg \u003e\n\u003cimg align=\"center\" alt=\"Augusto-SpringBoot\" height=\"60\" width=\"60\" src=\"https://raw.githubusercontent.com/devicons/devicon/1119b9f84c0290e0f0b38982099a2bd027a48bf1/icons/spring/spring-original-wordmark.svg\"\u003e\n\u003cimg align=\"center\" alt=\"Augusto-H2\" height=\"50\" width=\"50\" src=\"https://user-images.githubusercontent.com/101072311/200666111-2e4878bb-7d5c-4103-a159-fd00d0855a5d.png\"\u003e\n\n\u003c/div\u003e    \n\n### Teste o projeto 👁‍🗨\n\nDownload do projeto para testar em sua máquina: https://github.com/AugustoMello09/cursoSpringBackend/archive/refs/heads/master.zip\n\n## Entre em contato comigo através dos canais abaixo e desde já, agradeço a atenção. 🤝\n\n\u003cdiv\u003e\n\n  \u003ca href=\"https://www.linkedin.com/in/jos%C3%A9-augusto-mello-794a94234\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/badge/-LinkedIn-%230077B5?style=for-the-badge\u0026logo=linkedin\u0026logoColor=white\" target=\"_blank\"\u003e\u003c/a\u003e\n   \u003ca href=\"mailto:joseaugusto.Mello01@gmail.com\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/badge/Gmail-D14836?style=for-the-badge\u0026logo=gmail\u0026logoColor=white\" target=\"_blank\"\u003e\u003c/a\u003e\n\n  \u003c/div\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faugustomello09%2Fcursospringbackend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faugustomello09%2Fcursospringbackend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faugustomello09%2Fcursospringbackend/lists"}