Skip to content

Commit 31a1486

Browse files
committed
Add Message Security Preparation Steps
Issue gh-11337
1 parent 5721b03 commit 31a1486

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed

docs/modules/ROOT/pages/migration.adoc

+267
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,273 @@ companion object {
212212
`@EnableMethodSecurity` and `<method-security>` activate stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
213213
If after moving to either you see ``AnnotationConfigurationException``s in your logs, follow the instructions in the exception message to clean up your application's method security annotation usage.
214214

215+
=== Use `AuthorizationManager` for Message Security
216+
217+
xref:servlet/integrations/websocket.adoc[Message Security] has been xref:servlet/integrations/websocket.adoc#websocket-configuration[improved] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API] and direct use of Spring AOP.
218+
219+
==== Ensure all messages have defined authorization rules
220+
221+
The now-deprecated {security-api-url}org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurer.html[message security support] permits all messages by default.
222+
xref:servlet/integrations/websocket.adoc[The new support] has the stronger default of denying all messages.
223+
224+
To prepare for this, ensure that authorization rules exist are declared for every request.
225+
226+
For example, an application configuration like:
227+
228+
====
229+
.Java
230+
[source,java,role="primary"]
231+
----
232+
@Override
233+
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
234+
messages
235+
.simpDestMatchers("/user/queue/errors").permitAll()
236+
.simpDestMatchers("/admin/**").hasRole("ADMIN");
237+
}
238+
----
239+
240+
.Kotlin
241+
[source,kotlin,role="secondary"]
242+
----
243+
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
244+
messages
245+
.simpDestMatchers("/user/queue/errors").permitAll()
246+
.simpDestMatchers("/admin/**").hasRole("ADMIN")
247+
}
248+
----
249+
250+
.Xml
251+
[source,xml,role="secondary"]
252+
----
253+
<websocket-message-broker>
254+
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
255+
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
256+
</websocket-message-broker>
257+
----
258+
====
259+
260+
should change to:
261+
262+
====
263+
.Java
264+
[source,java,role="primary"]
265+
----
266+
@Override
267+
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
268+
messages
269+
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
270+
.simpDestMatchers("/user/queue/errors").permitAll()
271+
.simpDestMatchers("/admin/**").hasRole("ADMIN")
272+
.anyMessage().denyAll();
273+
}
274+
----
275+
276+
.Kotlin
277+
[source,kotlin,role="secondary"]
278+
----
279+
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
280+
messages
281+
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
282+
.simpDestMatchers("/user/queue/errors").permitAll()
283+
.simpDestMatchers("/admin/**").hasRole("ADMIN")
284+
.anyMessage().denyAll()
285+
}
286+
----
287+
288+
.Xml
289+
[source,xml,role="secondary"]
290+
----
291+
<websocket-message-broker>
292+
<intercept-message type="CONNECT" access="permitAll"/>
293+
<intercept-message type="DISCONNECT" access="permitAll"/>
294+
<intercept-message type="UNSUBSCRIBE" access="permitAll"/>
295+
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
296+
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
297+
<intercept-message pattern="/**" access="denyAll"/>
298+
</websocket-message-broker>
299+
----
300+
====
301+
302+
==== Add `@EnableWebSocketSecurity`
303+
304+
[NOTE]
305+
====
306+
If you want to have CSRF disabled and you are using Java configuration, the migration steps are slightly different.
307+
Instead of using `@EnableWebSocketSecurity`, you will override the appropriate methods in `WebSocketMessageBrokerConfigurer` yourself.
308+
Please see xref:servlet/integrations/websocket.adoc#websocket-sameorigin-disable[the reference manual] for details about this step.
309+
====
310+
311+
If you are using Java Configuration, add {security-api-url}org/springframework/security/config/annotation/web/socket/EnableWebSocketSecurity.html[`@EnableWebSocketSecurity`] to your application.
312+
313+
For example, you can add it to your websocket security configuration class, like so:
314+
315+
====
316+
.Java
317+
[source,java,role="primary"]
318+
----
319+
@EnableWebSocketSecurity
320+
@Configuration
321+
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
322+
// ...
323+
}
324+
----
325+
326+
.Kotlin
327+
[source,kotlin,role="secondary"]
328+
----
329+
@EnableWebSocketSecurity
330+
@Configuration
331+
class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
332+
// ...
333+
}
334+
----
335+
====
336+
337+
This will make a prototype instance of `MessageMatcherDelegatingAuthorizationManager.Builder` available to encourage configuration by composition instead of extension.
338+
339+
==== Use an `AuthorizationManager<Message<?>>` instance
340+
341+
To start using `AuthorizationManager`, you can set the `use-authorization-manager` attribute in XML or you can publish an `AuthorizationManager<Message<?>>` `@Bean` in Java.
342+
343+
For example, the following application configuration:
344+
345+
====
346+
.Java
347+
[source,java,role="primary"]
348+
----
349+
@Override
350+
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
351+
messages
352+
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
353+
.simpDestMatchers("/user/queue/errors").permitAll()
354+
.simpDestMatchers("/admin/**").hasRole("ADMIN")
355+
.anyMessage().denyAll();
356+
}
357+
----
358+
359+
.Kotlin
360+
[source,kotlin,role="secondary"]
361+
----
362+
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
363+
messages
364+
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
365+
.simpDestMatchers("/user/queue/errors").permitAll()
366+
.simpDestMatchers("/admin/**").hasRole("ADMIN")
367+
.anyMessage().denyAll()
368+
}
369+
----
370+
371+
.Xml
372+
[source,xml,role="secondary"]
373+
----
374+
<websocket-message-broker>
375+
<intercept-message type="CONNECT" access="permitAll"/>
376+
<intercept-message type="DISCONNECT" access="permitAll"/>
377+
<intercept-message type="UNSUBSCRIBE" access="permitAll"/>
378+
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
379+
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
380+
<intercept-message pattern="/**" access="denyAll"/>
381+
</websocket-message-broker>
382+
----
383+
====
384+
385+
changes to:
386+
387+
====
388+
.Java
389+
[source,java,role="primary"]
390+
----
391+
@Bean
392+
AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
393+
messages
394+
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
395+
.simpDestMatchers("/user/queue/errors").permitAll()
396+
.simpDestMatchers("/admin/**").hasRole("ADMIN")
397+
.anyMessage().denyAll();
398+
return messages.build();
399+
}
400+
----
401+
402+
.Kotlin
403+
[source,kotlin,role="secondary"]
404+
----
405+
@Bean
406+
fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
407+
messages
408+
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
409+
.simpDestMatchers("/user/queue/errors").permitAll()
410+
.simpDestMatchers("/admin/**").hasRole("ADMIN")
411+
.anyMessage().denyAll()
412+
return messages.build()
413+
}
414+
----
415+
416+
.Xml
417+
[source,xml,role="secondary"]
418+
----
419+
<websocket-message-broker use-authorization-manager="true">
420+
<intercept-message type="CONNECT" access="permitAll"/>
421+
<intercept-message type="DISCONNECT" access="permitAll"/>
422+
<intercept-message type="UNSUBSCRIBE" access="permitAll"/>
423+
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
424+
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
425+
<intercept-message pattern="/**" access="denyAll"/>
426+
</websocket-message-broker>
427+
----
428+
====
429+
430+
==== Stop Implementing `AbstractSecurityWebSocketMessageBrokerConfigurer`
431+
432+
If you are using Java configuration, you can now simply extend `WebSocketMessageBrokerConfigurer`.
433+
434+
For example, if your class that extends `AbstractSecurityWebSocketMessageBrokerConfigurer` is called `WebSocketSecurityConfig`, then:
435+
436+
====
437+
.Java
438+
[source,java,role="primary"]
439+
----
440+
@EnableWebSocketSecurity
441+
@Configuration
442+
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
443+
// ...
444+
}
445+
----
446+
447+
.Kotlin
448+
[source,kotlin,role="secondary"]
449+
----
450+
@EnableWebSocketSecurity
451+
@Configuration
452+
class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
453+
// ...
454+
}
455+
----
456+
====
457+
458+
changes to:
459+
460+
====
461+
.Java
462+
[source,java,role="primary"]
463+
----
464+
@EnableWebSocketSecurity
465+
@Configuration
466+
public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
467+
// ...
468+
}
469+
----
470+
471+
.Kotlin
472+
[source,kotlin,role="secondary"]
473+
----
474+
@EnableWebSocketSecurity
475+
@Configuration
476+
class WebSocketSecurityConfig: WebSocketMessageBrokerConfigurer {
477+
// ...
478+
}
479+
----
480+
====
481+
215482
== Reactive
216483

217484
=== Use `AuthorizationManager` for Method Security

0 commit comments

Comments
 (0)