Pārlūkot izejas kodu

Add remaining Kotlin samples to reference docs

Closes gh-8172
Eleftheria Stein 4 gadi atpakaļ
vecāks
revīzija
da9d7414bd

+ 36 - 2
docs/manual/src/docs/asciidoc/_includes/reactive/cors.adoc

@@ -10,7 +10,9 @@ The easiest way to ensure that CORS is handled first is to use the `CorsWebFilte
 Users can integrate the `CorsWebFilter` with Spring Security by providing a `CorsConfigurationSource`.
 Users can integrate the `CorsWebFilter` with Spring Security by providing a `CorsConfigurationSource`.
 For example, the following will integrate CORS support within Spring Security:
 For example, the following will integrate CORS support within Spring Security:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @Bean
 @Bean
 CorsConfigurationSource corsConfigurationSource() {
 CorsConfigurationSource corsConfigurationSource() {
@@ -23,9 +25,26 @@ CorsConfigurationSource corsConfigurationSource() {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Bean
+fun corsConfigurationSource(): CorsConfigurationSource {
+    val configuration = CorsConfiguration()
+    configuration.allowedOrigins = listOf("https://example.com")
+    configuration.allowedMethods = listOf("GET", "POST")
+    val source = UrlBasedCorsConfigurationSource()
+    source.registerCorsConfiguration("/**", configuration)
+    return source
+}
+----
+====
+
 The following will disable the CORS integration within Spring Security:
 The following will disable the CORS integration within Spring Security:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @Bean
 @Bean
 SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
@@ -35,3 +54,18 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 	return http.build();
 	return http.build();
 }
 }
 ----
 ----
+
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Bean
+fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
+    return http {
+        // ...
+        cors {
+            disable()
+        }
+    }
+}
+----
+====

+ 181 - 10
docs/manual/src/docs/asciidoc/_includes/reactive/rsocket.adoc

@@ -14,7 +14,9 @@ You can find a few sample applications that demonstrate the code below:
 
 
 You can find a minimal RSocket Security configuration below:
 You can find a minimal RSocket Security configuration below:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 -----
 -----
 @Configuration
 @Configuration
 @EnableRSocketSecurity
 @EnableRSocketSecurity
@@ -32,6 +34,25 @@ public class HelloRSocketSecurityConfig {
 }
 }
 -----
 -----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Configuration
+@EnableRSocketSecurity
+open class HelloRSocketSecurityConfig {
+    @Bean
+    open fun userDetailsService(): MapReactiveUserDetailsService {
+        val user = User.withDefaultPasswordEncoder()
+            .username("user")
+            .password("user")
+            .roles("USER")
+            .build()
+        return MapReactiveUserDetailsService(user)
+    }
+}
+----
+====
+
 This configuration enables <<rsocket-authentication-simple,simple authentication>> and sets up <<rsocket-authorization,rsocket-authorization>> to require an authenticated user for any request.
 This configuration enables <<rsocket-authentication-simple,simple authentication>> and sets up <<rsocket-authorization,rsocket-authorization>> to require an authenticated user for any request.
 
 
 == Adding SecuritySocketAcceptorInterceptor
 == Adding SecuritySocketAcceptorInterceptor
@@ -86,7 +107,9 @@ See `RSocketSecurity.basicAuthentication(Customizer)` for setting it up.
 The RSocket receiver can decode the credentials using `AuthenticationPayloadExchangeConverter` which is automatically setup using the `simpleAuthentication` portion of the DSL.
 The RSocket receiver can decode the credentials using `AuthenticationPayloadExchangeConverter` which is automatically setup using the `simpleAuthentication` portion of the DSL.
 An explicit configuration can be found below.
 An explicit configuration can be found below.
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @Bean
 @Bean
 PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
 PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
@@ -101,17 +124,45 @@ PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Bean
+open fun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorInterceptor {
+    rsocket
+        .authorizePayload { authorize -> authorize
+                .anyRequest().authenticated()
+                .anyExchange().permitAll()
+        }
+        .simpleAuthentication(withDefaults())
+    return rsocket.build()
+}
+----
+====
+
 The RSocket sender can send credentials using `SimpleAuthenticationEncoder` which can be added to Spring's `RSocketStrategies`.
 The RSocket sender can send credentials using `SimpleAuthenticationEncoder` which can be added to Spring's `RSocketStrategies`.
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 RSocketStrategies.Builder strategies = ...;
 RSocketStrategies.Builder strategies = ...;
 strategies.encoder(new SimpleAuthenticationEncoder());
 strategies.encoder(new SimpleAuthenticationEncoder());
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+var strategies: RSocketStrategies.Builder = ...
+strategies.encoder(SimpleAuthenticationEncoder())
+----
+====
+
 It can then be used to send a username and password to the receiver in the setup:
 It can then be used to send a username and password to the receiver in the setup:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 MimeType authenticationMimeType =
 MimeType authenticationMimeType =
 	MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
 	MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
@@ -122,9 +173,24 @@ Mono<RSocketRequester> requester = RSocketRequester.builder()
 	.connectTcp(host, port);
 	.connectTcp(host, port);
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+val authenticationMimeType: MimeType =
+    MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)
+val credentials = UsernamePasswordMetadata("user", "password")
+val requester: Mono<RSocketRequester> = RSocketRequester.builder()
+    .setupMetadata(credentials, authenticationMimeType)
+    .rsocketStrategies(strategies.build())
+    .connectTcp(host, port)
+----
+====
+
 Alternatively or additionally, a username and password can be sent in a request.
 Alternatively or additionally, a username and password can be sent in a request.
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 Mono<RSocketRequester> requester;
 Mono<RSocketRequester> requester;
 UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
 UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
@@ -138,6 +204,26 @@ public Mono<AirportLocation> findRadar(String code) {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+import org.springframework.messaging.rsocket.retrieveMono
+
+// ...
+
+var requester: Mono<RSocketRequester>? = null
+var credentials = UsernamePasswordMetadata("user", "password")
+
+open fun findRadar(code: String): Mono<AirportLocation> {
+    return requester!!.flatMap { req ->
+        req.route("find.radar.{code}", code)
+            .metadata(credentials, authenticationMimeType)
+            .retrieveMono<AirportLocation>()
+    }
+}
+----
+====
+
 [[rsocket-authentication-jwt]]
 [[rsocket-authentication-jwt]]
 === JWT
 === JWT
 
 
@@ -147,7 +233,9 @@ The support comes in the form of authenticating a JWT (determining the JWT is va
 The RSocket receiver can decode the credentials using `BearerPayloadExchangeConverter` which is automatically setup using the `jwt` portion of the DSL.
 The RSocket receiver can decode the credentials using `BearerPayloadExchangeConverter` which is automatically setup using the `jwt` portion of the DSL.
 An example configuration can be found below:
 An example configuration can be found below:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @Bean
 @Bean
 PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
 PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
@@ -162,10 +250,28 @@ PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Bean
+fun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorInterceptor {
+    rsocket
+        .authorizePayload { authorize -> authorize
+            .anyRequest().authenticated()
+            .anyExchange().permitAll()
+        }
+        .jwt(withDefaults())
+    return rsocket.build()
+}
+----
+====
+
 The configuration above relies on the existence of a `ReactiveJwtDecoder` `@Bean` being present.
 The configuration above relies on the existence of a `ReactiveJwtDecoder` `@Bean` being present.
 An example of creating one from the issuer can be found below:
 An example of creating one from the issuer can be found below:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @Bean
 @Bean
 ReactiveJwtDecoder jwtDecoder() {
 ReactiveJwtDecoder jwtDecoder() {
@@ -174,10 +280,23 @@ ReactiveJwtDecoder jwtDecoder() {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Bean
+fun jwtDecoder(): ReactiveJwtDecoder {
+    return ReactiveJwtDecoders
+        .fromIssuerLocation("https://example.com/auth/realms/demo")
+}
+----
+====
+
 The RSocket sender does not need to do anything special to send the token because the value is just a simple String.
 The RSocket sender does not need to do anything special to send the token because the value is just a simple String.
 For example, the token can be sent at setup time:
 For example, the token can be sent at setup time:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 MimeType authenticationMimeType =
 MimeType authenticationMimeType =
 	MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
 	MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
@@ -187,9 +306,24 @@ Mono<RSocketRequester> requester = RSocketRequester.builder()
 	.connectTcp(host, port);
 	.connectTcp(host, port);
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+val authenticationMimeType: MimeType =
+    MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)
+val token: BearerTokenMetadata = ...
+
+val requester = RSocketRequester.builder()
+    .setupMetadata(token, authenticationMimeType)
+    .connectTcp(host, port)
+----
+====
+
 Alternatively or additionally, the token can be sent in a request.
 Alternatively or additionally, the token can be sent in a request.
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 MimeType authenticationMimeType =
 MimeType authenticationMimeType =
 	MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
 	MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
@@ -205,6 +339,24 @@ public Mono<AirportLocation> findRadar(String code) {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+val authenticationMimeType: MimeType =
+    MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)
+var requester: Mono<RSocketRequester>? = null
+val token: BearerTokenMetadata = ...
+
+open fun findRadar(code: String): Mono<AirportLocation> {
+    return this.requester!!.flatMap { req ->
+        req.route("find.radar.{code}", code)
+            .metadata(token, authenticationMimeType)
+            .retrieveMono<AirportLocation>()
+    }
+}
+----
+====
+
 [[rsocket-authorization]]
 [[rsocket-authorization]]
 == RSocket Authorization
 == RSocket Authorization
 
 
@@ -212,7 +364,9 @@ RSocket authorization is performed with `AuthorizationPayloadInterceptor` which
 The DSL can be used to setup authorization rules based upon the `PayloadExchange`.
 The DSL can be used to setup authorization rules based upon the `PayloadExchange`.
 An example configuration can be found below:
 An example configuration can be found below:
 
 
-[[source,java]]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 rsocket
 rsocket
 	.authorizePayload(authz ->
 	.authorizePayload(authz ->
@@ -227,6 +381,23 @@ rsocket
 			.anyExchange().permitAll() // <6>
 			.anyExchange().permitAll() // <6>
 	);
 	);
 ----
 ----
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+rsocket
+    .authorizePayload { authz ->
+        authz
+            .setup().hasRole("SETUP") // <1>
+            .route("fetch.profile.me").authenticated() // <2>
+            .matcher { payloadExchange -> isMatch(payloadExchange) } // <3>
+            .hasRole("CUSTOM")
+            .route("fetch.profile.{username}") // <4>
+            .access { authentication, context -> checkFriends(authentication, context) }
+            .anyRequest().authenticated() // <5>
+            .anyExchange().permitAll()
+    } // <6>
+----
+====
 <1> Setting up a connection requires the authority `ROLE_SETUP`
 <1> Setting up a connection requires the authority `ROLE_SETUP`
 <2> If the route is `fetch.profile.me` authorization only requires the user be authenticated
 <2> If the route is `fetch.profile.me` authorization only requires the user be authenticated
 <3> In this rule we setup a custom matcher where authorization requires the user to have the authority `ROLE_CUSTOM`
 <3> In this rule we setup a custom matcher where authorization requires the user to have the authority `ROLE_CUSTOM`

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 573 - 37
docs/manual/src/docs/asciidoc/_includes/reactive/test.adoc


+ 43 - 1
docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc

@@ -134,7 +134,9 @@ You can configure multiple `SecurityWebFilterChain` instances to separate config
 
 
 For example, you can isolate configuration for URLs that start with `/api`, like so:
 For example, you can isolate configuration for URLs that start with `/api`, like so:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @Configuration
 @Configuration
 @EnableWebFluxSecurity
 @EnableWebFluxSecurity
@@ -171,6 +173,46 @@ static class MultiSecurityHttpConfig {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Configuration
+@EnableWebFluxSecurity
+open class MultiSecurityHttpConfig {
+    @Order(Ordered.HIGHEST_PRECEDENCE)                                                      <1>
+    @Bean
+    open fun apiHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
+        return http {
+            securityMatcher(PathPatternParserServerWebExchangeMatcher("/api/**"))           <2>
+            authorizeExchange {
+                authorize(anyExchange, authenticated)
+            }
+            oauth2ResourceServer {
+                jwt { }                                                                     <3>
+            }
+        }
+    }
+
+    @Bean
+    open fun webHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {            <4>
+        return http {
+            authorizeExchange {
+                authorize(anyExchange, authenticated)
+            }
+            httpBasic { }                                                                   <5>
+        }
+    }
+
+    @Bean
+    open fun userDetailsService(): ReactiveUserDetailsService {
+        return MapReactiveUserDetailsService(
+            PasswordEncodedUser.user(), PasswordEncodedUser.admin()
+        )
+    }
+}
+----
+====
+
 <1> Configure a `SecurityWebFilterChain` with an `@Order` to specify which `SecurityWebFilterChain` Spring Security should consider first
 <1> Configure a `SecurityWebFilterChain` with an `@Order` to specify which `SecurityWebFilterChain` Spring Security should consider first
 <2> Use `PathPatternParserServerWebExchangeMatcher` to state that this `SecurityWebFilterChain` will only apply to URL paths that start with `/api/`
 <2> Use `PathPatternParserServerWebExchangeMatcher` to state that this `SecurityWebFilterChain` will only apply to URL paths that start with `/api/`
 <3> Specify the authentication mechanisms that will be used for `/api/**` endpoints
 <3> Specify the authentication mechanisms that will be used for `/api/**` endpoints

+ 45 - 2
docs/manual/src/docs/asciidoc/_includes/reactive/x509.adoc

@@ -4,7 +4,9 @@
 Similar to <<servlet-x509,Servlet X.509 authentication>>, reactive x509 authentication filter allows extracting an authentication token from a certificate provided by a client.
 Similar to <<servlet-x509,Servlet X.509 authentication>>, reactive x509 authentication filter allows extracting an authentication token from a certificate provided by a client.
 
 
 Below is an example of a reactive x509 security configuration:
 Below is an example of a reactive x509 security configuration:
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @Bean
 @Bean
 public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
 public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
@@ -17,11 +19,28 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Bean
+fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
+    return http {
+        x509 { }
+        authorizeExchange {
+            authorize(anyExchange, authenticated)
+        }
+    }
+}
+----
+====
+
 In the configuration above, when neither `principalExtractor` nor `authenticationManager` is provided defaults will be used. The default principal extractor is `SubjectDnX509PrincipalExtractor` which extracts the CN (common name) field from a certificate provided by a client. The default authentication manager is `ReactivePreAuthenticatedAuthenticationManager` which performs user account validation, checking that user account with a name extracted by `principalExtractor` exists and it is not locked, disabled, or expired.
 In the configuration above, when neither `principalExtractor` nor `authenticationManager` is provided defaults will be used. The default principal extractor is `SubjectDnX509PrincipalExtractor` which extracts the CN (common name) field from a certificate provided by a client. The default authentication manager is `ReactivePreAuthenticatedAuthenticationManager` which performs user account validation, checking that user account with a name extracted by `principalExtractor` exists and it is not locked, disabled, or expired.
 
 
 The next example demonstrates how these defaults can be overridden.
 The next example demonstrates how these defaults can be overridden.
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @Bean
 @Bean
 public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
 public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
@@ -47,6 +66,30 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Bean
+fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? {
+    val customPrincipalExtractor = SubjectDnX509PrincipalExtractor()
+    customPrincipalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)")
+    val customAuthenticationManager = ReactiveAuthenticationManager { authentication: Authentication ->
+        authentication.isAuthenticated = "Trusted Org Unit" == authentication.name
+        Mono.just(authentication)
+    }
+    return http {
+        x509 {
+            principalExtractor = customPrincipalExtractor
+            authenticationManager = customAuthenticationManager
+        }
+        authorizeExchange {
+            authorize(anyExchange, authenticated)
+        }
+    }
+}
+----
+====
+
 In this example, a username is extracted from the OU field of a client certificate instead of CN, and account lookup using `ReactiveUserDetailsService` is not performed at all. Instead, if the provided certificate issued to an OU named "Trusted Org Unit", a request will be authenticated.
 In this example, a username is extracted from the OU field of a client certificate instead of CN, and account lookup using `ReactiveUserDetailsService` is not performed at all. Instead, if the provided certificate issued to an OU named "Trusted Org Unit", a request will be authenticated.
 
 
 For an example of configuring Netty and `WebClient` or `curl` command-line tool to use mutual TLS and enable X.509 authentication, please refer to https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration/authentication/x509.
 For an example of configuring Netty and `WebClient` or `curl` command-line tool to use mutual TLS and enable X.509 authentication, please refer to https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration/authentication/x509.

+ 17 - 1
docs/manual/src/docs/asciidoc/_includes/servlet/authentication/anonymous.adoc

@@ -108,7 +108,9 @@ https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc
 
 
 This means that a construct like this one:
 This means that a construct like this one:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @GetMapping("/")
 @GetMapping("/")
 public String method(Authentication authentication) {
 public String method(Authentication authentication) {
@@ -120,6 +122,20 @@ public String method(Authentication authentication) {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@GetMapping("/")
+fun method(authentication: Authentication?): String {
+    return if (authentication is AnonymousAuthenticationToken) {
+        "anonymous"
+    } else {
+        "not anonymous"
+    }
+}
+----
+====
+
 will always return "not anonymous", even for anonymous requests.
 will always return "not anonymous", even for anonymous requests.
 The reason is that Spring MVC resolves the parameter using `HttpServletRequest#getPrincipal`, which is `null` when the request is anonymous.
 The reason is that Spring MVC resolves the parameter using `HttpServletRequest#getPrincipal`, which is `null` when the request is anonymous.
 
 

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

@@ -820,12 +820,22 @@ class DirectlyConfiguredJwkSetUri : WebSecurityConfigurerAdapter() {
 
 
 Or similarly with method security:
 Or similarly with method security:
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 @PreAuthorize("hasAuthority('SCOPE_messages')")
 @PreAuthorize("hasAuthority('SCOPE_messages')")
 public List<Message> getMessages(...) {}
 public List<Message> getMessages(...) {}
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@PreAuthorize("hasAuthority('SCOPE_messages')")
+fun getMessages(): List<Message> { }
+----
+====
+
 [[oauth2resourceserver-jwt-authorization-extraction]]
 [[oauth2resourceserver-jwt-authorization-extraction]]
 ==== Extracting Authorities Manually
 ==== Extracting Authorities Manually
 
 

+ 16 - 1
docs/manual/src/docs/asciidoc/_includes/servlet/test/method.adoc

@@ -4,7 +4,9 @@
 This section demonstrates how to use Spring Security's Test support to test method based security.
 This section demonstrates how to use Spring Security's Test support to test method based security.
 We first introduce a `MessageService` that requires the user to be authenticated in order to access it.
 We first introduce a `MessageService` that requires the user to be authenticated in order to access it.
 
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 ----
 public class HelloMessageService implements MessageService {
 public class HelloMessageService implements MessageService {
 
 
@@ -17,6 +19,19 @@ public class HelloMessageService implements MessageService {
 }
 }
 ----
 ----
 
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+class HelloMessageService : MessageService {
+    @PreAuthorize("authenticated")
+    fun getMessage(): String {
+        val authentication: Authentication = SecurityContextHolder.getContext().authentication
+        return "Hello $authentication"
+    }
+}
+----
+====
+
 The result of `getMessage` is a String saying "Hello" to the current Spring Security `Authentication`.
 The result of `getMessage` is a String saying "Hello" to the current Spring Security `Authentication`.
 An example of the output is displayed below.
 An example of the output is displayed below.
 
 

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels