Implementing JAX-RS-security via Basic-auth

31 October 2017

Basic-auth is the simplest and weakest protection you can add to your resources in a Java EE application. This post shows how to leverage it for JAX-RS-resources that are accessed by a plain HTML5/JavaScript app.

Additionally, I had the following requirements:

  • The JAX-RS-resource is requested from a prue JavaScript-based webapp via the fetch-API; I want to leverage the authentication-dialog from the browser within the webapp (no custom dialog as the webapp should stay as simple as possible and use as much as possible the standard offered by the browser).

  • But I don’t want the whole WAR (i.e. JavaScript app) to be protected. Just the request to the JAX-RS-endpoint should be protected via Basic-auth

  • At the server-side I want to be able to connect to my own/custom identity-store; i.e. I want to programatically check the username/password myself. In other words: I don’t want the application-server’s internal identity-stores/authentication.

Protecting the JAX-RS-endpoint at server-side is as simple as implementing a request-filter. I could have used a low-level servlet-filter, but instead decided to use the JAX-RS-specific equivalent:

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

@Provider
public class SecurityFilter implements ContainerRequestFilter {

	@Override
	public void filter(ContainerRequestContext requestContext) throws IOException {
		String authHeader = requestContext.getHeaderString("Authorization");
		if (authHeader == null || !authHeader.startsWith("Basic")) {
			requestContext.abortWith(Response.status(401).header("WWW-Authenticate", "Basic").build());
			return;
		}

		String[] tokens = (new String(Base64.getDecoder().decode(authHeader.split(" ")[1]), "UTF-8")).split(":");
		final String username = tokens[0];
		final String password = tokens[1];

		if (username.equals("daniel") && password.equals("123")) {
			// all good
		}
		else {
			requestContext.abortWith(Response.status(401).build());
			return;
		}
	}

}

If the Authorization header is not present, we request the authentication-dialog from the browser by sending the header WWW-Authenticate=Basic. If i directly open up the JAX-RS-resource in the browser, I get the uthentication-dialog from the browser and can access the resource (if I provide the correct username and password).

Now the question is if this also works when the JAX-RS-resource if fetched via the JavaScript fetch-API. I tried this:

function handleResponse(response) {
	if (response.status == "401") {
		alert("not authorized!")
	} else {
		response.json().then(function(data) {
			console.log(data)
		});
	}
}

fetch("http://localhost:8080/service/resources/health").then(handleResponse);

It did not work; I was getting 401 from the server because the browser was not sending the "Authorization" header; but the browser also did not show the authentication-dialog.

A peak into the spec hinted that it should work:

  1. If request’s use-URL-credentials flag is unset or authentication-fetch flag is set, then run these subsubsteps: …​

  2. Let username and password be the result of prompting the end user for a username and password, respectively, in request’s window.

So, i added the credentials to the fetch:

fetch("http://localhost:8080/service/resources/health", {credentials: 'same-origin'}).then(handleResponse);

It worked. The browser shows the authentication-dialog after the first 401. In subsequent request to the JAX-RS-resouce, the "Authorization" header is always sent along. No need to reenter every time (Chrome discards it as soon as the browser window is closed).

The only disadvantage I found so far is from a development-perspective. I usually run the JAX-RS-endpoint seperately from my Javascript app; i.e. the JAX-RS-endpoint is hosted as a WAR in the application-server but the JavaScript-app is hosted via LiveReload or browser-sync. In this case, the JAX-RS-service and the webapp do not have the same origin (different port) and I have to use the CORS-header Access-Control-Allow-Origin=* to allow communication between the two. But with this header set, the Authorization-token collected by the JavaScript-app will not be shared with the JAX-RS-endpoint.

Github - Switch to fork

05 October 2017

Say you just have cloned a massive github repository (like Netbeans) where cloning already takes minutes and now decide to contribute. You will fork the repo and than clone the fork and spend another X minutes waiting?

This sometimes seems like to much of an effort. And thankfully, there are steps how you can transform the already cloned repo to use your fork.

  1. Fork the repo

  2. Rename origin to upstream (your fork will be origin)

    git remote rename origin upstream
  3. Set origin as your fork

    git remote add origin git@github...my-fork
  4. Fetch origin

    git fetch origin
  5. Make master track new origin/master

    git checkout -B master --track origin/master

Websphere Administration via JMX, JConsole and JVisualVM

25 September 2017

How to connect to the Websphere-specific MBean server to configure the environment and monitor the applications?

Start JConsole with the following script:

#!/bin/bash

# Change me!
export HOST=swpsws16
# This is ORB_LISTENER_ADDRESS
export IIOP_PORT=9811

export WAS_HOME=/home/daniel/IBM/WebSphere/AppServer

export PROVIDER=-Djava.naming.provider.url=corbaname:iiop:$HOST:$IIOP_PORT

export CLASSPATH=
export CLASSPATH=$CLASSPATH:$WAS_HOME/java/lib/tools.jar
export CLASSPATH=$CLASSPATH:$WAS_HOME/runtimes/com.ibm.ws.admin.client_8.5.0.jar
export CLASSPATH=$CLASSPATH:$WAS_HOME/runtimes/com.ibm.ws.ejb.thinclient_8.5.0.jar
export CLASSPATH=$CLASSPATH:$WAS_HOME/runtimes/com.ibm.ws.orb_8.5.0.jar
export CLASSPATH=$CLASSPATH:$WAS_HOME/java/lib/jconsole.jar

export URL=service:jmx:iiop://$HOST:$IIOP_PORT/jndi/JMXConnector

$WAS_HOME/java/bin/java -classpath $CLASSPATH $PROVIDER sun.tools.jconsole.JConsole $URL

Even nicer: Install VisualWAS plugin for JVisualVM.

  • Use "Add JMX Connection"

  • Use Connection-Type "Websphere"

  • For port, use SOAP_CONNECTOR_ADDRESS (default 8880)

Websphere and JVisualVM

25 September 2017

How to inspect a Websphere server via JVisualVM?

Go to "Application servers > SERVER-NAME > Java and Process management > Process Defintion > Java Virtual Machine > Generic JVM arguments" and add the following JMV settings:

-Djavax.management.builder.initial= \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.local.only=false \
-Dcom.sun.management.jmxremote.port=1099 \
-Djava.rmi.server.hostname=10.226.2.64

Providing an external ip or hostname was important for it to work.

Select "Add JMX Connection" in JVisualVM and enter: 10.226.2.64:1099.

Jenkins in Docker using Docker

23 September 2017

Say you want to run your Jenkins itself in docker. But the Jenkins build-jobs also uses docker!?

Either you have to install docker in docker, or you let the Jenkins docker-client access the host’s docker-daemon.

Steps

  1. Map the unix socket into the Jenkins container:

    -v /var/run/docker.sock:/var/run/docker.sock
  2. But the jenkins user will not have permissions to access the socket by default. So, first check the GID of the group that owns the socket:

    getent group dockerroot
  3. Now create a group (name is irrelevant; lets name it "docker") in the Jenkins container with the same GID and assign the jenkins user to it:

    sudo groupadd -g 982 docker
    sudo usermod -aG docker jenkins

ES6 with Nashorn in JDK9

14 June 2017

JDK9 is planning to incrementally support the ES6 features of JavaScript. In the current early-access builds (tested with 9-ea+170), major features like classes are not supported yet; but keywords like let/const, arrow functions and string-interpolation already work:

#!jjs --language=es6
"use strict";

let hello = (from, to) => print(`Hello from ${from} to ${to}`);

if ($EXEC('uname -n')) {
    let hostname = $OUT.trim();
    hello(hostname, 'daniel');
}

For details on what’s included by now, read JEP 292.

AWS ECS: Push a docker container

28 May 2017

Steps to deploy docker containers to AWS EC2:

  1. Created a docker-repository with the name de.dplatz/abc, you will get a page with all the steps and coordinates for docker login, docker tag and docker push.

  2. From CLI run:

    aws ecr get-login --region eu-central-1
    docker tag de.dplatz/abc:latest <my-aws-url>/de.dplatz/abc:latest
    docker push <my-aws-url>/de.dplatz/abc:latest

See here for starting the container.

JDK9 HttpClient

20 May 2017

Required some clarification from the JDK team how to access the new HttpClient API (which actually is incubating now):

$ ./jdk-9_168/bin/jshell --add-modules jdk.incubator.httpclient
|  Welcome to JShell -- Version 9-ea
|  For an introduction type: /help intro

jshell> import jdk.incubator.http.*;

jshell> import static jdk.incubator.http.HttpResponse.BodyHandler.*;

jshell> URI uri = new URI("http://openjdk.java.net/projects/jigsaw/");
uri ==> http://openjdk.java.net/projects/jigsaw/

jshell> HttpRequest request = HttpRequest.newBuilder(uri).build();
request ==> http://openjdk.java.net/projects/jigsaw/ GET

jshell> HttpResponse response = HttpClient.newBuilder().build().send(request, discard(null));
response ==> jdk.incubator.http.HttpResponseImpl@133814f

jshell> response.statusCode();
$6 ==> 200

I really like the jshell-integration in Netbeans; unfortunately, it does not allow to set commandline-flags for the started shells yet. Filed an issue and got a workaround for now.

Websphere Liberty Admin Console

12 May 2017

$ bin/installUtility install adminCenter-1.0
server.xml
<!-- Enable features -->
<featureManager>
    <!-- ... -->
    <feature>adminCenter-1.0</feature>
</featureManager>

<keyStore id="defaultKeyStore" password="admin123" />

<basicRegistry id="basic" realm="BasicRealm">
    <user name="admin" password="admin123" />
</basicRegistry>
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://localhost:9090/adminCenter/

Docker JVM Memory Settings

01 May 2017

Read this, this and this.

  • JDK9 has -XX:+UseCGroupMemoryLimitForHeap

  • JDK8 pre 131: Always specify -Xmx1024m and -XX:MaxMetaspaceSize

  • JDK8 since 131: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap

Docker Rest API

01 May 2017

SSL keys are at /cygdrive/c/Users/<username>/.docker/machine/machines/default

 curl --insecure -v --cert cert.pem --key key.pem -X GET https://192.168.99.100:2376/images/json

strace

01 May 2017

strace -fopen,read,close,fstat java -jar Test.jar

Stacktrace in Eclipse Debugger

12 April 2017

How to see the stacktrace for an exception-variable within the eclipse debugger?

Go to Preferences / Java / Debug / Detail Formatter; Add for Throwable:

java.io.Writer stackTrace = new java.io.StringWriter();
java.io.PrintWriter printWriter = new java.io.PrintWriter(stackTrace);
printStackTrace(printWriter);
return getMessage() + "\n" + stackTrace;

Java debug-flags

22 March 2017

-Xdebug
// shared-memory (windows only)
-agentlib:jdwp=transport=dt_shmem,address=eclipse,server=y,suspend=n
// socket
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999

inotifywait

07 March 2017

Monitor filesystem-changes:

while inotifywait -qr /dir/to/monitor; do
    rsync -avz /dir/to/monitor/ /dir/to/sync/to
done

List classes in Jar

29 January 2017

List all classes in a jar-file:

$ unzip -l MyJar.jar "*.class" | tail -n+4 | head -n-2 | tr -s ' ' | cut -d ' ' -f5 | tr / . | sed 's/\.class$//'

rsync tricks

20 January 2017

This command removes files that have been removed from the source directory but will not overwrite newer files in the destination:

$ rsync -avu --delete sourcedir/ /cygwin/e/destdir/

To rsync to another system with ssh over the net:

$ rsync -avu --delete -e ssh sourcedir/ username@machine:~/destdir/

Shell Alias-Expansion

17 January 2017

Say, you have defined an alias:

$ alias gg='git log --oneline --decorate --graph'

But when typing 'gg' wouldn’t it be nice to expand the alias so you can make a small modification to the args?

$ gg<Ctrl+Alt+e>

Say, you want to easily clear the screen; there is a shortcut Ctrl+L. But maybe you also always want to print the contents of the current directory: you can rebind the shortcut:

$ bind -x '"\C-l": clear; ls -l'

Java Version Strings

16 January 2017

For what JDK version is a class compiled?

$ javap -verbose MyClass.class | grep "major"
  • Java 5: major version 49

  • Java 6: major version 50

  • Java 7: major version 51

  • Java 8: major version 52

SSH Keys

13 January 2017

To connect to a remote-host without password-entry (for scripting):

# generate ssh keys for local (if not already done)
$ ssh-keygen
$ ssh-copy-id -i ~/.ssh/id_rsa.pub <remote-host>
$ ssh <remote-host>

Maven Fat & Thin Jar

12 January 2017

Building a fat and a thin jar in one go:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.4.3</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <shadedArtifactAttached>true</shadedArtifactAttached>
                <shadedClassifierName>all</shadedClassifierName>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.mycompany.myproduct.Main</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

Commandline HTTP-Server

10 January 2017

A very simple http-server:

while true ; do echo -e  "HTTP/1.1 200 OK\nAccess-Control-Allow-Origin: *\n\n $(cat index.html)" |  nc -l localhost 1500; done


Older posts are available in the archive.