Julio @faermanj
https://faermanj.me
https://caravana.cluoud
https://gist.github.com/faermanj/270a8a8ab817f95fc2e350ec2d481bd2
Aplicação estilo "microserviços" ("PetCare")
- Agilidade + Segurança
- Sim, é compatível com Spring, sigam Jonathan Vila https://twitter.com/vilojona
┌────────┐
┌───┴──────┐ │
┌────────┐ ┌─────────┐ ┌─┴────────┐ │ │
│ User ├────► │ APP ├────► │ API │ ├─┘
│ Browser│ │ Vaadin │ │ Quarkus ├─┘
└────────┘ └────┬────┘ └─┬──┬─────┘
│ │ │
│ │ │ ┌──────────────────┐
│ │ └────────►│ Database MySQL │
│ │ └──────────────────┘
│ │
│ │ ┌──────────────────┐
│ └───────────►│ Auth* │
│ OpenId Connect │ Keycloak │
└─────────────────────────►└──────────────────┘
git clone # URL do repositorio
cd # diretorio do projeto
https://quarkus.io/guides/cli-tooling
quarkus version
quarkus create app petcare:ptc-api:0.0.1 \
--java=17 \
--package-name=petcare.api
Iniciando o módulo API
cd ptc-api
quarkus dev
Testando o módulo API
curl http://localhost:8080/hello
code src/main/java/petcare/api/GreetingResource.java
Quarkus Dev Console http://localhost:8080/q/dev
quarkus ext add resteasy-jackson
Hello JSON
@GET
@Path("/json")
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> getJSON() {
return Map.of("message", "Hello RESTEasy");
}
quarkus ext add agroal
quarkus ext add jdbc-mysql
Datasource Resource
import javax.inject.Inject;
import javax.sql.DataSource;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import java.sql.SQLException;
@Path("/ds")
public class DatasourceResource {
@Inject
DataSource datasource;
@GET
public String getDS() {
try (var con = datasource.getConnection();
var stmt = con.createStatement();
var rs = stmt.executeQuery("SELECT 1+1")) {
rs.next();
return rs.getString(1);
} catch (SQLException e) {
throw new WebApplicationException(e);
}
}
}
curl http://localhost:8080/ds
application-dev.properties Referencia: https://quarkus.io/guides/all-config
# DevServices
quarkus.devservices.enabled=true
quarkus.live-reload.instrumentation=true
# Database
quarkus.datasource.devservices.port=13371
quarkus.datasource.username=admin
quarkus.datasource.password=Masterkey123
quarkus.hibernate-orm.database.generation=drop-and-create
# Logging
quarkus.hibernate-orm.log.sql=true
quarkus.http.access-log.enabled=true
# Utilities
mysql -h127.0.0.1 -P13371 -uadmin -pMasterkey123 default
quarkus ext add hibernate-orm
quarkus ext add hibernate-orm-panache
Pet.java
@Entity
public class Pet extends PanacheEntity {
String name;
String cuteName;
LocalDate birthday;
// Constructor
// Getters
}
JPA Resource
@Path("/jpa")
public class JPAResource {
@Inject
EntityManager em;
@Path("/new")
@GET
@Transactional
public Pet getNew(){
var pet = new Pet("Dexter",
"Maxwell",
LocalDate.of(2020,1,1));
em.persist(pet);
return pet;
}
}
curl http://localhost:8080/jpa/new
Panache Resource
@Path("/panache")
public class PanacheResource {
@Path("new")
@GET
@Transactional
public Pet getNew(){
var pet = new Pet("Bjorn",
"Pikeu",
LocalDate.of(2021, 1, 1));
pet.persist();
return pet;
}
@Path("find")
@GET
@Transactional
public List<Pet> findByName(@QueryParam("name") String name){
return Pet.find("name", name).list();
}
}
curl http://localhost:8080/panache/new
curl http://localhost:8080/panache/find?name=Bjorn
quarkus ext add oidc
quarkus ext add keycloak-authorization
application-dev.properties
# Keycloak
quarkus.keycloak.devservices.enabled=true
quarkus.keycloak.devservices.shared=true
quarkus.keycloak.devservices.port=13370
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.client-secret=secret
quarkus.keycloak.devservices.realm-name=petcare-realm
quarkus.keycloak.devservices.users.alice=Masterkey123
quarkus.keycloak.devservices.roles.alice=user
Testando a autenticação
curl -s -X POST 'http://localhost:13370/realms/petcare-realm/protocol/openid-connect/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=alice' \
--data-urlencode 'password=Masterkey123' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=quarkus-app' \
--data-urlencode 'client_secret=secret' | jq
WhoAmIResource
import io.quarkus.security.identity.SecurityIdentity;
import org.jboss.resteasy.annotations.cache.NoCache;
import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path("/user/whoami")
public class WhoAmIResource {
@Inject
SecurityIdentity securityIdentity;
@GET
@RolesAllowed("user")
@NoCache
public String me() {
var username = securityIdentity.getPrincipal().getName();
return username;
}
}
Enviando uma requisição com o token
export ACCESS_TOKEN=$(curl -s --location --request POST 'http://localhost:13370/realms/petcare-realm/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=alice' \
--data-urlencode 'password=Masterkey123' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=quarkus-app' \
--data-urlencode 'client_secret=secret' | jq -r '.access_token')
export ACCESS_HEADER="Authorization: Bearer $ACCESS_TOKEN"
curl -v --location \
--request GET 'http://localhost:8080/user/whoami' \
--header "$ACCESS_HEADER"
src/main/resources/META-INF/resources/[index.html]
quarkus ext add resteasy-qute
src/main/resources/templaters/greeting.html
GreetingResource
@Path("/greeting")
public class GreetingResource {
@Inject
Template greeting;
@GET
@Produces(MediaType.TEXT_HTML)
public TemplateInstance get(@QueryParam("name") @DefaultValue("fulano") String name) {
return greeting.data("name", name);
}
}
./mvnw package
# export
java -jar target/quarkus-app/quarkus-run.jar
Containers, native images & more!
cd ..
gh repo clone vaadin/base-starter-flow-quarkus
mv base-starter-flow-quarkus ptc-app
application.properties
# HTTP
quarkus.http.host=0.0.0.0
quarkus.http.port=8081
quarkus.http.cors=true
quarkus.http.limits.max-body-size=200M
# Disable DevSevices by default
quarkus.datasource.jdbc=false
quarkus.devservices.enabled=false
quarkus dev
localhost:8081 MainView
quarkus ext add rest-client
quarkus ext add rest-client-jackson
quarkus ext add oidc
quarkus ext add keycloak-authorization
WhoAmI View
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import javax.inject.Inject;
@Route("user/whoami")
public class WhoAmIView extends VerticalLayout {
@Inject
public WhoAmIView(@RestClient WhoAmIService whoAmIService) {
add(new Label("Hello " + whoAmIService.whoami()));
}
}
Client Interface
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
@RegisterRestClient(configKey = "whoami")
@RegisterClientHeaders(AuthClientHeaders.class)
public interface WhoAmIService {
@GET
String whoami();
}
Auth Headers
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
@Dependent
public class AuthClientHeaders implements ClientHeadersFactory {
@Inject
JsonWebToken accessToken;
@Override
public MultivaluedMap<String, String> update(
MultivaluedMap<String, String> incomingHeaders,
MultivaluedMap<String, String> clientOutgoingHeaders) {
var token = "Bearer " + accessToken.getRawToken();
return new MultivaluedHashMap<String, String>(){{
add("Authorization", token);
}};
}
}
application-dev.properties
quarkus.oidc.auth-server-url=http://localhost:13370/realms/petcare-realm
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.secret=secret
quarkus.oidc.application-type=web-app
quarkus.oidc.authentication.user-info-required=true
quarkus.http.auth.permission.authenticated.paths=/user/*
quarkus.http.auth.permission.authenticated.policy=authenticated
rest-client.base-uri=http://localhost:8080
quarkus.rest-client.whoami.uri=${rest-client.base-uri}/user/whoami
quarkus.rest-client.ping.hostname-verifier=io.quarkus.restclient.NoopHostnameVerifier
quarkus.tls.trust-all=true
http://localhost:8081/user/whoami
Julio Faerman
@faermanj
https://quarkus.io/guides/all-config
https://quarkus.io/guides/dev-services
https://lordofthejars.github.io/quarkus-cheat-sheet/
https://gist.github.com/faermanj/270a8a8ab817f95fc2e350ec2d481bd2