Skip to content

Commit 9a3eb07

Browse files
committed
Add Resource Server XML snippets
Fixes gh-8077
1 parent 3903ac4 commit 9a3eb07

File tree

1 file changed

+238
-6
lines changed

1 file changed

+238
-6
lines changed

docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-resourceserver.adoc

+238-6
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,17 @@ Methods on the `oauth2ResourceServer` DSL will also override or replace auto con
208208

209209
For example, the second `@Bean` Spring Boot creates is a `JwtDecoder`, which decodes `String` tokens into validated instances of `Jwt`:
210210

211+
212+
.JWT Decoder
213+
====
211214
[source,java]
212215
----
213216
@Bean
214217
public JwtDecoder jwtDecoder() {
215218
return JwtDecoders.fromIssuerLocation(issuerUri);
216219
}
217220
----
221+
====
218222

219223
[NOTE]
220224
Calling `{security-api-url}org/springframework/security/oauth2/jwt/JwtDecoders.html#fromIssuerLocation-java.lang.String-[JwtDecoders#fromIssuerLocation]` is what invokes the Provider Configuration or Authorization Server Metadata endpoint in order to derive the JWK Set Uri.
@@ -223,6 +227,39 @@ If the application doesn't expose a `JwtDecoder` bean, then Spring Boot will exp
223227

224228
And its configuration can be overridden using `jwkSetUri()` or replaced using `decoder()`.
225229

230+
Or, if you're not using Spring Boot at all, then both of these components - the filter chain and a `JwtDecoder` can be specified in XML.
231+
232+
The filter chain is specified like so:
233+
234+
.Default JWT Configuration
235+
====
236+
.Xml
237+
[source,xml,role="primary"]
238+
----
239+
<http>
240+
<intercept-uri pattern="/**" access="authenticated"/>
241+
<oauth2-resource-server>
242+
<jwt decoder-ref="jwtDecoder"/>
243+
</oauth2-resource-server>
244+
</http>
245+
----
246+
====
247+
248+
And the `JwtDecoder` like so:
249+
250+
.JWT Decoder
251+
====
252+
.Xml
253+
[source,xml,role="primary"]
254+
----
255+
<bean id="jwtDecoder"
256+
class="org.springframework.security.oauth2.jwt.JwtDecoders"
257+
factory-method="fromIssuerLocation">
258+
<constructor-arg value="${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}"/>
259+
</bean>
260+
----
261+
====
262+
226263
[[oauth2resourceserver-jwt-jwkseturi-dsl]]
227264
==== Using `jwkSetUri()`
228265

@@ -268,6 +305,17 @@ class DirectlyConfiguredJwkSetUri : WebSecurityConfigurerAdapter() {
268305
}
269306
}
270307
----
308+
309+
.Xml
310+
[source,xml,role="secondary"]
311+
----
312+
<http>
313+
<intercept-uri pattern="/**" access="authenticated"/>
314+
<oauth2-resource-server>
315+
<jwt jwk-set-uri="https://idp.example.com/.well-known/jwks.json"/>
316+
</oauth2-resource-server>
317+
</http>
318+
----
271319
====
272320

273321
Using `jwkSetUri()` takes precedence over any configuration property.
@@ -317,6 +365,17 @@ class DirectlyConfiguredJwtDecoder : WebSecurityConfigurerAdapter() {
317365
}
318366
}
319367
----
368+
369+
.Xml
370+
[source,xml,role="secondary"]
371+
----
372+
<http>
373+
<intercept-uri pattern="/**" access="authenticated"/>
374+
<oauth2-resource-server>
375+
<jwt decoder-ref="myCustomDecoder"/>
376+
</oauth2-resource-server>
377+
</http>
378+
----
320379
====
321380

322381
This is handy when deeper configuration, like <<oauth2resourceserver-jwt-validation,validation>>, <<oauth2resourceserver-jwt-claimsetmapping,mapping>>, or <<oauth2resourceserver-jwt-timeouts,request timeouts>>, is necessary.
@@ -541,6 +600,18 @@ class DirectlyConfiguredJwkSetUri : WebSecurityConfigurerAdapter() {
541600
}
542601
}
543602
----
603+
604+
.Xml
605+
[source,xml,role="secondary"]
606+
----
607+
<http>
608+
<intercept-uri pattern="/contacts/**" access="hasAuthority('SCOPE_contacts')"/>
609+
<intercept-uri pattern="/messages/**" access="hasAuthority('SCOPE_messages')"/>
610+
<oauth2-resource-server>
611+
<jwt jwk-set-uri="https://idp.example.org/.well-known/jwks.json"/>
612+
</oauth2-resource-server>
613+
</http>
614+
----
544615
====
545616

546617
Or similarly with method security:
@@ -616,6 +687,26 @@ class DirectlyConfiguredJwkSetUri : WebSecurityConfigurerAdapter() {
616687
}
617688
}
618689
----
690+
691+
.Xml
692+
[source,xml,role="secondary"]
693+
----
694+
<http>
695+
<intercept-uri pattern="/contacts/**" access="hasAuthority('SCOPE_contacts')"/>
696+
<intercept-uri pattern="/messages/**" access="hasAuthority('SCOPE_messages')"/>
697+
<oauth2-resource-server>
698+
<jwt jwk-set-uri="https://idp.example.org/.well-known/jwks.json"
699+
jwt-authentication-converter-ref="grantedAuthoritiesExtractor"/>
700+
</oauth2-resource-server>
701+
</http>
702+
703+
<bean id="grantedAuthoritiesExtractor"
704+
class="org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter">
705+
<property name="jwtGrantedAuthoritiesConverter">
706+
<bean class="my.custom.GrantedAuthoritiesConverter"/>
707+
</property>
708+
</bean>
709+
----
619710
====
620711

621712
which is responsible for converting a `Jwt` into an `Authentication`.
@@ -1070,6 +1161,40 @@ If the application doesn't expose a `OpaqueTokenIntrospector` bean, then Spring
10701161

10711162
And its configuration can be overridden using `introspectionUri()` and `introspectionClientCredentials()` or replaced using `introspector()`.
10721163

1164+
Or, if you're not using Spring Boot at all, then both of these components - the filter chain and a `OpaqueTokenIntrospector` can be specified in XML.
1165+
1166+
The filter chain is specified like so:
1167+
1168+
.Default Opaque Token Configuration
1169+
====
1170+
.Xml
1171+
[source,xml,role="primary"]
1172+
----
1173+
<http>
1174+
<intercept-uri pattern="/**" access="authenticated"/>
1175+
<oauth2-resource-server>
1176+
<opaque-token introspector-ref="opaqueTokenIntrospector"/>
1177+
</oauth2-resource-server>
1178+
</http>
1179+
----
1180+
====
1181+
1182+
And the `OpaqueTokenIntrospector` like so:
1183+
1184+
.Opaque Token Introspector
1185+
====
1186+
.Xml
1187+
[source,xml,role="primary"]
1188+
----
1189+
<bean id="opaqueTokenIntrospector"
1190+
class="org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector">
1191+
<constructor-arg value="${spring.security.oauth2.resourceserver.opaquetoken.introspection_uri}"/>
1192+
<constructor-arg value="${spring.security.oauth2.resourceserver.opaquetoken.client_id}"/>
1193+
<constructor-arg value="${spring.security.oauth2.resourceserver.opaquetoken.client_secret}"/>
1194+
</bean>
1195+
----
1196+
====
1197+
10731198
[[oauth2resourceserver-opaque-introspectionuri-dsl]]
10741199
==== Using `introspectionUri()`
10751200

@@ -1117,6 +1242,17 @@ class DirectlyConfiguredIntrospectionUri : WebSecurityConfigurerAdapter() {
11171242
}
11181243
}
11191244
----
1245+
1246+
.Xml
1247+
[source,xml,role="secondary"]
1248+
----
1249+
<bean id="opaqueTokenIntrospector"
1250+
class="org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector">
1251+
<constructor-arg value="https://idp.example.com/introspect"/>
1252+
<constructor-arg value="client"/>
1253+
<constructor-arg value="secret"/>
1254+
</bean>
1255+
----
11201256
====
11211257

11221258
Using `introspectionUri()` takes precedence over any configuration property.
@@ -1166,6 +1302,17 @@ class DirectlyConfiguredIntrospector : WebSecurityConfigurerAdapter() {
11661302
}
11671303
}
11681304
----
1305+
1306+
.Xml
1307+
[source,xml,role="secondary"]
1308+
----
1309+
<http>
1310+
<intercept-uri pattern="/**" access="authenticated"/>
1311+
<oauth2-resource-server>
1312+
<opaque-token introspector-ref="myCustomIntrospector"/>
1313+
</oauth2-resource-server>
1314+
</http>
1315+
----
11691316
====
11701317

11711318
This is handy when deeper configuration, like <<oauth2resourceserver-opaque-authorization-extraction,authority mapping>>, <<oauth2resourceserver-opaque-jwt-introspector,JWT revocation>>, or <<oauth2resourceserver-opaque-timeouts,request timeouts>>, is necessary.
@@ -1194,7 +1341,11 @@ When this is the case, Resource Server will attempt to coerce these scopes into
11941341

11951342
This means that to protect an endpoint or method with a scope derived from an Opaque Token, the corresponding expressions should include this prefix:
11961343

1197-
```java
1344+
.Authorization Opaque Token Configuration
1345+
====
1346+
.Java
1347+
[source,java,role="primary"]
1348+
----
11981349
@EnableWebSecurity
11991350
public class MappedAuthorities extends WebSecurityConfigurerAdapter {
12001351
protected void configure(HttpSecurity http) {
@@ -1207,7 +1358,20 @@ public class MappedAuthorities extends WebSecurityConfigurerAdapter {
12071358
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
12081359
}
12091360
}
1210-
```
1361+
----
1362+
1363+
.Xml
1364+
[source,xml,role="secondary"]
1365+
----
1366+
<http>
1367+
<intercept-uri pattern="/contacts/**" access="hasAuthority('SCOPE_contacts')"/>
1368+
<intercept-uri pattern="/messages/**" access="hasAuthority('SCOPE_messages')"/>
1369+
<oauth2-resource-server>
1370+
<opaque-token introspector-ref="opaqueTokenIntrospector"/>
1371+
</oauth2-resource-server>
1372+
</http>
1373+
----
1374+
====
12111375

12121376
Or similarly with method security:
12131377

@@ -1450,7 +1614,10 @@ AuthenticationManagerResolver<HttpServletRequest> tokenAuthenticationManagerReso
14501614

14511615
And then specify this `AuthenticationManagerResolver` in the DSL:
14521616

1453-
[source,java]
1617+
.Authentication Manager Resolver
1618+
====
1619+
.Java
1620+
[source,java,role="primary"]
14541621
----
14551622
http
14561623
.authorizeRequests(authorize -> authorize
@@ -1461,6 +1628,15 @@ http
14611628
);
14621629
----
14631630
1631+
.Xml
1632+
[source,xml,role="secondary"]
1633+
----
1634+
<http>
1635+
<oauth2-resource-server authentication-manager-resolver-ref="tokenAuthenticationManagerResolver"/>
1636+
</http>
1637+
----
1638+
====
1639+
14641640
[[oauth2resourceserver-multitenancy]]
14651641
=== Multi-tenancy
14661642

@@ -1478,7 +1654,10 @@ In each case, there are two things that need to be done and trade-offs associate
14781654

14791655
One way to differentiate tenants is by the issuer claim. Since the issuer claim accompanies signed JWTs, this can be done with the `JwtIssuerAuthenticationManagerResolver`, like so:
14801656

1481-
[source,java]
1657+
.Multitenancy Tenant by JWT Claim
1658+
====
1659+
.Java
1660+
[source,java,role="primary"]
14821661
----
14831662
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver
14841663
("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo");
@@ -1492,6 +1671,25 @@ http
14921671
);
14931672
----
14941673
1674+
.Xml
1675+
[source,xml,role="secondary"]
1676+
----
1677+
<http>
1678+
<oauth2-resource-server authentication-manager-resolver-ref="authenticationManagerResolver"/>
1679+
</http>
1680+
1681+
<bean id="authenticationManagerResolver"
1682+
class="org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver">
1683+
<constructor-arg>
1684+
<list>
1685+
<value>https://idp.example.org/issuerOne</value>
1686+
<value>https://idp.example.org/issuerTwo</value>
1687+
</list>
1688+
</constructor-arg>
1689+
</bean>
1690+
----
1691+
====
1692+
14951693
This is nice because the issuer endpoints are loaded lazily.
14961694
In fact, the corresponding `JwtAuthenticationProvider` is instantiated only when the first request with the corresponding issuer is sent.
14971695
This allows for an application startup that is independent from those authorization servers being up and available.
@@ -1667,19 +1865,39 @@ This, however, can be customized in a couple of ways.
16671865
For example, you may have a need to read the bearer token from a custom header.
16681866
To achieve this, you can wire a `HeaderBearerTokenResolver` instance into the DSL, as you can see in the following example:
16691867

1670-
[source,java]
1868+
.Custom Bearer Token Header
1869+
====
1870+
.Java
1871+
[source,java,role="primary"]
16711872
----
16721873
http
16731874
.oauth2ResourceServer(oauth2 -> oauth2
16741875
.bearerTokenResolver(new HeaderBearerTokenResolver("x-goog-iap-jwt-assertion"))
16751876
);
16761877
----
16771878
1879+
.Xml
1880+
[source,xml,role="secondary"]
1881+
----
1882+
<http>
1883+
<oauth2-resource-server bearer-token-resolver-ref="bearerTokenResolver"/>
1884+
</http>
1885+
1886+
<bean id="bearerTokenResolver"
1887+
class="org.springframework.security.oauth2.server.resource.web.HeaderBearerTokenResolver">
1888+
<constructor-arg value="x-goog-iap-jwt-assertion"/>
1889+
</bean>
1890+
----
1891+
====
1892+
16781893
==== Reading the Bearer Token from a Form Parameter
16791894

16801895
Or, you may wish to read the token from a form parameter, which you can do by configuring the `DefaultBearerTokenResolver`, as you can see below:
16811896

1682-
[source,java]
1897+
.Form Parameter Bearer Token
1898+
====
1899+
.Java
1900+
[source,java,role="primary"]
16831901
----
16841902
DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
16851903
resolver.setAllowFormEncodedBodyParameter(true);
@@ -1689,6 +1907,20 @@ http
16891907
);
16901908
----
16911909
1910+
.Xml
1911+
[source,xml,role="secondary"]
1912+
----
1913+
<http>
1914+
<oauth2-resource-server bearer-token-resolver-ref="bearerTokenResolver"/>
1915+
</http>
1916+
1917+
<bean id="bearerTokenResolver"
1918+
class="org.springframework.security.oauth2.server.resource.web.HeaderBearerTokenResolver">
1919+
<property name="allowFormEncodedBodyParameter" value="true"/>
1920+
</bean>
1921+
----
1922+
====
1923+
16921924
=== Bearer Token Propagation
16931925

16941926
Now that you're in possession of a bearer token, it might be handy to pass that to downstream services.

0 commit comments

Comments
 (0)