Skip to content

Commit a94a457

Browse files
committed
[WFLY-17649] Adds a detailed quickstart for bearer authentication use cases
1 parent a74b54b commit a94a457

File tree

20 files changed

+1659
-0
lines changed

20 files changed

+1659
-0
lines changed

ejb-security-jwt/README.adoc

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
include::../shared-doc/attributes.adoc[]
2+
3+
= ejb-security-jwt: EJB Security with bearer token authentication and authorization
4+
:author: Lin Gao
5+
:level: Advanced
6+
:technologies: EJB, Security, Bearer, OIDC, JWT
7+
:requires-multiple-servers: true
8+
9+
[abstract]
10+
The `ejb-security-jwt` quickstart uses EJB and `OAUTHBEARER` SASL mechanism to demonstrate how to access a secured EJB deployed to {productNameFull} from a remote Java client application, and how an EJB deployed in one {productNameFull} calls a secured EJB deployed in another {productNameFull} using the same authentication context.
11+
12+
:standalone-server-type: default
13+
:archiveType: ear
14+
:oidcIdp: KeyCloak
15+
:oidcIdpLink: https://www.keycloak.org/
16+
:serverNameBase: {jbossHomeName}
17+
18+
== What is it?
19+
20+
The `ejb-security-jwt` quickstart shows how to access a remote secured EJB from a remote Java client application. It demonstrates the use of EJB and `OAUTHBEARER` SASL mechanism in {productNameFull}. It uses {oidcIdpLink}[{oidcIdp}] as the OIDC Identity Provider(IDP) with a predefined realm setup for this quickstart.
21+
22+
This example consists of the following Maven projects, each with a shared parent:
23+
24+
[cols="40%,60%",options="headers"]
25+
|===
26+
|Project |Description
27+
28+
a|`app-one`
29+
a|An `EAR` application that can be called by the `client`. It shows current caller principal and if it has role of `user` and `admin`, it can also call the EJB deployed in a separate server from `app-two` using the same authentication context.
30+
31+
[[ejba]] We can call the EJB in `app-one` as `EJBA` in this document, it will be deployed into {productName}_1 server.
32+
33+
a|`app-two`
34+
a|An `EJB` application that shows current caller principal and if it has role of `user` and `admin`.
35+
36+
[[ejbb]] We can call the EJB in `app-two` as `EJBB` in this document, it will be deployed into {productName}_2 server.
37+
38+
a|`client`
39+
|This project builds the standalone client and executes it.
40+
|===
41+
42+
The root `pom.xml` builds each of the subprojects in an appropriate order.
43+
44+
The server configuration is done using CLI batch scripts located in the root of this quickstart folder.
45+
46+
// System Requirements
47+
include::../shared-doc/system-requirements.adoc[leveloffset=+1]
48+
// Use of {jbossHomeName}
49+
include::../shared-doc/use-of-jboss-home-name.adoc[leveloffset=+1]
50+
51+
== Start A {oidcIdp} Server With Predefined Realm
52+
53+
This quickstart needs an OIDC IDP from which to get the bearer token from. We use {oidcIdp} for this quickstart.
54+
55+
{oidcIdp} supports starting a Docker container with a predefined realm setup
56+
57+
.Start {oidcIdp} Server with realm setup using `keycloak/realm/realm-import.json`:
58+
[source, subs="+quotes,attributes+"]
59+
----
60+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt
61+
$ docker run --rm -p 8180:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -v $(pwd)/keycloak/realm:/opt/keycloak/data/import quay.io/keycloak/keycloak:21.0.0 start-dev --import-realm
62+
----
63+
64+
This predefined realm has the following configuration:
65+
66+
A realm called `jwt-realm` is created, there is a client called `app` created under this realm, the secret for this client is `secret`, and there are 2 users created:
67+
68+
[cols="30%,30%,40%",options="headers"]
69+
|===
70+
|UserName | Password | Realm Roles
71+
|quickstartUser |quickstartPwd1! | user
72+
|admin |admin | user, admin
73+
|===
74+
75+
You can also set up the realm manually, please refer to {oidcIdpLink} for detail.
76+
77+
== Build the Project
78+
. Navigate to the quickstart directory to build the project
79+
+
80+
[source, subs="+quotes,attributes+", options="nowrap"]
81+
----
82+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt
83+
----
84+
85+
. Build the project
86+
+
87+
[source,options="nowrap"]
88+
----
89+
$ mvn clean install
90+
----
91+
92+
== Demonstrations
93+
94+
There are 2 parts in this quickstart, the first part is showing how the remote EJB client calls EJB deployed in {productName}_1 server using `OAUTHBEARER` SASL mechanism, the second one is showing how the remote EJB client calls EJB deployed in {productName}_1 server which in turn invokes EJB deployed in {productName}_2 server with the authentication context propagated.
95+
96+
Let's start the first part.
97+
98+
=== Remote EJB Client calls EJB using `OAUTHBEARER` mechanism
99+
100+
:jbossHomeName: {serverNameBase}_1
101+
[#start-server-1]
102+
include::../shared-doc/start-the-standalone-server.adoc[leveloffset=+3]
103+
104+
:hostController: --controller=localhost:9990
105+
include::./configure_server.adoc[]
106+
107+
==== Deploy EJB in `app-one` to {productName}_1 Server
108+
The EJB `JWTSecurityEJBRemoteA` in `app-one` has one method declared:
109+
110+
[source, java]
111+
----
112+
String securityInfo(boolean recursive);
113+
----
114+
which will return a String including the caller principal and the authorization check result if it has role of `user` and `admin`.
115+
116+
Open a terminal, and use the following command to deploy EJB in `app-one` module to {productName}_1 Server:
117+
118+
[source, bash, subs="+quotes,attributes+"]
119+
----
120+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt/app-one/ejb/
121+
$ mvn wildfly:deploy
122+
----
123+
124+
==== Configure and run EJB client application
125+
126+
The remote EJB client application will invoke link:#ejba[EJBA] twice. The first invocation reads the authentication configuration from `META-INF/wildfly-config.xml` where the `quickstartUser` and it's password is specified to get the bearer token from a specified token endpoint url. The second invocation uses programmatic authentication switching to use `admin` user. The only difference between 2 invocations is that they are using different user, thus differnt roles.
127+
128+
It has a system property called `recursive` in the client to decide if link:#ejba[EJBA] should call link:#ejbb[EJBB], we won't call EJBB in the first part, so we leave it as the default value: `false`.
129+
130+
Open a terminal, run the following command to run the remote EJB client application:
131+
132+
[source, bash]
133+
----
134+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt/client
135+
$ mvn exec:exec
136+
----
137+
138+
==== Investigate the Console Output
139+
140+
When the client application runs, it performs the following steps:
141+
142+
. Obtains a stateless session bean instance.
143+
. Sends method invocations to the stateless bean to get current security information from server side.
144+
145+
The following output is displayed in the terminal window:
146+
147+
[source, bash]
148+
----
149+
* * * * * * * * * * * recursive: false * * * * * * * * * * * * * * * * * * *
150+
151+
Security Info in JWTSecurityEJBA:
152+
Caller: [quickstartuser]
153+
quickstartuser has user role: (true)
154+
quickstartuser has admin role: (false)
155+
156+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
157+
158+
* * * * * * * Below are invoked using admin account * * * * * *
159+
160+
* * * * * * * * * * * recursive: false * * * * * * * * * * * * * * * * * * *
161+
162+
Security Info in JWTSecurityEJBA:
163+
Caller: [admin]
164+
admin has user role: (true)
165+
admin has admin role: (true)
166+
167+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
168+
169+
----
170+
171+
We can see that the user `quickstartUser` has the `user` role, but does not have `admin` role, the user `admin` has both roles. It can be confirmed in the {oidcIdp} server setup.
172+
173+
174+
Now let's jump to the second part.
175+
176+
=== Propagate the authentication context using `OAUTHBEARER` mechanism for EJB calls EJB.
177+
178+
In this part, we will demonstrate how the link:#ejba[EJBA] calls link:#ejbb[EJBB], and how to configure the {productName}_1 to propagate the authentication context from remote client using `OAUTHBEARER` SASL mechanism.
179+
180+
==== Configure remote outbound connection in {productName}_1 Server
181+
We need to create a `remote-outbound-connection` in remoting subsystem of {productName}_1 server with the authentication context specified in elytron subsystem to propagate.
182+
183+
Open a terminal, and run the following command:
184+
185+
[source,subs="+quotes,attributes+",options="nowrap"]
186+
----
187+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt
188+
$ __${jbossHomeName}__/bin/jboss-cli.sh -c --file=configure-ejb-outbound-connection.cli
189+
----
190+
191+
You will see the following configuration in standalone.xml of {productName}_1 server:
192+
193+
[source, xml]
194+
----
195+
<!-- in elytron subsystem -->
196+
<authentication-client>
197+
<authentication-configuration name="ejb-outbound-configuration" security-domain="jwt-domain" sasl-mecha
198+
nism-selector="OAUTHBEARER"/>
199+
<authentication-context name="ejb-outbound-context">
200+
<match-rule authentication-configuration="ejb-outbound-configuration"/>
201+
</authentication-context>
202+
</authentication-client>
203+
204+
<!-- in remoting subsystem -->
205+
<outbound-connections>
206+
<remote-outbound-connection name="ejb-outbound-connection" outbound-socket-binding-ref="ejb-outbound" authentication-context="ejb-outbound-context"/>
207+
</outbound-connections>
208+
209+
<!-- in socket-binding-group -->
210+
<outbound-socket-binding name="ejb-outbound">
211+
<remote-destination host="localhost" port="8280"/>
212+
</outbound-socket-binding>
213+
----
214+
215+
==== Redeploy EJB in `app-one` to {productName}_1 Server
216+
In the first part, we deployed EJB `jar` to {productName}_1 server for the demonstration, to be able to make link:#ejba[EJBA] calls link:#ejbb[EJBB], we need to package the EJBs in `EAR` to have ejb client dependency of EJBB inside.
217+
218+
Open a terminal, and run the following command to undeploy the link:#ejba[EJBA] in jar archive, and deploy it again using ear archive:
219+
220+
[source, bash, subs="+quotes,attributes+"]
221+
----
222+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt/app-one/ejb
223+
$ mvn wildfly:undeploy
224+
$ cd ../ear
225+
$ mvn wildfly:deploy
226+
----
227+
228+
There is a file `META-INF/jbos-ejb-client.xml` in the `EAR` deployment, which specifies the outbound-connection reference to `ejb-outbound-connection`, it matches what was defined above, and it points to the HTTP port: `8280` which is used by the {productName}_2 server.
229+
230+
==== Starts {productName}_2 server
231+
232+
:jbossHomeName: {serverNameBase}_2
233+
Now it's time to start {productName}_2 server. Open a terminal, and like what you did to start link:#start-server-1[{productName}_1 server], you need to specify a port offset to avoid port conflicts: `-Djboss.socket.binding.port-offset=200`, this makes the HTTP port opened by {productName}_2 server becomes `8280`:
234+
235+
.Example of starting {productName}_2 server in Linux system:
236+
[source,subs="+quotes,attributes+"]
237+
----
238+
$ __{jbossHomeName}__/bin/standalone.sh -Djboss.socket.binding.port-offset=200
239+
----
240+
241+
:hostController: --controller=localhost:10190
242+
include::./configure_server.adoc[]
243+
244+
==== Deploy EJB in `app-two` to {productName}_2 Server
245+
The EJB `JWTSecurityEJBRemoteB` in `app-two` has one method declared:
246+
247+
[source, java]
248+
----
249+
String securityInfo();
250+
----
251+
which will return a String including the caller principal and the authorization check result if it has role of `user` and `admin`.
252+
253+
Open a terminal, and use the following command to deploy EJB in `app-two` module to {productName}_2 Server:
254+
255+
[source, bash, subs="+quotes,attributes+"]
256+
----
257+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt/app-two
258+
$ mvn wildfly:deploy -Dwildfly.port=10190
259+
----
260+
261+
==== Configure and run EJB client application
262+
Now let's run the remote client again, we will specify `-Drecursive=true` to let EJBA calls EJBB this time.
263+
264+
Open a terminal, and run the following command:
265+
266+
[source, bash]
267+
----
268+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt/client
269+
$ mvn exec:exec -Drecursive=true
270+
----
271+
272+
273+
==== Investigate the Console Output
274+
The following output is displayed in the terminal window:
275+
276+
[source, bash]
277+
----
278+
* * * * * * * * * * * recursive: true * * * * * * * * * * * * * * * * * * *
279+
280+
Security Info in JWTSecurityEJBA:
281+
Caller: [quickstartuser]
282+
quickstartuser has user role: (true)
283+
quickstartuser has admin role: (false)
284+
285+
=========== Below are invocation from remote EJB in app-two ===========
286+
Security Info in JWTSecurityEJBB:
287+
Caller: [quickstartuser]
288+
quickstartuser has user role: (true)
289+
quickstartuser has admin role: (false)
290+
291+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
292+
293+
* * * * * * * Below are invoked using admin account * * * * * *
294+
295+
* * * * * * * * * * * recursive: true * * * * * * * * * * * * * * * * * * *
296+
297+
Security Info in JWTSecurityEJBA:
298+
Caller: [admin]
299+
admin has user role: (true)
300+
admin has admin role: (true)
301+
302+
=========== Below are invocation from remote EJB in app-two ===========
303+
Security Info in JWTSecurityEJBB:
304+
Caller: [admin]
305+
admin has user role: (true)
306+
admin has admin role: (true)
307+
308+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
309+
----
310+
311+
We can see that the user `quickstartUser` has the `user` role, but does not have `admin` role, the user `admin` has both roles. It can be confirmed in the {oidcIdp} server setup.
312+
313+
We can also see that the invocation from EJBA to EJBB uses the same authentication context as what is used in remote client calls EJBA.
314+
315+
== Undeploy the Archives
316+
317+
To undeploy the components from the {productName} servers:
318+
319+
. Navigate to the `app-one/ear` subdirectory:
320+
+
321+
[source,options="nowrap"]
322+
----
323+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt/app-one/ear/
324+
$ mvn wildfly:undeploy
325+
----
326+
327+
. Navigate to the `app-two` subdirectory:
328+
+
329+
[source,options="nowrap"]
330+
----
331+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt/app-two
332+
$ mvn wildfly:undeploy -Dwildfly.port=10190
333+
----
334+
335+
== Restore the servers
336+
After un-deployed the archives from both servers, you can restore the server configurations:
337+
338+
[source,subs="+quotes,attributes+",options="nowrap"]
339+
----
340+
$ cd ${QUICKSTART_HOME}/ejb-security-jwt
341+
$ __${serverNameBase}_1__/bin/jboss-cli.sh -c --file=restore-configuration.cli
342+
$ __${serverNameBase}_2__/bin/jboss-cli.sh -c {hostController} --file=restore-configuration.cli
343+
----
344+

0 commit comments

Comments
 (0)