فهرست منبع

Add remaining Kotlin samples to reference docs

Closes gh-8172
Eleftheria Stein 4 سال پیش
والد
کامیت
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`.
 For example, the following will integrate CORS support within Spring Security:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @Bean
 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:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @Bean
 SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
@@ -35,3 +54,18 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 	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:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 -----
 @Configuration
 @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.
 
 == 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.
 An explicit configuration can be found below.
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @Bean
 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`.
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 RSocketStrategies.Builder strategies = ...;
 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:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 MimeType authenticationMimeType =
 	MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
@@ -122,9 +173,24 @@ Mono<RSocketRequester> requester = RSocketRequester.builder()
 	.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.
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 Mono<RSocketRequester> requester;
 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]]
 === 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.
 An example configuration can be found below:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @Bean
 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.
 An example of creating one from the issuer can be found below:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @Bean
 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.
 For example, the token can be sent at setup time:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 MimeType authenticationMimeType =
 	MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
@@ -187,9 +306,24 @@ Mono<RSocketRequester> requester = RSocketRequester.builder()
 	.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.
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 MimeType authenticationMimeType =
 	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
 
@@ -212,7 +364,9 @@ RSocket authorization is performed with `AuthorizationPayloadInterceptor` which
 The DSL can be used to setup authorization rules based upon the `PayloadExchange`.
 An example configuration can be found below:
 
-[[source,java]]
+====
+.Java
+[source,java,role="primary"]
 ----
 rsocket
 	.authorizePayload(authz ->
@@ -227,6 +381,23 @@ rsocket
 			.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`
 <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`

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 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:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @Configuration
 @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
 <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

+ 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.
 
 Below is an example of a reactive x509 security configuration:
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @Bean
 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.
 
 The next example demonstrates how these defaults can be overridden.
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @Bean
 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.
 
 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:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @GetMapping("/")
 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.
 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:
 
-[source,java]
+====
+.Java
+[source,java,role="primary"]
 ----
 @PreAuthorize("hasAuthority('SCOPE_messages')")
 public List<Message> getMessages(...) {}
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@PreAuthorize("hasAuthority('SCOPE_messages')")
+fun getMessages(): List<Message> { }
+----
+====
+
 [[oauth2resourceserver-jwt-authorization-extraction]]
 ==== 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.
 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 {
 
@@ -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`.
 An example of the output is displayed below.
 

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است