Polish websocket
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
This commit is contained in:
committed by
Rob Winch
parent
c34cb108cb
commit
0f7a6d45fd
@@ -31,179 +31,31 @@ 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 xref:servlet/appendix/namespace/websocket.adoc#nsa-websocket-security[XML] use the `use-authorization-manager` attribute.
|
||||
One way to do this is by using the `AuthorizationManagerMessageMatcherRegistry` to specify endpoint patterns like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSocketSecurity // <1> <2>
|
||||
public class WebSocketSecurityConfig {
|
||||
include-code::./WebSocketSecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
@Bean
|
||||
AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
|
||||
messages
|
||||
.simpDestMatchers("/user/**").hasRole("USER"); // <3>
|
||||
|
||||
return messages.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSocketSecurity // <1> <2>
|
||||
open class WebSocketSecurityConfig { // <1> <2>
|
||||
@Bean
|
||||
fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {
|
||||
messages.simpDestMatchers("/user/**").hasRole("USER") // <3>
|
||||
return messages.build()
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<websocket-message-broker use-authorization-manager="true"> <1> <2>
|
||||
<intercept-message pattern="/user/**" access="hasRole('USER')"/> <3>
|
||||
</websocket-message-broker>
|
||||
----
|
||||
======
|
||||
<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 require `ROLE_USER`. You can find additional details on authorization in <<websocket-authorization>>
|
||||
|
||||
[[custom-authorization]]
|
||||
=== Custom Authorization
|
||||
|
||||
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:
|
||||
For example, you can publish an `AuthorizationManager` that requires that all messages have a role of `"USER"` using `AuthorityAuthorizationManager`, as seen below:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSocketSecurity // <1> <2>
|
||||
public class WebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
|
||||
return AuthorityAuthorizationManager.hasRole("USER");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSocketSecurity // <1> <2>
|
||||
open class WebSocketSecurityConfig {
|
||||
@Bean
|
||||
fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {
|
||||
return AuthorityAuthorizationManager.hasRole("USER") // <3>
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<bean id="authorizationManager" class="org.example.MyAuthorizationManager"/>
|
||||
|
||||
<websocket-message-broker authorization-manager-ref="myAuthorizationManager"/>
|
||||
----
|
||||
======
|
||||
include-code::./WebSocketSecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
There are several ways to further match messages, as can be seen in a more advanced example below:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
public class WebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
|
||||
messages
|
||||
.nullDestMatcher().authenticated() // <1>
|
||||
.simpSubscribeDestMatchers("/user/queue/errors").permitAll() // <2>
|
||||
.simpDestMatchers("/app/**").hasRole("USER") // <3>
|
||||
.simpSubscribeDestMatchers("/user/**", "/topic/friends/*").hasRole("USER") // <4>
|
||||
.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll() // <5>
|
||||
.anyMessage().denyAll(); // <6>
|
||||
|
||||
return messages.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
open class WebSocketSecurityConfig {
|
||||
fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {
|
||||
messages
|
||||
.nullDestMatcher().authenticated() // <1>
|
||||
.simpSubscribeDestMatchers("/user/queue/errors").permitAll() // <2>
|
||||
.simpDestMatchers("/app/**").hasRole("USER") // <3>
|
||||
.simpSubscribeDestMatchers("/user/**", "/topic/friends/*").hasRole("USER") // <4>
|
||||
.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll() // <5>
|
||||
.anyMessage().denyAll() // <6>
|
||||
|
||||
return messages.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
<websocket-message-broker use-authorization-manager="true">
|
||||
<!--1-->
|
||||
<intercept-message type="CONNECT" access="permitAll" />
|
||||
<intercept-message type="UNSUBSCRIBE" access="permitAll" />
|
||||
<intercept-message type="DISCONNECT" access="permitAll" />
|
||||
|
||||
<intercept-message pattern="/user/queue/errors" type="SUBSCRIBE" access="permitAll" /> <!--2-->
|
||||
<intercept-message pattern="/app/**" access="hasRole('USER')" /> <!--3-->
|
||||
|
||||
<!--4-->
|
||||
<intercept-message pattern="/user/**" type="SUBSCRIBE" access="hasRole('USER')" />
|
||||
<intercept-message pattern="/topic/friends/*" type="SUBSCRIBE" access="hasRole('USER')" />
|
||||
|
||||
<!--5-->
|
||||
<intercept-message type="MESSAGE" access="denyAll" />
|
||||
<intercept-message type="SUBSCRIBE" access="denyAll" />
|
||||
|
||||
<intercept-message pattern="/**" access="denyAll" /> <!--6-->
|
||||
</websocket-message-broker>
|
||||
----
|
||||
======
|
||||
include-code::./AdvancedWebSocketSecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
This will ensure that:
|
||||
|
||||
<1> Any message without a destination (i.e. anything other than Message type of MESSAGE or SUBSCRIBE) will require the user to be authenticated
|
||||
<2> Anyone can subscribe to /user/queue/errors
|
||||
<3> Any message that has a destination starting with "/app/" will be require the user to have the role ROLE_USER
|
||||
<4> Any message that starts with "/user/" or "/topic/friends/" that is of type SUBSCRIBE will require ROLE_USER
|
||||
<5> Any other message of type MESSAGE or SUBSCRIBE is rejected. Due to 6 we do not need this step, but it illustrates how one can match on specific message types.
|
||||
<1> Any message without a destination (i.e. anything other than Message type of `MESSAGE` or `SUBSCRIBE`) will require the user to be authenticated
|
||||
<2> Anyone can subscribe to `/user/queue/errors`
|
||||
<3> Any message that has a destination starting with `"/app/"` will be required the user to have the role `ROLE_USER`
|
||||
<4> Any message that starts with `"/user/"` or `"/topic/friends/"` that is of type SUBSCRIBE will require `ROLE_USER`
|
||||
<5> Any other message of type `MESSAGE` or `SUBSCRIBE` is rejected. Due to 6 we do not need this step, but it illustrates how one can match on specific message types.
|
||||
<6> Any other Message is rejected. This is a good idea to ensure that you do not miss any messages.
|
||||
|
||||
[[migrating-spel-expressions]]
|
||||
@@ -217,46 +69,7 @@ However, to ease migration, you can use
|
||||
|
||||
And specify an instance for each matcher that you cannot yet migrate:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
public class WebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
|
||||
messages
|
||||
// ...
|
||||
.simpSubscribeDestMatchers("/topic/friends/{friend}")
|
||||
.access(new MessageExpressionAuthorizationManager("#friend == 'john'"));
|
||||
// ...
|
||||
|
||||
return messages.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
open class WebSocketSecurityConfig {
|
||||
fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?> {
|
||||
messages
|
||||
// ..
|
||||
.simpSubscribeDestMatchers("/topic/friends/{friend}")
|
||||
.access(MessageExpressionAuthorizationManager("#friend == 'john'"))
|
||||
// ...
|
||||
|
||||
return messages.build()
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./WebSocketSecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
[[websocket-authorization-notes]]
|
||||
=== WebSocket Authorization Notes
|
||||
@@ -380,6 +193,7 @@ Kotlin::
|
||||
----
|
||||
@RestController
|
||||
class CsrfController {
|
||||
|
||||
@RequestMapping("/csrf")
|
||||
fun csrf(token: CsrfToken): CsrfToken {
|
||||
return token
|
||||
@@ -409,69 +223,7 @@ 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:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final AuthorizationManager<Message<?>> authorizationManager;
|
||||
|
||||
public WebSocketSecurityConfig(ApplicationContext applicationContext, AuthorizationManager<Message<?>> authorizationManager) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.authorizationManager = authorizationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
argumentResolvers.add(new AuthenticationPrincipalArgumentResolver());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
AuthorizationChannelInterceptor authz = new AuthorizationChannelInterceptor(authorizationManager);
|
||||
AuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(applicationContext);
|
||||
authz.setAuthorizationEventPublisher(publisher);
|
||||
registration.interceptors(new SecurityContextChannelInterceptor(), authz);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
open class WebSocketSecurityConfig(val applicationContext: ApplicationContext, val authorizationManager: AuthorizationManager<Message<*>>) : WebSocketMessageBrokerConfigurer {
|
||||
@Override
|
||||
override fun addArgumentResolvers(argumentResolvers: List<HandlerMethodArgumentResolver>) {
|
||||
argumentResolvers.add(AuthenticationPrincipalArgumentResolver())
|
||||
}
|
||||
|
||||
@Override
|
||||
override fun configureClientInboundChannel(registration: ChannelRegistration) {
|
||||
var authz: AuthorizationChannelInterceptor = AuthorizationChannelInterceptor(authorizationManager)
|
||||
var publisher: AuthorizationEventPublisher = SpringAuthorizationEventPublisher(applicationContext)
|
||||
authz.setAuthorizationEventPublisher(publisher)
|
||||
registration.interceptors(SecurityContextChannelInterceptor(), authz)
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<websocket-message-broker use-authorization-manager="true" same-origin-disabled="true">
|
||||
<intercept-message pattern="/**" access="authenticated"/>
|
||||
</websocket-message-broker>
|
||||
----
|
||||
======
|
||||
include-code::./WebSocketSecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
[[websocket-expression-handler]]
|
||||
=== Custom Expression Handler
|
||||
@@ -537,52 +289,7 @@ For example, the following instructs Spring Security to use `X-Frame-Options: SA
|
||||
|
||||
Similarly, you can customize frame options to use the same origin within Java Configuration by using the following:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers((headers) -> headers
|
||||
.frameOptions((frameOptions) -> frameOptions
|
||||
.sameOrigin()
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class WebSecurityConfig {
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
// ...
|
||||
headers {
|
||||
frameOptions {
|
||||
sameOrigin = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./WebSecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
[[websocket-sockjs-csrf]]
|
||||
=== SockJS & Relaxing CSRF
|
||||
@@ -601,91 +308,11 @@ 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:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig {
|
||||
include-code::./WebSecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf((csrf) -> csrf
|
||||
// ignore our stomp endpoints since they are protected using Stomp headers
|
||||
.ignoringRequestMatchers("/chat/**")
|
||||
)
|
||||
.headers((headers) -> headers
|
||||
// allow same origin to frame our site to support iframe SockJS
|
||||
.frameOptions((frameOptions) -> frameOptions
|
||||
.sameOrigin()
|
||||
)
|
||||
)
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
...
|
||||
);
|
||||
...
|
||||
}
|
||||
}
|
||||
----
|
||||
If we use XML-based configuration, we can use the xref:servlet/appendix/namespace/http.adoc#nsa-csrf-request-matcher-ref[csrf@request-matcher-ref].
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class WebSecurityConfig {
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
csrf {
|
||||
ignoringRequestMatchers("/chat/**")
|
||||
}
|
||||
headers {
|
||||
frameOptions {
|
||||
sameOrigin = true
|
||||
}
|
||||
}
|
||||
authorizeHttpRequests {
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
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 ...>
|
||||
<csrf request-matcher-ref="csrfMatcher"/>
|
||||
|
||||
<headers>
|
||||
<frame-options policy="SAMEORIGIN"/>
|
||||
</headers>
|
||||
|
||||
...
|
||||
</http>
|
||||
|
||||
<b:bean id="csrfMatcher"
|
||||
class="AndRequestMatcher">
|
||||
<b:constructor-arg value="#{T(org.springframework.security.web.csrf.CsrfFilter).DEFAULT_CSRF_MATCHER}"/>
|
||||
<b:constructor-arg>
|
||||
<b:bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
|
||||
<b:bean class="org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean">
|
||||
<b:constructor-arg value="/chat/**"/>
|
||||
</b:bean>
|
||||
</b:bean>
|
||||
</b:constructor-arg>
|
||||
</b:bean>
|
||||
----
|
||||
include-code::./WebSocketSecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
[[legacy-websocket-configuration]]
|
||||
== Legacy WebSocket Configuration
|
||||
|
||||
@@ -43,6 +43,7 @@ dependencies {
|
||||
testImplementation project(':spring-security-test')
|
||||
testImplementation project(':spring-security-oauth2-client')
|
||||
testImplementation project(':spring-security-oauth2-resource-server')
|
||||
testImplementation project(':spring-security-messaging')
|
||||
testImplementation 'com.squareup.okhttp3:mockwebserver'
|
||||
testImplementation libs.com.password4j.password4j
|
||||
testImplementation 'com.unboundid:unboundid-ldapsdk'
|
||||
@@ -51,6 +52,7 @@ dependencies {
|
||||
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||
testImplementation 'org.springframework:spring-core'
|
||||
testImplementation 'org.springframework:spring-test'
|
||||
testImplementation 'org.springframework:spring-websocket'
|
||||
|
||||
testImplementation 'org.springframework:spring-webmvc'
|
||||
testImplementation 'jakarta.servlet:jakarta.servlet-api'
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.integrations.customauthorization;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
|
||||
|
||||
import static org.springframework.messaging.simp.SimpMessageType.MESSAGE;
|
||||
import static org.springframework.messaging.simp.SimpMessageType.SUBSCRIBE;
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
public class AdvancedWebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
|
||||
messages
|
||||
.nullDestMatcher().authenticated() // <1>
|
||||
.simpSubscribeDestMatchers("/user/queue/errors").permitAll() // <2>
|
||||
.simpDestMatchers("/app/**").hasRole("USER") // <3>
|
||||
.simpSubscribeDestMatchers("/user/**", "/topic/friends/*").hasRole("USER") // <4>
|
||||
.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll() // <5>
|
||||
.anyMessage().denyAll(); // <6>
|
||||
|
||||
return messages.build();
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.integrations.customauthorization;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity;
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSocketSecurity
|
||||
public class WebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
|
||||
return AuthorityAuthorizationManager.hasRole("USER");
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.integrations.migratingspelexpressions;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.messaging.access.expression.MessageExpressionAuthorizationManager;
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
public class WebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
|
||||
messages
|
||||
// ...
|
||||
.simpSubscribeDestMatchers("/topic/friends/{friend}")
|
||||
.access(new MessageExpressionAuthorizationManager("#friend == 'john'"));
|
||||
// ...
|
||||
|
||||
return messages.build();
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.integrations.websocketauthorization;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity;
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSocketSecurity // <1> <2>
|
||||
public class WebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
|
||||
messages.simpDestMatchers("/user/**").hasRole("USER"); // <3>
|
||||
return messages.build();
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.integrations.websocketsameorigindisable;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||
import org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;
|
||||
import org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver;
|
||||
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final AuthorizationManager<Message<?>> authorizationManager;
|
||||
|
||||
public WebSocketSecurityConfig(ApplicationContext applicationContext, AuthorizationManager<Message<?>> authorizationManager) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.authorizationManager = authorizationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
argumentResolvers.add(new AuthenticationPrincipalArgumentResolver());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
AuthorizationChannelInterceptor authz = new AuthorizationChannelInterceptor(authorizationManager);
|
||||
AuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(applicationContext);
|
||||
authz.setAuthorizationEventPublisher(publisher);
|
||||
registration.interceptors(new SecurityContextChannelInterceptor(), authz);
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.integrations.websocketsockjscsrf;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf((csrf) -> csrf
|
||||
// ignore our stomp endpoints since they are protected using Stomp headers
|
||||
.ignoringRequestMatchers("/chat/**")
|
||||
)
|
||||
.headers((headers) -> headers
|
||||
// allow same origin to frame our site to support iframe SockJS
|
||||
.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
|
||||
)
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
/* @chomp:line // ... */.anyRequest().permitAll()
|
||||
)
|
||||
/* @chomp:line // ... */;
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.integrations.websocketsockjssameorigin;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers((headers) -> headers
|
||||
.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.integrations.customauthorization
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.security.authorization.AuthorizationManager
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager
|
||||
|
||||
import org.springframework.messaging.simp.SimpMessageType.MESSAGE
|
||||
import org.springframework.messaging.simp.SimpMessageType.SUBSCRIBE
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
open class AdvancedWebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
open fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {
|
||||
messages
|
||||
.nullDestMatcher().authenticated() // <1>
|
||||
.simpSubscribeDestMatchers("/user/queue/errors").permitAll() // <2>
|
||||
.simpDestMatchers("/app/**").hasRole("USER") // <3>
|
||||
.simpSubscribeDestMatchers("/user/**", "/topic/friends/*").hasRole("USER") // <4>
|
||||
.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll() // <5>
|
||||
.anyMessage().denyAll() // <6>
|
||||
|
||||
return messages.build()
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.integrations.customauthorization
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager
|
||||
import org.springframework.security.authorization.AuthorizationManager
|
||||
import org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSocketSecurity
|
||||
open class WebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
open fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {
|
||||
return AuthorityAuthorizationManager.hasRole("USER")
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.integrations.migratingspelexpressions
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.security.authorization.AuthorizationManager
|
||||
import org.springframework.security.messaging.access.expression.MessageExpressionAuthorizationManager
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
open class WebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
open fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {
|
||||
messages
|
||||
// ...
|
||||
.simpSubscribeDestMatchers("/topic/friends/{friend}")
|
||||
.access(MessageExpressionAuthorizationManager("#friend == 'john'"))
|
||||
// ...
|
||||
|
||||
return messages.build()
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.integrations.websocketauthorization
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.security.authorization.AuthorizationManager
|
||||
import org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSocketSecurity // <1> <2>
|
||||
open class WebSocketSecurityConfig {
|
||||
|
||||
@Bean
|
||||
open fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {
|
||||
messages.simpDestMatchers("/user/**").hasRole("USER") // <3>
|
||||
return messages.build()
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.integrations.websocketsameorigindisable
|
||||
|
||||
import org.springframework.context.ApplicationContext
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher
|
||||
import org.springframework.security.authorization.AuthorizationManager
|
||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher
|
||||
import org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor
|
||||
import org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver
|
||||
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
open class WebSocketSecurityConfig(val applicationContext: ApplicationContext, val authorizationManager: AuthorizationManager<Message<*>>): WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
override fun addArgumentResolvers(argumentResolvers: MutableList<HandlerMethodArgumentResolver>) {
|
||||
argumentResolvers.add(AuthenticationPrincipalArgumentResolver())
|
||||
}
|
||||
|
||||
@Override
|
||||
override fun configureClientInboundChannel(registration: ChannelRegistration) {
|
||||
val authz = AuthorizationChannelInterceptor(authorizationManager)
|
||||
val publisher: AuthorizationEventPublisher = SpringAuthorizationEventPublisher(applicationContext)
|
||||
authz.setAuthorizationEventPublisher(publisher)
|
||||
registration.interceptors(SecurityContextChannelInterceptor(), authz)
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.integrations.websocketsockjscsrf
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.invoke
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class WebSecurityConfig {
|
||||
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
csrf {
|
||||
// ignore our stomp endpoints since they are protected using Stomp headers
|
||||
ignoringRequestMatchers("/chat/**")
|
||||
}
|
||||
headers {
|
||||
frameOptions {
|
||||
// allow same origin to frame our site to support iframe SockJS
|
||||
sameOrigin = true
|
||||
}
|
||||
}
|
||||
authorizeHttpRequests {
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.integrations.websocketsockjssameorigin
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.invoke
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class WebSecurityConfig {
|
||||
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
// ...
|
||||
headers {
|
||||
frameOptions {
|
||||
sameOrigin = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026-present the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<!-- tag::snippet[] -->
|
||||
<websocket-message-broker use-authorization-manager="true">
|
||||
<!--1-->
|
||||
<intercept-message type="CONNECT" access="permitAll" />
|
||||
<intercept-message type="UNSUBSCRIBE" access="permitAll" />
|
||||
<intercept-message type="DISCONNECT" access="permitAll" />
|
||||
|
||||
<intercept-message pattern="/user/queue/errors" type="SUBSCRIBE" access="permitAll" /> <!--2-->
|
||||
<intercept-message pattern="/app/**" access="hasRole('USER')" /> <!--3-->
|
||||
|
||||
<!--4-->
|
||||
<intercept-message pattern="/user/**" type="SUBSCRIBE" access="hasRole('USER')" />
|
||||
<intercept-message pattern="/topic/friends/*" type="SUBSCRIBE" access="hasRole('USER')" />
|
||||
|
||||
<!--5-->
|
||||
<intercept-message type="MESSAGE" access="denyAll" />
|
||||
<intercept-message type="SUBSCRIBE" access="denyAll" />
|
||||
|
||||
<intercept-message pattern="/**" access="denyAll" /> <!--6-->
|
||||
</websocket-message-broker>
|
||||
<!-- end::snippet[] -->
|
||||
|
||||
</b:beans>
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026-present the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<!-- tag::snippet[] -->
|
||||
<b:bean id="myAuthorizationManager"
|
||||
class="org.springframework.security.authorization.AuthorityAuthorizationManager"
|
||||
factory-method="hasRole">
|
||||
<b:constructor-arg type="java.lang.String" value="USER"/>
|
||||
</b:bean>
|
||||
<websocket-message-broker authorization-manager-ref="myAuthorizationManager"/>
|
||||
<!-- end::snippet[] -->
|
||||
|
||||
</b:beans>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026-present the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<!-- tag::snippet[] -->
|
||||
<websocket-message-broker use-authorization-manager="true"> <!--1--> <!--2-->
|
||||
<intercept-message pattern="/user/**" access="hasRole('USER')"/> <!--3-->
|
||||
</websocket-message-broker>
|
||||
<!-- end::snippet[] -->
|
||||
|
||||
</b:beans>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026-present the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<!-- tag::snippet[] -->
|
||||
<websocket-message-broker use-authorization-manager="true" same-origin-disabled="true">
|
||||
<intercept-message pattern="/**" access="authenticated"/>
|
||||
</websocket-message-broker>
|
||||
<!-- end::snippet[] -->
|
||||
|
||||
</b:beans>
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026-present the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<!-- tag::snippet[] -->
|
||||
<http> <!-- ... -->
|
||||
<csrf request-matcher-ref="csrfMatcher"/>
|
||||
|
||||
<headers>
|
||||
<frame-options policy="SAMEORIGIN"/>
|
||||
</headers>
|
||||
|
||||
<!-- ... -->
|
||||
</http>
|
||||
|
||||
<b:bean id="csrfMatcher"
|
||||
class="org.springframework.security.web.util.matcher.AndRequestMatcher">
|
||||
<b:constructor-arg>
|
||||
<b:array>
|
||||
<b:bean class="org.springframework.security.web.csrf.CsrfFilter$DefaultRequiresCsrfMatcher"/>
|
||||
<b:bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
|
||||
<b:constructor-arg>
|
||||
<b:bean class="org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean">
|
||||
<b:constructor-arg value="/chat/**"/>
|
||||
</b:bean>
|
||||
</b:constructor-arg>
|
||||
</b:bean>
|
||||
</b:array>
|
||||
</b:constructor-arg>
|
||||
</b:bean>
|
||||
<!-- end::snippet[] -->
|
||||
|
||||
</b:beans>
|
||||
Reference in New Issue
Block a user