diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 28bc5af3b8..9246ec17d2 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -122,6 +122,7 @@ *** xref:reactive/authentication/x509.adoc[X.509 Authentication] *** xref:reactive/authentication/logout.adoc[Logout] ** Authorization +*** xref:reactive/authorization/authorize-http-requests.adoc[Authorize HTTP Requests] *** xref:reactive/authorization/method.adoc[EnableReactiveMethodSecurity] ** xref:reactive/oauth2/index.adoc[OAuth2] *** xref:reactive/oauth2/login/index.adoc[OAuth2 Log In] diff --git a/docs/modules/ROOT/pages/reactive/authorization/authorize-http-requests.adoc b/docs/modules/ROOT/pages/reactive/authorization/authorize-http-requests.adoc new file mode 100644 index 0000000000..2a4faefc65 --- /dev/null +++ b/docs/modules/ROOT/pages/reactive/authorization/authorize-http-requests.adoc @@ -0,0 +1,104 @@ +[[servlet-authorization-authorizationfilter]] += Authorize ServerHttpRequest + +Spring Security provides support for authorizing the incoming HTTP requests. +By default, Spring Security’s authorization will require all requests to be authenticated. +The explicit configuration looks like: + +.All Requests Require Authenticated User +==== +.Java +[source,java,role="primary"] +---- +@Bean +SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http + .authorizeExchange(exchanges -> exchanges + .anyExchange().authenticated() + ) + .httpBasic(withDefaults()) + .formLogin(withDefaults()); + return http.build(); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Bean +fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { + return http { + authorizeExchange { + authorize(anyExchange, authenticated) + } + formLogin { } + httpBasic { } + } +} +---- +==== + + +We can configure Spring Security to have different rules by adding more rules in order of precedence. + +.Multiple Authorize Requests Rules +==== +.Java +[source,java,role="primary"] +---- +import static org.springframework.security.authorization.AuthorityReactiveAuthorizationManager.hasRole; +// ... +@Bean +SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) { + // @formatter:off + http + // ... + .authorizeExchange((authorize) -> authorize // <1> + .pathMatchers("/resources/**", "/signup", "/about").permitAll() // <2> + .pathMatchers("/admin/**").hasRole("ADMIN") // <3> + .pathMatchers("/db/**").access((authentication, context) -> // <4> + hasRole("ADMIN").check(authentication, context) + .filter(decision -> !decision.isGranted()) + .switchIfEmpty(hasRole("DBA").check(authentication, context)) + ) + .anyExchange().denyAll() // <5> + ); + // @formatter:on + return http.build(); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Bean +fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { + return http { + authorizeExchange { // <1> + authorize(pathMatchers("/resources/**", "/signup", "/about"), permitAll) // <2> + authorize("/admin/**", hasRole("ADMIN")) // <3> + authorize("/db/**", { authentication, context -> // <4> + hasRole("ADMIN").check(authentication, context) + .filter({ decision -> !decision.isGranted() }) + .switchIfEmpty(hasRole("DBA").check(authentication, context)) + }) + authorize(anyExchange, denyAll) // <5> + } + // ... + } +} +---- +==== + +<1> There are multiple authorization rules specified. +Each rule is considered in the order they were declared. +<2> We specified multiple URL patterns that any user can access. +Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about". +<3> Any URL that starts with "/admin/" will be restricted to users who have the authority "ROLE_ADMIN". +You will notice that since we are invoking the `hasRole` method we do not need to specify the "ROLE_" prefix. +<4> Any URL that starts with "/db/" requires the user to have both "ROLE_ADMIN" and "ROLE_DBA". +This demonstrates the flexibility of providing a custom `ReactiveAuthorizationManager` allowing us to implement arbitrary authorization logic. +For simplicity, the sample uses a lambda and delegate to the existing `AuthorityReactiveAuthorizationManager.hasRole` implementation. +However, in a real world situation applications would likely implement the logic in a proper class implementing `ReactiveAuthorizationManager`. +<5> Any URL that has not already been matched on is denied access. +This is a good strategy if you do not want to accidentally forget to update your authorization rules.