Merge branch '5.8.x' into 6.0.x

Closes gh-13406
This commit is contained in:
Rob Winch
2023-06-18 21:33:58 -05:00
116 changed files with 4826 additions and 3206 deletions

View File

@@ -30,8 +30,10 @@ In Spring Security 5.8, this support has been refreshed to use the `Authorizatio
To configure authorization using Java Configuration, simply include the `@EnableWebSocketSecurity` annotation and publish an `AuthorizationManager<Message<?>>` bean or in XML use the `use-authorization-manager` attribute.
One way to do this is by using the `AuthorizationManagerMessageMatcherRegistry` to specify endpoint patterns like so:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -48,7 +50,8 @@ public class WebSocketSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -61,29 +64,26 @@ open class WebSocketSecurityConfig { // <1> <2>
}
}
----
======
<1> Any inbound CONNECT message requires a valid CSRF token to enforce the <<websocket-sameorigin,Same Origin Policy>>.
<2> The `SecurityContextHolder` is populated with the user within the `simpUser` header attribute for any inbound request.
<3> Our messages require the proper authorization. Specifically, any inbound message that starts with `/user/` will requires `ROLE_USER`. You can find additional details on authorization in <<websocket-authorization>>
====
Spring Security also provides xref:servlet/appendix/namespace/websocket.adoc#nsa-websocket-security[XML Namespace] support for securing WebSockets.
A comparable XML based configuration looks like the following:
====
[source,xml]
----
<websocket-message-broker use-authorization-manager="true"> <!--1--> <!--2-->
<intercept-message pattern="/user/**" access="authenticated"/> <!--3-->
</websocket-message-broker>
----
====
This will ensure that:
<1> Any inbound CONNECT message requires a valid CSRF token to enforce <<websocket-sameorigin,Same Origin Policy>>
<2> The SecurityContextHolder is populated with the user within the simpUser header attribute for any inbound request.
<3> Our messages require the proper authorization. Specifically, any inbound message that starts with "/user/" will require ROLE_USER. Additional details on authorization can be found in <<websocket-authorization>>
====
=== Custom Authorization
@@ -91,8 +91,10 @@ This will ensure that:
When using `AuthorizationManager`, customization is quite simple.
For example, you can publish an `AuthorizationManager` that requires that all messages have a role of "USER" using `AuthorityAuthorizationManager`, as seen below:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -106,7 +108,8 @@ public class WebSocketSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -119,19 +122,22 @@ open class WebSocketSecurityConfig {
}
----
.Xml
Xml::
+
[source,xml,role="secondary"]
----
<bean id="authorizationManager" class="org.example.MyAuthorizationManager"/>
<websocket-message-broker authorization-manager-ref="myAuthorizationManager"/>
----
====
======
There are several ways to further match messages, as can be seen in a more advanced example below:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -152,7 +158,8 @@ public class WebSocketSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -171,7 +178,8 @@ open class WebSocketSecurityConfig {
}
----
.Xml
Xml::
+
[source,kotlin,role="secondary"]
----
<websocket-message-broker use-authorization-manager="true">
@@ -194,7 +202,7 @@ open class WebSocketSecurityConfig {
<intercept-message pattern="/**" access="denyAll" /> <!--6-->
</websocket-message-broker>
----
====
======
This will ensure that:
@@ -296,19 +304,19 @@ Instead, we must include the token in the Stomp headers.
Applications can xref:servlet/exploits/csrf.adoc#servlet-csrf-include[obtain a CSRF token] by accessing the request attribute named `_csrf`.
For example, the following allows accessing the `CsrfToken` in a JSP:
====
[source,javascript]
----
var headerName = "${_csrf.headerName}";
var token = "${_csrf.token}";
----
====
If you use static HTML, you can expose the `CsrfToken` on a REST endpoint.
For example, the following would expose the `CsrfToken` on the `/csrf` URL:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@RestController
@@ -321,7 +329,8 @@ public class CsrfController {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@RestController
@@ -332,13 +341,12 @@ class CsrfController {
}
}
----
====
======
The JavaScript can make a REST call to the endpoint and use the response to populate the `headerName` and the token.
We can now include the token in our Stomp client:
====
[source,javascript]
----
...
@@ -349,7 +357,6 @@ stompClient.connect(headers, function(frame) {
})
----
====
[[websocket-sameorigin-disable]]
=== Disable CSRF within WebSockets
@@ -357,8 +364,10 @@ NOTE: At this point, CSRF is not configurable when using `@EnableWebSocketSecuri
To disable CSRF, instead of using `@EnableWebSocketSecurity`, you can use XML support or add the Spring Security components yourself, like so:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -380,7 +389,8 @@ public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -401,20 +411,23 @@ open class WebSocketSecurityConfig : WebSocketMessageBrokerConfigurer {
}
----
.Xml
Xml::
+
[source,xml,role="secondary"]
----
<websocket-message-broker use-authorization-manager="true" same-origin-disabled="true">
<intercept-message pattern="/**" access="authenticated"/>
</websocket-message-broker>
----
====
======
On the other hand, if you are using the <<legacy-websocket-configuration,legacy `AbstractSecurityWebSocketMessageBrokerConfigurer`>> and you want to allow other domains to access your site, you can disable Spring Security's protection.
For example, in Java Configuration you can use the following:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -429,7 +442,8 @@ public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBro
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -442,7 +456,7 @@ open class WebSocketSecurityConfig : AbstractSecurityWebSocketMessageBrokerConfi
}
}
----
====
======
[[websocket-expression-handler]]
=== Custom Expression Handler
@@ -494,7 +508,6 @@ To allow SockJS frame-based transports to work, we need to configure Spring Secu
You can customize `X-Frame-Options` with the xref:servlet/appendix/namespace/http.adoc#nsa-frame-options[frame-options] element.
For example, the following instructs Spring Security to use `X-Frame-Options: SAMEORIGIN`, which allows iframes within the same domain:
====
[source,xml]
----
<http>
@@ -506,12 +519,13 @@ For example, the following instructs Spring Security to use `X-Frame-Options: SA
</headers>
</http>
----
====
Similarly, you can customize frame options to use the same origin within Java Configuration by using the following:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -532,7 +546,8 @@ public class WebSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -552,7 +567,7 @@ open class WebSecurityConfig {
}
}
----
====
======
[[websocket-sockjs-csrf]]
=== SockJS & Relaxing CSRF
@@ -571,8 +586,10 @@ We can easily achieve this by providing a CSRF `RequestMatcher`.
Our Java configuration makes this easy.
For example, if our stomp endpoint is `/chat`, we can disable CSRF protection only for URLs that start with `/chat/` by using the following configuration:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -600,7 +617,8 @@ public class WebSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -625,11 +643,10 @@ open class WebSecurityConfig {
}
}
----
====
======
If we use XML-based configuration, we can use thexref:servlet/appendix/namespace/http.adoc#nsa-csrf-request-matcher-ref[csrf@request-matcher-ref].
====
[source,xml]
----
<http ...>
@@ -661,8 +678,10 @@ If we use XML-based configuration, we can use thexref:servlet/appendix/namespace
Before Spring Security 5.8, the way to configure messaging authorization using Java Configuration, was to extend the `AbstractSecurityWebSocketMessageBrokerConfigurer` and configure the `MessageSecurityMetadataSourceRegistry`.
For example:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -676,7 +695,8 @@ public class WebSocketSecurityConfig
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -686,7 +706,7 @@ open class WebSocketSecurityConfig : AbstractSecurityWebSocketMessageBrokerConfi
}
}
----
====
======
This will ensure that: