I present here a microservice-oriented version of the previous project https://github.com/dubersfeld/sharewood-boot-oauth2. It uses Zuul as a reverse proxy and all servers run in Docker containers.
Here are the prerequisites for running the complete application:
Any Linux platform (I use Ubuntu 16.04)
A recent Apache Maven version installed (I use 3.3.9)
A recent Docker version installed (I use 17.12.1-ce)
In addition I used Spring Tool Suite for developing this demo but it is not required for running the application.
The complete application is comprised of an OAuth2 authorization server and an OAuth2 resource server. The resource server is hidden behind a reverse proxy. The authorization server is not.
A separate Eureka server is used to register the resource server.
An additional user server is used to provide user authentication needed for Authorization Code Grant.
A separate Spring Cloud configuration server sets all deployment configuration properties for the three servers and the gateway that all have spring-cloud-config-client dependency. It fetches properties from the local file system, not from a GitHub repository. All configuration YAML files are stored in the subdirectory config-repo.
Three separate databases are used to persist Oauth2 tokens, photos and users.
The dependencies are summarized on figure below:
Project name | Application name | Port | Database | Routing |
---|---|---|---|---|
authorization-server | authorization | 8080 | sharewood_tokens | |
sharewood-resource | sharewood-server | 8081 | sharewood_tokens, sharewood_photos | sharewood |
sharewood-config | 8888 | |||
sharewood-gateway | zuul-service | 5555 | ||
user-server | users-service | 9090 | sharewood_users | |
eureka-service | 8761 |
Here are the steps to run the application.
In folder docker/photos run the script photosBuild.sh then run the script photosVolume.sh. It creates a volume named sharewood_photos_db. Run the command:
sudo docker exec -it photos_create /bin/bash
then enter:
mysql -u dbuser -p
on prompt enter password1234. Then the database sharewood_photos should be accessible inside the container:
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| sharewood_photos |
+--------------------+
2 rows in set (0.09 sec)
Exit from container and kill it with the command:
[sudo] docker rm -f photos_create
In folder docker/users run the script usersBuild.sh then run the script usersVolume.sh. It creates a volume named sharewood_users_db. Run the command:
sudo docker exec -it users_create /bin/bash
then enter:
mysql -u dbuser -p
on prompt enter password1234. Then the database sharewood_users should be accessible inside the container:
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| sharewood_users |
+--------------------+
2 rows in set (0.09 sec)
Exit from container and kill it with the command:
[sudo] docker rm -f users_create
In folder docker/tokens run the script tokensBuild.sh then run the script tokensVolume.sh. It creates a volume named sharewood_tokens_db. Run the command:
sudo docker exec -it tokens_create /bin/bash
then enter:
mysql -u dbuser -p
on prompt enter password1234. Then the database sharewood_tokens should be accessible inside the container:
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| sharewood_tokens |
+--------------------+
2 rows in set (0.09 sec)
Exit from container and kill it with the command:
[sudo] docker rm -f tokens_create
In addition create a blank volume named sharewood_photos_store with the command:
[sudo] docker volume create sharewood\_photos\_store
Note that all redirect URIs used by a registered client have to be explicitly declared. This happens in this line of the file sharewoodTokenDB.sql:
INSERT INTO oauth\_client_details (
client_id,
resource_ids,
client_secret,
scope,
authorized\_grant_types,
web\_server\_redirect_uri,
authorities,
access\_token_validity,
refresh\_token_validity,
additional_information,
autoapprove)
VALUES('Fleetwood', 'SHAREWOOD', '{bcrypt}$2a$10$xMgMQRCnrZ.vf/8WxyIwrOTiFKGJF72FYGxUjnSIWQMpFxFTtoU.2', 'READ,WRITE,DELETE', 'authorization\_code', 'http://localhost:8090/fleetwood/sharewood/photosMy,http://localhost:8090/fleetwood/sharewood/sharedPhotos,http://localhost:8090/fleetwood/sharewood/updatePhoto,http://localhost:8090/fleetwood/sharewood/deletePhoto,http://localhost:8090/fleetwood/sharewood/createPhotoMulti,http://localhost:8090/fleetwood/sharewood/getToken', 'ROLE_CLIENT', '520', null, '{}', null);
In each project directory:
- config-server
- eureka-service
- user-server
- authorization-server
- sharewood-resource
- sharewood-gateway
- fleetwood
run the Maven command:
[sudo] mvn clean package docker:build
This will create all the Spring Boot images except for sharewood resource that requires an additional step. In folder docker/sharewood run the script sharewoodBuild.sh. I creates a new image named sharewood/sharewood populated with some photo JPG files.
In directory docker run th command:
[sudo] docker-compose up
It will start all the servers in Docker containers.
In fleetwood directory run the command:
mvn spring-boot:run
to start the client.
The users server is populated with two users Alice and Carol who have the role USER. Their passwords are: Alice: o8p7e6r5a Carol: s1a2t3o4r
Now the user is presented the authentication page and approval page shown below. Note that the only port exposed is the proxy port 5555.
Note: it takes some time for the routes to be taken into account. The actual delay depends on the machine.
Once logged the user can execute all RESTful request after authenticating to the authorization server and granting to fleetwood the required scope. Note that all changes are persisted even if the servers are taken down for example by the command:
[sudo] docker-compose down
The most tricky part of this project was to force the correct redirection after a successful login to authentication-server. This is achieved by subclassing the beans AuthenticationSuccessHandler and ExceptionTranslationFilter in authentication-server.
Another point of interest is that the resource server connects to two different databases. This is achieved by creating a separate configuration class for each database and placing the relevant classes in two sepatrate packages.