From 0428cdd9348ae02c3b3a7395965bff0be21d8ae7 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 19 May 2017 21:11:42 -0500 Subject: [PATCH] Add @EnableWebFluxSecurity Fixes gh-4344 --- .../web/reactive/EnableWebFluxSecurity.java | 36 +++++++++ .../WebFluxSecurityConfiguration.java | 79 +++++++++++++++++++ .../java/sample/HelloWebfluxApplication.java | 57 +------------ .../src/main/java/sample/SecurityConfig.java | 54 +++++++++++++ .../sample/HelloWebfluxFnApplication.java | 24 ------ .../src/main/java/sample/SecurityConfig.java | 54 +++++++++++++ 6 files changed, 224 insertions(+), 80 deletions(-) create mode 100644 config/src/main/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurity.java create mode 100644 config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java create mode 100644 samples/javaconfig/hellowebflux/src/main/java/sample/SecurityConfig.java create mode 100644 samples/javaconfig/hellowebfluxfn/src/main/java/sample/SecurityConfig.java diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurity.java new file mode 100644 index 0000000000..2546840ce3 --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurity.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2002-2017 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 + * * + * * http://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.config.annotation.web.reactive; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +/** + * @author Rob Winch + * @since 5.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@Import(WebFluxSecurityConfiguration.class) +@Configuration +public @interface EnableWebFluxSecurity { +} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java new file mode 100644 index 0000000000..2745200e3e --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java @@ -0,0 +1,79 @@ +/* + * + * * Copyright 2002-2017 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 + * * + * * http://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.config.annotation.web.reactive; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.ReactiveAdapterRegistry; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.authentication.UserDetailsRepository; +import org.springframework.security.authentication.UserDetailsRepositoryAuthenticationManager; +import org.springframework.security.config.web.server.HttpSecurity; +import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver; +import org.springframework.security.web.server.context.WebSessionSecurityContextRepository; +import org.springframework.web.reactive.config.WebFluxConfigurer; +import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; + +import static org.springframework.security.config.web.server.HttpSecurity.http; + +/** + * @author Rob Winch + * @since 5.0 + */ +@Configuration +public class WebFluxSecurityConfiguration implements WebFluxConfigurer { + @Autowired(required = false) + private ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); + + @Autowired(required = false) + private ReactiveAuthenticationManager authenticationManager; + + @Autowired(required = false) + private UserDetailsRepository userDetailsRepository; + + @Override + public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { + configurer.addCustomResolver(authenticationPrincipalArgumentResolver()); + } + + @Bean + public AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver() { + return new AuthenticationPrincipalArgumentResolver(adapterRegistry); + } + + @Bean + public HttpSecurity httpSecurity() { + HttpSecurity http = http(); + http.authenticationManager(authenticationManager()); + http.securityContextRepository(new WebSessionSecurityContextRepository()); + return http; + } + + private ReactiveAuthenticationManager authenticationManager() { + if(authenticationManager != null) { + return authenticationManager; + } + if(userDetailsRepository != null) { + return new UserDetailsRepositoryAuthenticationManager(userDetailsRepository); + } + return null; + } +} diff --git a/samples/javaconfig/hellowebflux/src/main/java/sample/HelloWebfluxApplication.java b/samples/javaconfig/hellowebflux/src/main/java/sample/HelloWebfluxApplication.java index 12d48f5263..a479e78910 100644 --- a/samples/javaconfig/hellowebflux/src/main/java/sample/HelloWebfluxApplication.java +++ b/samples/javaconfig/hellowebflux/src/main/java/sample/HelloWebfluxApplication.java @@ -16,36 +16,19 @@ package sample; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.authentication.UserDetailsRepositoryAuthenticationManager; -import org.springframework.security.authorization.AuthorizationDecision; -import org.springframework.security.config.web.server.AuthorizeExchangeBuilder; -import org.springframework.security.config.web.server.HttpSecurity; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver; -import org.springframework.security.web.server.authorization.AuthorizationContext; -import org.springframework.security.web.server.context.WebSessionSecurityContextRepository; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.config.EnableWebFlux; -import org.springframework.web.reactive.config.WebFluxConfigurer; -import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; -import org.springframework.web.server.WebFilter; -import reactor.core.publisher.Mono; import reactor.ipc.netty.NettyContext; import reactor.ipc.netty.http.server.HttpServer; -import static org.springframework.security.config.web.server.HttpSecurity.http; - /** * @author Rob Winch * @since 5.0 @@ -53,24 +36,16 @@ import static org.springframework.security.config.web.server.HttpSecurity.http; @Configuration @EnableWebFlux @ComponentScan -public class HelloWebfluxApplication implements WebFluxConfigurer { +public class HelloWebfluxApplication { @Value("${server.port:8080}") private int port = 8080; - @Autowired - private ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); - public static void main(String[] args) throws Exception { try(AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HelloWebfluxApplication.class)) { context.getBean(NettyContext.class).onClose().block(); } } - @Override - public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { - configurer.addCustomResolver(authenticationPrincipalArgumentResolver()); - } - @Bean public NettyContext nettyContext(ApplicationContext context) { HttpHandler handler = DispatcherHandler.toHttpHandler(context); @@ -78,34 +53,4 @@ public class HelloWebfluxApplication implements WebFluxConfigurer { HttpServer httpServer = HttpServer.create("localhost", port); return httpServer.newHandler(adapter).block(); } - - @Bean - public AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver() { - return new AuthenticationPrincipalArgumentResolver(adapterRegistry); - } - - @Bean - WebFilter springSecurityFilterChain(ReactiveAuthenticationManager manager) throws Exception { - HttpSecurity http = http(); - http.securityContextRepository(new WebSessionSecurityContextRepository()); - http.authenticationManager(manager); - http.httpBasic(); - - AuthorizeExchangeBuilder authorize = http.authorizeExchange(); - authorize.antMatchers("/admin/**").hasRole("ADMIN"); - authorize.antMatchers("/users/{user}/**").access(this::currentUserMatchesPath); - authorize.anyExchange().authenticated(); - return http.build(); - } - - private Mono currentUserMatchesPath(Mono authentication, AuthorizationContext context) { - return authentication - .map( a -> context.getVariables().get("user").equals(a.getName())) - .map( granted -> new AuthorizationDecision(granted)); - } - - @Bean - public ReactiveAuthenticationManager authenticationManager(UserRepositoryUserDetailsRepository udr) { - return new UserDetailsRepositoryAuthenticationManager(udr); - } } diff --git a/samples/javaconfig/hellowebflux/src/main/java/sample/SecurityConfig.java b/samples/javaconfig/hellowebflux/src/main/java/sample/SecurityConfig.java new file mode 100644 index 0000000000..d848ed222d --- /dev/null +++ b/samples/javaconfig/hellowebflux/src/main/java/sample/SecurityConfig.java @@ -0,0 +1,54 @@ +/* + * + * * Copyright 2002-2017 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 + * * + * * http://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 sample; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.AuthorizeExchangeBuilder; +import org.springframework.security.config.web.server.HttpSecurity; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.server.authorization.AuthorizationContext; +import org.springframework.web.server.WebFilter; +import reactor.core.publisher.Mono; + +/** + * @author Rob Winch + * @since 5.0 + */ +@EnableWebFluxSecurity +public class SecurityConfig { + + @Bean + WebFilter springSecurityFilterChain(HttpSecurity http) throws Exception { + http.httpBasic(); + + AuthorizeExchangeBuilder authorize = http.authorizeExchange(); + authorize.antMatchers("/admin/**").hasRole("ADMIN"); + authorize.antMatchers("/users/{user}/**").access(this::currentUserMatchesPath); + authorize.anyExchange().authenticated(); + return http.build(); + } + + private Mono currentUserMatchesPath(Mono authentication, AuthorizationContext context) { + return authentication + .map( a -> context.getVariables().get("user").equals(a.getName())) + .map( granted -> new AuthorizationDecision(granted)); + } +} diff --git a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java index 28d3f604ff..b4ba39a8fe 100644 --- a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java +++ b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java @@ -90,28 +90,4 @@ public class HelloWebfluxFnApplication { return RouterFunctions.toHttpHandler(route, handlerStrategies); } - @Bean - WebFilter springSecurityFilterChain(ReactiveAuthenticationManager manager) throws Exception { - HttpSecurity http = http(); - http.securityContextRepository(new WebSessionSecurityContextRepository()); - http.authenticationManager(manager); - http.httpBasic(); - - AuthorizeExchangeBuilder authorize = http.authorizeExchange(); - authorize.antMatchers("/admin/**").hasRole("ADMIN"); - authorize.antMatchers("/users/{user}/**").access(this::currentUserMatchesPath); - authorize.anyExchange().authenticated(); - return http.build(); - } - - private Mono currentUserMatchesPath(Mono authentication, AuthorizationContext context) { - return authentication - .map( a -> context.getVariables().get("user").equals(a.getName())) - .map( granted -> new AuthorizationDecision(granted)); - } - - @Bean - public ReactiveAuthenticationManager authenticationManager(UserRepositoryUserDetailsRepository udr) { - return new UserDetailsRepositoryAuthenticationManager(udr); - } } diff --git a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/SecurityConfig.java b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/SecurityConfig.java new file mode 100644 index 0000000000..d848ed222d --- /dev/null +++ b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/SecurityConfig.java @@ -0,0 +1,54 @@ +/* + * + * * Copyright 2002-2017 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 + * * + * * http://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 sample; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.AuthorizeExchangeBuilder; +import org.springframework.security.config.web.server.HttpSecurity; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.server.authorization.AuthorizationContext; +import org.springframework.web.server.WebFilter; +import reactor.core.publisher.Mono; + +/** + * @author Rob Winch + * @since 5.0 + */ +@EnableWebFluxSecurity +public class SecurityConfig { + + @Bean + WebFilter springSecurityFilterChain(HttpSecurity http) throws Exception { + http.httpBasic(); + + AuthorizeExchangeBuilder authorize = http.authorizeExchange(); + authorize.antMatchers("/admin/**").hasRole("ADMIN"); + authorize.antMatchers("/users/{user}/**").access(this::currentUserMatchesPath); + authorize.anyExchange().authenticated(); + return http.build(); + } + + private Mono currentUserMatchesPath(Mono authentication, AuthorizationContext context) { + return authentication + .map( a -> context.getVariables().get("user").equals(a.getName())) + .map( granted -> new AuthorizationDecision(granted)); + } +}