SlideShare une entreprise Scribd logo
1  sur  61
Télécharger pour lire hors ligne
A MICRONAUT
WALKS INTO A SPA
MICRONAUT FOR SINGLE-PAGE-APPS
A MICRONAUT WALKS INTO A SPA
ABOUT ME
▸ Zachary Klein is a Senior Software Engineer
at OCI. He has been practicing web
development since 2010 and frontend
development since 2015. He’s a
contributor to both the Grails and
Micronaut frameworks, a conference
speaker and an occasional instructor in
OCI’s training practice.
▸ Zachary’s home base is in St Louis, MO,
along with his wife, Beth, and their three
children.
▸ Brief Introduction to Micronaut
▸ Restful Backends
▸ Gateways
▸ Security with JWT
▸ Building with Gradle
▸ Deploying a SPA in a JAR
A MICRONAUT WALKS INTO A SPA
AGENDA
AND GRAPHQL?
BRIEF INTRODUCTION TO MICRONAUT
▸ Designed with Microservices in mind
▸ Reactive HTTP server built on Netty
▸ AOT (Ahead of Time) Compilation for DI, AOP, and
configuration
▸ Declarative HTTP Client
▸ “Natively” Cloud-Native: service-discovery, LB,
circuit-breakers, tracing, and more
▸ Support for Java, Kotlin and Groovy
A MICRONAUT WALKS INTO A SPA
MICRONAUT
A MICRONAUT WALKS INTO A SPA
MICRONAUT: GETTING STARTED
~ curl -s "https://get.sdkman.io" | bash
~ source "$HOME/.sdkman/bin/sdkman-init.sh"
~ sdk install micronaut
~ mn create-app hello-world
A MICRONAUT WALKS INTO A SPA
MICRONAUT CLI
▸ Language defaults to Java
▸ Use --lang to specify groovy or kotlin
▸ Build tool defaults to Gradle
▸ Use --build to specify maven
▸ Run mn without arguments to enter interactive
mode - includes tab-completion
A MICRONAUT WALKS INTO A SPA
@Controller("/")
class HelloController {
@Get("/hello/{name}")
String hello(String name) {
return "Hello " + name;
}
}
MICRONAUT: CONTROLLERS & CLIENTS
@Client("/")
interface HelloClient {
@Get("/hello/{name}")
String hello(String name);
// Implementation generated
// at compile time
}
A MICRONAUT WALKS INTO A SPA
MICRONAUT: DEPENDENCY INJECTION
@Singleton //Bean definition generated at compile time
class WeatherService {
Integer currentTemp() { //... }
}
@Controller('/weather')
class WeatherController {
@Inject WeatherService weatherService
//DI computed at compile time
@Get("/")
HttpResponse<Integer> currentTemp() {
HttpResponse.ok(weatherService.currentTemp())
}
}
A MICRONAUT WALKS INTO A SPA
MICRONAUT: CLOUD NATIVE
//Lookup client from service-discovery registry
@Client(id="billing", path=“/billing")
interface BillingClient { ... }
//Automatically retry failing calls
@Client("https://api.external.service")
@Retryable(attempts = '3', delay = '5ms')
interface ExternalApiClient { ... }
//Immediately fail after set number of failures
//Begin accepting calls after `reset` interval
@Singleton
@CircuitBreaker(attempts = '5', reset = '300ms')
class MyService { ... }
SERVICE DISCOVERY
RETRYABLE
CIRCUIT BREAKERS
A MICRONAUT WALKS INTO A SPA
MICRONAUT: CLOUD NATIVE
▸ Cloud-Aware Environment Detection
▸ Cloud Provider Integration - AWS, GCP, Spring
Cloud
▸ Metrics & Monitoring
▸ Distributed Configuration
▸ Distributed Tracing
https://angular.io https://vuejs.org https://reactjs.org
~ vue create my-project~ ng new my-dream-app ~ npx create-react-app my-app
RESTFUL BACKENDS
BackendFrontend
REST
{JSON}
BackendFrontend
REST
{JSON}
▸ Declarative Routes via method annotations:
▸ @Get, @Put, @Post, @Delete
▸ JSON binding/rendering via Jackson
▸ Request Arguments via annotations:
▸ @Header, @Body, @CookieValue, @QueryValue
A MICRONAUT WALKS INTO A SPA
MICRONAUT & REST
A MICRONAUT WALKS INTO A SPA
JACKSON: JSON BINDING
public class Author {
private String name;
@JsonSerialize(MySerializer.class)
private Date birthday;
}
@Post(“/")
public HttpResponse<Author> save(
@Body Author author) {
if(bookRepository.save(author)) {
return HttpResponse.ok();
} else {
/* handle error */
}
}
https://www.baeldung.com/jackson-annotations
fetch("http://localhost:8080/
author/", {
method: "POST",
headers: new Headers({
"Content-Type":
"application/json"
}),
body: JSON.stringify({
name: "Author's Name",
birthday: "01/31/1985"
})
})
JAVASCRIPT
A MICRONAUT WALKS INTO A SPA
JACKSON: JSON RENDERING
{
“name": "Title Here”,
"author": {
"name": "Author"
},
"pages": 150,
"tags": [
"tech",
"bestseller"
]
}
@JsonIgnoreProperties({"id", "version"})
public class Book {
private Long id;
private Long version;
@JsonProperty(“name")
private String title;
private Author author;
private Integer pages;
private List<String> tags;
}
@Get("/{id}")
public Book show(Serializable id) {
return bookRepository.get(id);
}
https://www.baeldung.com/jackson-annotations
JSON
A MICRONAUT WALKS INTO A SPA
@Controller("/book")
class BookController {
@Post
HttpResponse<BookDetails> save(@Valid @Body BookDetails bookDetails) { /* .. */}
@Put
HttpResponse<BookDetails> update(@Valid @Body BookDetails bookDetails) { /* .. */}
@Delete("/{id}")
HttpResponse delete(Serializable id) { /* .. */}
@Get(“{?max,offset}")
@Transactional(readOnly = false)
HttpResponse<List<Book>> list(@Nullable Integer max, @Nullable Integer offset) { /* .. */}
@Get("/{id}")
@Transactional(readOnly = true)
HttpResponse<BookDetails> get(Serializable id) { /* .. */}
HttpResponse<Integer> count() { /* .. */}
}
REST CONTROLLER
BOOKCONTROLLER.JAVA
A MICRONAUT WALKS INTO A SPA
import { customHeaders } from "../security/SecurityUtils";
import { composeResourceURL } from "../rest/ClientUtils";
const list = (max, offset, callback, errorCallback) => {
fetch(composeResourceURL(“book","/", { max, offset }), {
method: "GET",
headers: customHeaders()
})
.then(response => response.json())
.then(json => callback(json))
.catch(error => errorCallback(error));
};
const get = (id, callback, errorCallback) => {
/* .. */
};
const save = (resource, callback, errorCallback) => {
/* .. */
};
const update = (resource, callback, errorCallback) => {
/* .. */
};
const remove = (id, callback, errorCallback) => {
/* .. */
};
export default { list, count, get, save, update, remove };
JAVASCRIPT CLIENT
BOOKCLIENT.JS
▸ Framework-agnostic client
▸ Using the fetch API (axios
is another popular option)
▸ Based on callback
functions (async/await is
another option)
▸ customHeaders() function
could return Authorization
headers, tenantId for
multi-tenancy, etc)
A MICRONAUT WALKS INTO A SPA
JAVASCRIPT CLIENT const composeResourceURL = (resource, append, params) => {
if (params !== null) {
append = `${append}${composeParams(params)}`;
}
return `${process.env.SERVER_URL}/${resource}/${append}`;
};
const composeParams = params => {
let paramString = "";
const paramKeys = Object.keys(params);
if (paramKeys.length > 0) {
paramString = "?";
paramKeys.map(key => {
const paramValue = params[key];
if (paramValue !== null && paramValue !== undefined) {
if (paramString !== "?") {
paramString = `${paramString}&`;
}
paramString = `${paramString}${key}=${params[key]}`;
}
});
}
return paramString;
};
export default { composeResourceURL };
CLIENTUTILS.JS
▸ General purpose REST
client functions
▸ Compose restful URL
with resource name,
path, parameters
▸ Compose URL
parameters from object
▸ Resolve Gateway URL
via Node environment
variable (e.g, SERVER_URL)
A MICRONAUT WALKS INTO A SPA
ENABLING CORS
▸ CORS support included in
Micronaut
▸ Disabled by default
▸ Can specify allowed
origins, methods, headers,
max age, and more.
micronaut:
application:
name: my-app
server:
cors:
enabled: true
APPLICATION.YML
▸ An alternative to REST
▸ Server provides a schema that
describes exposed resources
▸ Schema defines queries and mutations
▸ Clients can request only the resources
& fields they want
▸ Client libraries can intelligently merge
queries for efficiency
A MICRONAUT WALKS INTO A SPA
GRAPHQL
https://medium.freecodecamp.org/so-whats-this-graphql-thing-i-keep-hearing-about-baf4d36c20cf?gi=e256cd305c64
A MICRONAUT WALKS INTO A SPA
MICRONAUT & GRAPHQL
▸ micronaut-graphql library - contributed by Marcel Overdijk
▸ Wraps graphql-java and provides a default controller for accepting
queries & mutations
▸ The GraphQL schema still needs to be created, either manually via the
graphql-java API, or an integration library such as GORM for GraphQL
▸ Packages the GraphiQL in-browser IDE for exploring the GraphQL schema
▸ Example Projects: https://github.com/micronaut-projects/micronaut-
graphql/tree/master/examples/
A MICRONAUT WALKS INTO A SPA
MICRONAUT & GRAPHQL
dependencies {
compile “io.micronaut.graphql:micronaut-graphql”
}
BUILD.GRADLE
GATEWAYS
▸ Architectural pattern for microservice-based systems
▸ Expose a single client-facing API (for SPA, mobile, etc)
▸ Minimizing integration points - decoupling
▸ https://microservices.io/patterns/apigateway.html
A MICRONAUT WALKS INTO A SPA
GATEWAYS
Backend
Frontend
BillingMailAnalyticsInventory
Frontend
BillingMailAnalyticsInventory
Frontend
Billing
Frontend
MailAnalyticsInventory
Gateway
▸ Version by URL: @Get("/v1/user/profile")
▸ Using config property:
@Value("${core.api.version}")
String version
@Get("/${version}/user/profile")
‣ Client-facing versioning can be separate from versioning
within the microservice architecture
A MICRONAUT WALKS INTO A SPA
VERSIONING AN API
core:
api:
version: v1
APPLICATION.YML
BillingMailAnalyticsInventory
Frontend
Gateway
V1
Gateway
V2
BillingMailAnalyticsInventory
Frontend
Gateway
V1
Gateway
V2
▸ Partition your API
▸ Support different
client experiences/
functions (e.g, admin
vs customer)
A MICRONAUT WALKS INTO A SPA
MULTIPLE GATEWAYS
Billing
Web
MailAnalyticsInventory
Gateway
Admin Web
Admin
Gateway
Mobile
Mobile
Gateway
A MICRONAUT WALKS INTO A SPA
MICRONAUT PETSTORE
https://github.com/micronaut-projects/micronaut-examples/tree/master/petstore
SECURITY WITH JWT
A MICRONAUT WALKS INTO A SPA
JWTI: JSON WEB TOKEN
‣ Open standard for representing
claims securely between two parties
‣ Tokens can be signed with either a
secret or public/private key
‣ Standard approach for stateless
authentication
‣ Ideal for transmitting authentication
& authorization data between
microservices and single-page-apps
A MICRONAUT WALKS INTO A SPA
MICRONAUT SECURITY
▸ Core Micronaut Library - supports JWT, Session, Basic Auth
▸ Annotation-based API & config-based URL mappings
▸ Support for token propagation
▸ Supports RFC 6750 Bearer Token 
▸ JWTs can be read from cookie
dependencies {
compile "io.micronaut:micronaut-security-jwt"
}
micronaut:
security:
enabled: true
token:
jwt:
enabled: true
signatures:
secret:
generator:
secret: changeMe
APPLICATION.YML
BUILD.GRADLE
A MICRONAUT WALKS INTO A SPA
@SECURED ANNOTATION
▸ @Secured annotation applied
to controllers and methods
▸ All routes blocked by default
▸ Can require authentication
and/or authorization (role-
based)
▸ Alternative: JSR-250 security
annotations are also
supported: @PermitAll,
@RolesAllowed, @DenyAll
import java.security.Principal;
@Secured("isAuthenticated()")
@Controller("/")
public class HomeController {
@Get("/")
String index(Principal principal) {
return principal.getName();
}
@Secured({"ROLE_ADMIN", "ROLE_X"})
@Get("/classified")
String classified() {
return /* REDACTED */;
}
}
A MICRONAUT WALKS INTO A SPA
‣ Unauthorized request is made to API
‣ Responds with 401
‣ Client POSTs to login endpoint
‣ Server responds with JWT
‣ Client includes access token in the
Authorization header for subsequent
requests
‣ Server validates the incoming token
‣ If authorized, server responds with
resource
MICRONAUT JWT SECURITY
A MICRONAUT WALKS INTO A SPA
‣ POST credentials to the /
login endpoint
‣ Retrieve access token
from JWT and store in
application state, a
cookie, or local storage*
JAVASCRIPT JWT SECURITY const login = (credentials, callback, errorCallback) => {
fetch(`${process.env.SERVER_URL}/login`, {
method: "POST",
headers: new Headers({
Accept: "application/json",
"Content-Type": "application/json",
}),
body: JSON.stringify(credentials)
})
.then(response => {
if (response.ok) {
return response.json();
} else {
throw Error(response.statusText);
}
})
.then(json => callback(json))
.catch(error => errorCallback(error));
};
export default { login };
AUTHCLIENT.JS
import AuthClient from “./AuthClient";
AuthClient.login(
{ username: this.username, password: this.password },
json => /* handle login success */,
error => console.error(error)
);
*Local storage is not inherently secure:
https://www.rdegges.com/2018/please-stop-using-local-storage/
A MICRONAUT WALKS INTO A SPA
‣ Use custom Headers
object to include access
token (if using Bearer)
JAVASCRIPT JWT SECURITY
const securedHeaders = () => {
return new Headers({
Authorization: `Bearer ${token()}`,
"Content-Type": "application/json"
});
};
const token = () => //retrieve token from store;
export default { securedHeaders };
SECURITYUTILS.JS
import { securedHeaders } from "./SecurityUtils";
fetch(`${process.env.SERVER_URL}/home`, {
method: "GET",
headers: securedHeaders()
})
.then(response => response.text())
.then(text => (this.content = text))
.catch(error => {
console.log("Connection failure: ", error);
});
A MICRONAUT WALKS INTO A SPA
MICRONAUT SECURITY GUIDES
https://guides.micronaut.io/tags/security.html
BUILDING SPAS WITH GRADLE
MULTIPROJECT BUILDS
A MICRONAUT WALKS INTO A SPA
JAVASCRIPT APP
BackendFrontend
include "client", “server"
SETTINGS.GRADLE
GRADLE NODE PLUGIN
A MICRONAUT WALKS INTO A SPA
plugins {
id "com.github.node-gradle.node" version "1.3.0"
}
node {
version = '10.15.0' // https://nodejs.org/en/
yarnVersion = '1.13.0' // https://yarnpkg.com/en/
download = true
}
task start(type: YarnTask, dependsOn: 'yarn') {
group = 'application'
description = 'Run the client app'
args = ['run', 'start']
}
task build(type: YarnTask, dependsOn: 'yarn') {
group = 'build'
description = 'Build the client bundle'
args = ['run', 'build']
}
task test(type: YarnTask, dependsOn: 'yarn') {
group = 'verification'
description = 'Run the client tests'
args = ['run', 'test']
}
task eject(type: YarnTask, dependsOn: 'yarn') {
//...
}
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
//...
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
~ gradle start //yarn start
Server running on http://localhost:3000
BUILD.GRADLE PACKAGE.JSON
DEPLOYING A SPA IN A JAR
▸ Serve the Single-Page-App from within an executable JAR
file
▸ Removes need for a separate static server
▸ SPA can be packaged with its gateway
▸ Simplifies deployment of small apps and/or IoT
deployments
A MICRONAUT WALKS INTO A SPA
DEPLOYING A SPA IN A JAR
~ gradle shadowJar
BUILD SUCCESSFUL in 1s
DEPLOYMENT
A MICRONAUT WALKS INTO A SPA
JAVASCRIPT APP
~ yarn build
Creating a production build...
Compiled successfully.
DEPLOYMENT
A MICRONAUT WALKS INTO A SPA
JAVASCRIPT APP
BackendFrontend
COMBINED BUILD
A MICRONAUT WALKS INTO A SPA
~ java -jar build/libs/server-all.jar
Server Running: http://localhost:8080
STATIC ASSETS IN MICRONAUT
A MICRONAUT WALKS INTO A SPA
▸ Disabled by default
▸
▸
▸ List of paths, starting with classpath: or file:
▸
▸ The path from which resources should be served
micronaut.router.static-resources.*.enabled
micronaut.router.static-resources.*.paths
micronaut.router.static-resources.*.mapping
STATIC ASSETS IN MICRONAUT
A MICRONAUT WALKS INTO A SPA
micronaut:
router:
static-resources:
default:
enabled: true
mapping: "/**"
paths: "classpath:public"
local:
enabled: true
mapping: “/local/**"
paths: “file:path/to/files“
APPLICATION.YML
COMBINED BUILD
A MICRONAUT WALKS INTO A SPA
//Copy production SPA resources into server build directory
task copyClientResources(dependsOn: ':client:build') {
group = 'build'
description = 'Copy client resources into server'
}
copyClientResources.doFirst {
copy {
from project(':client').buildDir.absolutePath
into "${project(':server').buildDir}/resources/main/public"
}
}
//Build a single executable JAR with embedded SPA resources
task assembleServerAndClient(
dependsOn: ['copyClientResources', ':server:shadowJar']) {
group = 'build'
description = 'Build combined server & client JAR'
}
BUILD.GRADLE
COMBINED BUILD
A MICRONAUT WALKS INTO A SPA
~ java -jar build/libs/server-all.jar
Server Running: http://localhost:8080
MICRONAUT SINGLE-PAGE-APP GUIDE
A MICRONAUT WALKS INTO A SPA
https://guides.micronaut.io/micronaut-spa-react/guide/index.htmlNEW!
A MICRONAUT WALKS INTO A SPA
MICRONAUT PETSTORE
https://github.com/micronaut-projects/micronaut-examples/tree/master/petstore
A MICRONAUT WALKS INTO A SPA
LEARN MORE ABOUT OCI EVENTS AND TRAINING
Events:
‣ objectcomputing.com/events
Training:
‣ objectcomputing.com/training
‣ grailstraining.com
‣ micronauttraining.com
COME SEE US
AT OUR BOOTH!
A MICRONAUT WALKS INTO A SPA
THANK YOU@ZacharyAKlein kleinz@objectcomputing.com

Contenu connexe

Tendances

HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLHandlerSocket plugin for MySQL
HandlerSocket plugin for MySQL
akirahiguchi
 
なんとなくOAuth怖いって思ってるやつちょっと来い
なんとなくOAuth怖いって思ってるやつちょっと来いなんとなくOAuth怖いって思ってるやつちょっと来い
なんとなくOAuth怖いって思ってるやつちょっと来い
Ryo Ito
 
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くしたNginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
toshi_pp
 

Tendances (20)

HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLHandlerSocket plugin for MySQL
HandlerSocket plugin for MySQL
 
Harbor RegistryのReplication機能
Harbor RegistryのReplication機能Harbor RegistryのReplication機能
Harbor RegistryのReplication機能
 
Draft: building secure applications with keycloak (oidc/jwt)
Draft: building secure applications with keycloak (oidc/jwt)Draft: building secure applications with keycloak (oidc/jwt)
Draft: building secure applications with keycloak (oidc/jwt)
 
なんとなくOAuth怖いって思ってるやつちょっと来い
なんとなくOAuth怖いって思ってるやつちょっと来いなんとなくOAuth怖いって思ってるやつちょっと来い
なんとなくOAuth怖いって思ってるやつちょっと来い
 
User Management Life Cycle with Keycloak
User Management Life Cycle with KeycloakUser Management Life Cycle with Keycloak
User Management Life Cycle with Keycloak
 
ECMS2 Training Slides.pdf
ECMS2 Training Slides.pdfECMS2 Training Slides.pdf
ECMS2 Training Slides.pdf
 
Azure Cosmos DB で始める Java + NoSQL 開発
Azure Cosmos DB で始める Java + NoSQL 開発Azure Cosmos DB で始める Java + NoSQL 開発
Azure Cosmos DB で始める Java + NoSQL 開発
 
3. 마이크로 서비스 아키텍쳐
3. 마이크로 서비스 아키텍쳐3. 마이크로 서비스 아키텍쳐
3. 마이크로 서비스 아키텍쳐
 
Concourseで快適な自動化の旅
Concourseで快適な自動化の旅Concourseで快適な自動化の旅
Concourseで快適な自動化の旅
 
대용량 분산 아키텍쳐 설계 #5. rest
대용량 분산 아키텍쳐 설계 #5. rest대용량 분산 아키텍쳐 설계 #5. rest
대용량 분산 아키텍쳐 설계 #5. rest
 
HA Deployment Architecture with HAProxy and Keepalived
HA Deployment Architecture with HAProxy and KeepalivedHA Deployment Architecture with HAProxy and Keepalived
HA Deployment Architecture with HAProxy and Keepalived
 
Certificate Pinning in Mobile Applications
Certificate Pinning in Mobile ApplicationsCertificate Pinning in Mobile Applications
Certificate Pinning in Mobile Applications
 
講演資料: コスト最適なプライベートCDNを「NGINX」で実現するWeb最適化セミナー
講演資料: コスト最適なプライベートCDNを「NGINX」で実現するWeb最適化セミナー講演資料: コスト最適なプライベートCDNを「NGINX」で実現するWeb最適化セミナー
講演資料: コスト最適なプライベートCDNを「NGINX」で実現するWeb最適化セミナー
 
OAuth 2.0の概要とセキュリティ
OAuth 2.0の概要とセキュリティOAuth 2.0の概要とセキュリティ
OAuth 2.0の概要とセキュリティ
 
Spring Security 5.5 From Taxi to Takeoff
Spring Security 5.5 From Taxi to TakeoffSpring Security 5.5 From Taxi to Takeoff
Spring Security 5.5 From Taxi to Takeoff
 
最新Active DirectoryによるIDMaaSとハイブリッド認証基盤の実現
最新Active DirectoryによるIDMaaSとハイブリッド認証基盤の実現最新Active DirectoryによるIDMaaSとハイブリッド認証基盤の実現
最新Active DirectoryによるIDMaaSとハイブリッド認証基盤の実現
 
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くしたNginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
 
SI現場のテスト自動化への挑戦〜フルコンテナ構成のCI/CD環境〜
SI現場のテスト自動化への挑戦〜フルコンテナ構成のCI/CD環境〜SI現場のテスト自動化への挑戦〜フルコンテナ構成のCI/CD環境〜
SI現場のテスト自動化への挑戦〜フルコンテナ構成のCI/CD環境〜
 
オープンソースで始めるAR/VR開発
オープンソースで始めるAR/VR開発オープンソースで始めるAR/VR開発
オープンソースで始めるAR/VR開発
 
OpenAPI 3.0, And What It Means for the Future of Swagger
OpenAPI 3.0, And What It Means for the Future of SwaggerOpenAPI 3.0, And What It Means for the Future of Swagger
OpenAPI 3.0, And What It Means for the Future of Swagger
 

Similaire à Micronaut For Single Page Apps

Similaire à Micronaut For Single Page Apps (20)

10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 202010 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
 
Micronaut Launchpad
Micronaut LaunchpadMicronaut Launchpad
Micronaut Launchpad
 
Multi Client Development with Spring
Multi Client Development with SpringMulti Client Development with Spring
Multi Client Development with Spring
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API's
 
TDC 2016 - Arquitetura Java - Spring Cloud
TDC 2016 - Arquitetura Java - Spring CloudTDC 2016 - Arquitetura Java - Spring Cloud
TDC 2016 - Arquitetura Java - Spring Cloud
 
TDC2016SP - Construindo Microserviços usando Spring Cloud
TDC2016SP - Construindo Microserviços usando Spring CloudTDC2016SP - Construindo Microserviços usando Spring Cloud
TDC2016SP - Construindo Microserviços usando Spring Cloud
 
ASP.NET MVC Internals
ASP.NET MVC InternalsASP.NET MVC Internals
ASP.NET MVC Internals
 
Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCL
 
Arquitecturas de microservicios - Medianet Software
Arquitecturas de microservicios   -  Medianet SoftwareArquitecturas de microservicios   -  Medianet Software
Arquitecturas de microservicios - Medianet Software
 
Dropwizard Introduction
Dropwizard IntroductionDropwizard Introduction
Dropwizard Introduction
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless
 
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
 
Dwr
DwrDwr
Dwr
 
Backbone js in action
Backbone js in actionBackbone js in action
Backbone js in action
 
Java Configuration Deep Dive with Spring
Java Configuration Deep Dive with SpringJava Configuration Deep Dive with Spring
Java Configuration Deep Dive with Spring
 
Hazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMSHazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMS
 
Futures and Rx Observables: powerful abstractions for consuming web services ...
Futures and Rx Observables: powerful abstractions for consuming web services ...Futures and Rx Observables: powerful abstractions for consuming web services ...
Futures and Rx Observables: powerful abstractions for consuming web services ...
 
Serverless archtiectures
Serverless archtiecturesServerless archtiectures
Serverless archtiectures
 
Introduction to angular js
Introduction to angular jsIntroduction to angular js
Introduction to angular js
 

Plus de Zachary Klein

Using React with Grails 3
Using React with Grails 3Using React with Grails 3
Using React with Grails 3
Zachary Klein
 

Plus de Zachary Klein (10)

Native Cloud-Native: Building Agile Microservices with the Micronaut Framework
Native Cloud-Native: Building Agile Microservices with the Micronaut FrameworkNative Cloud-Native: Building Agile Microservices with the Micronaut Framework
Native Cloud-Native: Building Agile Microservices with the Micronaut Framework
 
Groovy-Powered Microservices with Micronaut
Groovy-Powered Microservices with MicronautGroovy-Powered Microservices with Micronaut
Groovy-Powered Microservices with Micronaut
 
Micronaut: Changing the Micro Future
Micronaut: Changing the Micro FutureMicronaut: Changing the Micro Future
Micronaut: Changing the Micro Future
 
Grails 4: Upgrade your Game!
Grails 4: Upgrade your Game!Grails 4: Upgrade your Game!
Grails 4: Upgrade your Game!
 
Getting Groovy with JHipster and Micronaut
Getting Groovy with JHipster and MicronautGetting Groovy with JHipster and Micronaut
Getting Groovy with JHipster and Micronaut
 
Groovy for Java Devs
Groovy for Java DevsGroovy for Java Devs
Groovy for Java Devs
 
Grails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to OrbitGrails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to Orbit
 
Room with a Vue - Introduction to Vue.js
Room with a Vue - Introduction to Vue.jsRoom with a Vue - Introduction to Vue.js
Room with a Vue - Introduction to Vue.js
 
Shields Up! Securing React Apps
Shields Up! Securing React AppsShields Up! Securing React Apps
Shields Up! Securing React Apps
 
Using React with Grails 3
Using React with Grails 3Using React with Grails 3
Using React with Grails 3
 

Dernier

Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
chiefasafspells
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 

Dernier (20)

Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 

Micronaut For Single Page Apps

  • 1. A MICRONAUT WALKS INTO A SPA MICRONAUT FOR SINGLE-PAGE-APPS
  • 2. A MICRONAUT WALKS INTO A SPA ABOUT ME ▸ Zachary Klein is a Senior Software Engineer at OCI. He has been practicing web development since 2010 and frontend development since 2015. He’s a contributor to both the Grails and Micronaut frameworks, a conference speaker and an occasional instructor in OCI’s training practice. ▸ Zachary’s home base is in St Louis, MO, along with his wife, Beth, and their three children.
  • 3. ▸ Brief Introduction to Micronaut ▸ Restful Backends ▸ Gateways ▸ Security with JWT ▸ Building with Gradle ▸ Deploying a SPA in a JAR A MICRONAUT WALKS INTO A SPA AGENDA AND GRAPHQL?
  • 5. ▸ Designed with Microservices in mind ▸ Reactive HTTP server built on Netty ▸ AOT (Ahead of Time) Compilation for DI, AOP, and configuration ▸ Declarative HTTP Client ▸ “Natively” Cloud-Native: service-discovery, LB, circuit-breakers, tracing, and more ▸ Support for Java, Kotlin and Groovy A MICRONAUT WALKS INTO A SPA MICRONAUT
  • 6. A MICRONAUT WALKS INTO A SPA MICRONAUT: GETTING STARTED ~ curl -s "https://get.sdkman.io" | bash ~ source "$HOME/.sdkman/bin/sdkman-init.sh" ~ sdk install micronaut ~ mn create-app hello-world
  • 7. A MICRONAUT WALKS INTO A SPA MICRONAUT CLI ▸ Language defaults to Java ▸ Use --lang to specify groovy or kotlin ▸ Build tool defaults to Gradle ▸ Use --build to specify maven ▸ Run mn without arguments to enter interactive mode - includes tab-completion
  • 8. A MICRONAUT WALKS INTO A SPA @Controller("/") class HelloController { @Get("/hello/{name}") String hello(String name) { return "Hello " + name; } } MICRONAUT: CONTROLLERS & CLIENTS @Client("/") interface HelloClient { @Get("/hello/{name}") String hello(String name); // Implementation generated // at compile time }
  • 9. A MICRONAUT WALKS INTO A SPA MICRONAUT: DEPENDENCY INJECTION @Singleton //Bean definition generated at compile time class WeatherService { Integer currentTemp() { //... } } @Controller('/weather') class WeatherController { @Inject WeatherService weatherService //DI computed at compile time @Get("/") HttpResponse<Integer> currentTemp() { HttpResponse.ok(weatherService.currentTemp()) } }
  • 10. A MICRONAUT WALKS INTO A SPA MICRONAUT: CLOUD NATIVE //Lookup client from service-discovery registry @Client(id="billing", path=“/billing") interface BillingClient { ... } //Automatically retry failing calls @Client("https://api.external.service") @Retryable(attempts = '3', delay = '5ms') interface ExternalApiClient { ... } //Immediately fail after set number of failures //Begin accepting calls after `reset` interval @Singleton @CircuitBreaker(attempts = '5', reset = '300ms') class MyService { ... } SERVICE DISCOVERY RETRYABLE CIRCUIT BREAKERS
  • 11. A MICRONAUT WALKS INTO A SPA MICRONAUT: CLOUD NATIVE ▸ Cloud-Aware Environment Detection ▸ Cloud Provider Integration - AWS, GCP, Spring Cloud ▸ Metrics & Monitoring ▸ Distributed Configuration ▸ Distributed Tracing
  • 12.
  • 13. https://angular.io https://vuejs.org https://reactjs.org ~ vue create my-project~ ng new my-dream-app ~ npx create-react-app my-app
  • 17. ▸ Declarative Routes via method annotations: ▸ @Get, @Put, @Post, @Delete ▸ JSON binding/rendering via Jackson ▸ Request Arguments via annotations: ▸ @Header, @Body, @CookieValue, @QueryValue A MICRONAUT WALKS INTO A SPA MICRONAUT & REST
  • 18. A MICRONAUT WALKS INTO A SPA JACKSON: JSON BINDING public class Author { private String name; @JsonSerialize(MySerializer.class) private Date birthday; } @Post(“/") public HttpResponse<Author> save( @Body Author author) { if(bookRepository.save(author)) { return HttpResponse.ok(); } else { /* handle error */ } } https://www.baeldung.com/jackson-annotations fetch("http://localhost:8080/ author/", { method: "POST", headers: new Headers({ "Content-Type": "application/json" }), body: JSON.stringify({ name: "Author's Name", birthday: "01/31/1985" }) }) JAVASCRIPT
  • 19. A MICRONAUT WALKS INTO A SPA JACKSON: JSON RENDERING { “name": "Title Here”, "author": { "name": "Author" }, "pages": 150, "tags": [ "tech", "bestseller" ] } @JsonIgnoreProperties({"id", "version"}) public class Book { private Long id; private Long version; @JsonProperty(“name") private String title; private Author author; private Integer pages; private List<String> tags; } @Get("/{id}") public Book show(Serializable id) { return bookRepository.get(id); } https://www.baeldung.com/jackson-annotations JSON
  • 20. A MICRONAUT WALKS INTO A SPA @Controller("/book") class BookController { @Post HttpResponse<BookDetails> save(@Valid @Body BookDetails bookDetails) { /* .. */} @Put HttpResponse<BookDetails> update(@Valid @Body BookDetails bookDetails) { /* .. */} @Delete("/{id}") HttpResponse delete(Serializable id) { /* .. */} @Get(“{?max,offset}") @Transactional(readOnly = false) HttpResponse<List<Book>> list(@Nullable Integer max, @Nullable Integer offset) { /* .. */} @Get("/{id}") @Transactional(readOnly = true) HttpResponse<BookDetails> get(Serializable id) { /* .. */} HttpResponse<Integer> count() { /* .. */} } REST CONTROLLER BOOKCONTROLLER.JAVA
  • 21. A MICRONAUT WALKS INTO A SPA import { customHeaders } from "../security/SecurityUtils"; import { composeResourceURL } from "../rest/ClientUtils"; const list = (max, offset, callback, errorCallback) => { fetch(composeResourceURL(“book","/", { max, offset }), { method: "GET", headers: customHeaders() }) .then(response => response.json()) .then(json => callback(json)) .catch(error => errorCallback(error)); }; const get = (id, callback, errorCallback) => { /* .. */ }; const save = (resource, callback, errorCallback) => { /* .. */ }; const update = (resource, callback, errorCallback) => { /* .. */ }; const remove = (id, callback, errorCallback) => { /* .. */ }; export default { list, count, get, save, update, remove }; JAVASCRIPT CLIENT BOOKCLIENT.JS ▸ Framework-agnostic client ▸ Using the fetch API (axios is another popular option) ▸ Based on callback functions (async/await is another option) ▸ customHeaders() function could return Authorization headers, tenantId for multi-tenancy, etc)
  • 22. A MICRONAUT WALKS INTO A SPA JAVASCRIPT CLIENT const composeResourceURL = (resource, append, params) => { if (params !== null) { append = `${append}${composeParams(params)}`; } return `${process.env.SERVER_URL}/${resource}/${append}`; }; const composeParams = params => { let paramString = ""; const paramKeys = Object.keys(params); if (paramKeys.length > 0) { paramString = "?"; paramKeys.map(key => { const paramValue = params[key]; if (paramValue !== null && paramValue !== undefined) { if (paramString !== "?") { paramString = `${paramString}&`; } paramString = `${paramString}${key}=${params[key]}`; } }); } return paramString; }; export default { composeResourceURL }; CLIENTUTILS.JS ▸ General purpose REST client functions ▸ Compose restful URL with resource name, path, parameters ▸ Compose URL parameters from object ▸ Resolve Gateway URL via Node environment variable (e.g, SERVER_URL)
  • 23. A MICRONAUT WALKS INTO A SPA ENABLING CORS ▸ CORS support included in Micronaut ▸ Disabled by default ▸ Can specify allowed origins, methods, headers, max age, and more. micronaut: application: name: my-app server: cors: enabled: true APPLICATION.YML
  • 24. ▸ An alternative to REST ▸ Server provides a schema that describes exposed resources ▸ Schema defines queries and mutations ▸ Clients can request only the resources & fields they want ▸ Client libraries can intelligently merge queries for efficiency A MICRONAUT WALKS INTO A SPA GRAPHQL https://medium.freecodecamp.org/so-whats-this-graphql-thing-i-keep-hearing-about-baf4d36c20cf?gi=e256cd305c64
  • 25. A MICRONAUT WALKS INTO A SPA MICRONAUT & GRAPHQL
  • 26. ▸ micronaut-graphql library - contributed by Marcel Overdijk ▸ Wraps graphql-java and provides a default controller for accepting queries & mutations ▸ The GraphQL schema still needs to be created, either manually via the graphql-java API, or an integration library such as GORM for GraphQL ▸ Packages the GraphiQL in-browser IDE for exploring the GraphQL schema ▸ Example Projects: https://github.com/micronaut-projects/micronaut- graphql/tree/master/examples/ A MICRONAUT WALKS INTO A SPA MICRONAUT & GRAPHQL dependencies { compile “io.micronaut.graphql:micronaut-graphql” } BUILD.GRADLE
  • 28. ▸ Architectural pattern for microservice-based systems ▸ Expose a single client-facing API (for SPA, mobile, etc) ▸ Minimizing integration points - decoupling ▸ https://microservices.io/patterns/apigateway.html A MICRONAUT WALKS INTO A SPA GATEWAYS
  • 33. ▸ Version by URL: @Get("/v1/user/profile") ▸ Using config property: @Value("${core.api.version}") String version @Get("/${version}/user/profile") ‣ Client-facing versioning can be separate from versioning within the microservice architecture A MICRONAUT WALKS INTO A SPA VERSIONING AN API core: api: version: v1 APPLICATION.YML
  • 36. ▸ Partition your API ▸ Support different client experiences/ functions (e.g, admin vs customer) A MICRONAUT WALKS INTO A SPA MULTIPLE GATEWAYS Billing Web MailAnalyticsInventory Gateway Admin Web Admin Gateway Mobile Mobile Gateway
  • 37. A MICRONAUT WALKS INTO A SPA MICRONAUT PETSTORE https://github.com/micronaut-projects/micronaut-examples/tree/master/petstore
  • 39. A MICRONAUT WALKS INTO A SPA JWTI: JSON WEB TOKEN ‣ Open standard for representing claims securely between two parties ‣ Tokens can be signed with either a secret or public/private key ‣ Standard approach for stateless authentication ‣ Ideal for transmitting authentication & authorization data between microservices and single-page-apps
  • 40. A MICRONAUT WALKS INTO A SPA MICRONAUT SECURITY ▸ Core Micronaut Library - supports JWT, Session, Basic Auth ▸ Annotation-based API & config-based URL mappings ▸ Support for token propagation ▸ Supports RFC 6750 Bearer Token  ▸ JWTs can be read from cookie dependencies { compile "io.micronaut:micronaut-security-jwt" } micronaut: security: enabled: true token: jwt: enabled: true signatures: secret: generator: secret: changeMe APPLICATION.YML BUILD.GRADLE
  • 41. A MICRONAUT WALKS INTO A SPA @SECURED ANNOTATION ▸ @Secured annotation applied to controllers and methods ▸ All routes blocked by default ▸ Can require authentication and/or authorization (role- based) ▸ Alternative: JSR-250 security annotations are also supported: @PermitAll, @RolesAllowed, @DenyAll import java.security.Principal; @Secured("isAuthenticated()") @Controller("/") public class HomeController { @Get("/") String index(Principal principal) { return principal.getName(); } @Secured({"ROLE_ADMIN", "ROLE_X"}) @Get("/classified") String classified() { return /* REDACTED */; } }
  • 42. A MICRONAUT WALKS INTO A SPA ‣ Unauthorized request is made to API ‣ Responds with 401 ‣ Client POSTs to login endpoint ‣ Server responds with JWT ‣ Client includes access token in the Authorization header for subsequent requests ‣ Server validates the incoming token ‣ If authorized, server responds with resource MICRONAUT JWT SECURITY
  • 43. A MICRONAUT WALKS INTO A SPA ‣ POST credentials to the / login endpoint ‣ Retrieve access token from JWT and store in application state, a cookie, or local storage* JAVASCRIPT JWT SECURITY const login = (credentials, callback, errorCallback) => { fetch(`${process.env.SERVER_URL}/login`, { method: "POST", headers: new Headers({ Accept: "application/json", "Content-Type": "application/json", }), body: JSON.stringify(credentials) }) .then(response => { if (response.ok) { return response.json(); } else { throw Error(response.statusText); } }) .then(json => callback(json)) .catch(error => errorCallback(error)); }; export default { login }; AUTHCLIENT.JS import AuthClient from “./AuthClient"; AuthClient.login( { username: this.username, password: this.password }, json => /* handle login success */, error => console.error(error) ); *Local storage is not inherently secure: https://www.rdegges.com/2018/please-stop-using-local-storage/
  • 44. A MICRONAUT WALKS INTO A SPA ‣ Use custom Headers object to include access token (if using Bearer) JAVASCRIPT JWT SECURITY const securedHeaders = () => { return new Headers({ Authorization: `Bearer ${token()}`, "Content-Type": "application/json" }); }; const token = () => //retrieve token from store; export default { securedHeaders }; SECURITYUTILS.JS import { securedHeaders } from "./SecurityUtils"; fetch(`${process.env.SERVER_URL}/home`, { method: "GET", headers: securedHeaders() }) .then(response => response.text()) .then(text => (this.content = text)) .catch(error => { console.log("Connection failure: ", error); });
  • 45. A MICRONAUT WALKS INTO A SPA MICRONAUT SECURITY GUIDES https://guides.micronaut.io/tags/security.html
  • 47. MULTIPROJECT BUILDS A MICRONAUT WALKS INTO A SPA JAVASCRIPT APP BackendFrontend include "client", “server" SETTINGS.GRADLE
  • 48. GRADLE NODE PLUGIN A MICRONAUT WALKS INTO A SPA plugins { id "com.github.node-gradle.node" version "1.3.0" } node { version = '10.15.0' // https://nodejs.org/en/ yarnVersion = '1.13.0' // https://yarnpkg.com/en/ download = true } task start(type: YarnTask, dependsOn: 'yarn') { group = 'application' description = 'Run the client app' args = ['run', 'start'] } task build(type: YarnTask, dependsOn: 'yarn') { group = 'build' description = 'Build the client bundle' args = ['run', 'build'] } task test(type: YarnTask, dependsOn: 'yarn') { group = 'verification' description = 'Run the client tests' args = ['run', 'test'] } task eject(type: YarnTask, dependsOn: 'yarn') { //... } { "name": "client", "version": "0.1.0", "private": true, "dependencies": { //... }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, ~ gradle start //yarn start Server running on http://localhost:3000 BUILD.GRADLE PACKAGE.JSON
  • 49. DEPLOYING A SPA IN A JAR
  • 50. ▸ Serve the Single-Page-App from within an executable JAR file ▸ Removes need for a separate static server ▸ SPA can be packaged with its gateway ▸ Simplifies deployment of small apps and/or IoT deployments A MICRONAUT WALKS INTO A SPA DEPLOYING A SPA IN A JAR
  • 51. ~ gradle shadowJar BUILD SUCCESSFUL in 1s DEPLOYMENT A MICRONAUT WALKS INTO A SPA JAVASCRIPT APP ~ yarn build Creating a production build... Compiled successfully.
  • 52. DEPLOYMENT A MICRONAUT WALKS INTO A SPA JAVASCRIPT APP BackendFrontend
  • 53. COMBINED BUILD A MICRONAUT WALKS INTO A SPA ~ java -jar build/libs/server-all.jar Server Running: http://localhost:8080
  • 54. STATIC ASSETS IN MICRONAUT A MICRONAUT WALKS INTO A SPA ▸ Disabled by default ▸ ▸ ▸ List of paths, starting with classpath: or file: ▸ ▸ The path from which resources should be served micronaut.router.static-resources.*.enabled micronaut.router.static-resources.*.paths micronaut.router.static-resources.*.mapping
  • 55. STATIC ASSETS IN MICRONAUT A MICRONAUT WALKS INTO A SPA micronaut: router: static-resources: default: enabled: true mapping: "/**" paths: "classpath:public" local: enabled: true mapping: “/local/**" paths: “file:path/to/files“ APPLICATION.YML
  • 56. COMBINED BUILD A MICRONAUT WALKS INTO A SPA //Copy production SPA resources into server build directory task copyClientResources(dependsOn: ':client:build') { group = 'build' description = 'Copy client resources into server' } copyClientResources.doFirst { copy { from project(':client').buildDir.absolutePath into "${project(':server').buildDir}/resources/main/public" } } //Build a single executable JAR with embedded SPA resources task assembleServerAndClient( dependsOn: ['copyClientResources', ':server:shadowJar']) { group = 'build' description = 'Build combined server & client JAR' } BUILD.GRADLE
  • 57. COMBINED BUILD A MICRONAUT WALKS INTO A SPA ~ java -jar build/libs/server-all.jar Server Running: http://localhost:8080
  • 58. MICRONAUT SINGLE-PAGE-APP GUIDE A MICRONAUT WALKS INTO A SPA https://guides.micronaut.io/micronaut-spa-react/guide/index.htmlNEW!
  • 59. A MICRONAUT WALKS INTO A SPA MICRONAUT PETSTORE https://github.com/micronaut-projects/micronaut-examples/tree/master/petstore
  • 60. A MICRONAUT WALKS INTO A SPA LEARN MORE ABOUT OCI EVENTS AND TRAINING Events: ‣ objectcomputing.com/events Training: ‣ objectcomputing.com/training ‣ grailstraining.com ‣ micronauttraining.com COME SEE US AT OUR BOOTH!
  • 61. A MICRONAUT WALKS INTO A SPA THANK YOU@ZacharyAKlein kleinz@objectcomputing.com