This web application shows how to use Microprofile JWT to secure your microservices. It uses Apache TomEE as the default Microprofile implementation.
In order to work, this application requires an STS capable of generating signed JWT tokens. We use Tribestream API Gateway as it’s a complete Security Token Service. It’s also capable of doing routing and while enforcing permissions. If you want to know more, check out the website at https://tribestream.io
To start the Tribestream API Gateway on a terminal execute the following command:
mvn tag:run
The following message indicates the Tribestream API Gateway is up and running:
[INFO] Tribestream started http://localhost:8080/tag/
You can login now using on a browser the following URL: http://localhost:8080/tag/ using admin
for both the username and password.
On a new terminal execute the following command:
mvn clean install -DskipTests tomee:run
The following message indicates the application is up and running:
08-Mar-2019 11:34:07.760 INFO [main] sun.reflect.NativeMethodAccessorImpl.invoke Starting ProtocolHandler ["http-nio-8181"] 08-Mar-2019 11:34:07.765 INFO [main] sun.reflect.NativeMethodAccessorImpl.invoke Starting ProtocolHandler ["ajp-nio-8010"] 08-Mar-2019 11:34:07.767 INFO [main] sun.reflect.NativeMethodAccessorImpl.invoke Server startup in 6786 ms
You can login now using on a browser the following URL: http://localhost:8080/movies
You can test any of the following accounts:
- Account alex
with password password
with create, update and delete role.
- Account john
with password password
with just comments add role.
For deeper exaplanation of Microprofile JWT using this application access: https://tribestream.io/guide/en/api-gateway/microservice-security-microprofile-jwt/current/
For your project to sign a request you might need only one interceptor or header inject.
@Injectable()
export class AuthHeaderInterceptor implements HttpInterceptor {
constructor(private $auth: AuthService) { }
intercept(req: RetryHttpRequest, next: HttpHandler): Observable<any> {
return next.handle(req.clone({
headers:
req.headers.set(this.$auth.authHeader,
`${this.$auth.authPrefix} ${this.$auth.authToken}`)
}));
}
}
For this example we added three interceptors that are injected into angular 8 project into root module providers.
import { HeaderInterceptors } from './services/header.interceptor';
...
@NgModule({
declarations: [
...
],
imports: [
...
],
providers: [
...
HeaderInterceptors
],
bootstrap: [...]
})
export class AppModule { }
First interceptor NoAuthHeaderInterceptor adds $$noAuth
property to a request for any request that should not be signed/authorised.
@Injectable()
export class NoAuthHeaderInterceptor implements HttpInterceptor {
constructor(private $auth: AuthService) { }
intercept(req: RetryHttpRequest, next: HttpHandler): Observable<any> {
if (...) {
req.$$noAuth = true;
return next.handle(req);
}
return next.handle(req);
}
}
Second interceptor OauthHeaderInterceptor adds Authorization header to a request.
@Injectable()
export class OauthHeaderInterceptor implements HttpInterceptor {
constructor(private $auth: AuthService) { }
signRequest(req) {
return req.clone({
headers:
req.headers.set(this.$auth.authHeader,
`${this.$auth.authPrefix} ${this.$auth.authToken}`)
});
}
intercept(req: RetryHttpRequest, next: HttpHandler): Observable<any> {
if (req.$$noAuth) {
return next.handle(req);
}
return next.handle(this.signRequest(req));
}
}
Third interceptor RetryHeaderInterceptor checks for 401 errors and handles refresh/retry of request.
@Injectable()
export class RetryHeaderInterceptor implements HttpInterceptor {
constructor(private $auth: AuthService) { }
intercept(req: RetryHttpRequest, next: HttpHandler): Observable<any> {
if (req.$$retry) {
req.$$retry = false;
return next.handle(req);
}
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
...
if (error.status === 401) {
return this.$auth.refresh()
.pipe(
filter(auth => !!auth),
switchMap((auth) => {
const headers = req.headers
.set(this.$auth.authHeader,
`${this.$auth.authPrefix} ${this.$auth.authToken}`);
const clone = req.clone({ headers }) as RetryHttpRequest;
clone.$$retry = true;
clone.$$noAuth = true;
return next.handle(clone);
})
);
} else {
...
}
})
);
}
}