View Javadoc
1   /*
2    * Copyright 2020 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.bremersee.security.authentication;
18  
19  import java.util.Optional;
20  import lombok.extern.slf4j.Slf4j;
21  import org.springframework.beans.factory.ObjectProvider;
22  import org.springframework.boot.autoconfigure.AutoConfigureAfter;
23  import org.springframework.boot.autoconfigure.AutoConfigureBefore;
24  import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
25  import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
26  import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
27  import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
28  import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
29  import org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration;
30  import org.springframework.boot.autoconfigure.security.SecurityProperties;
31  import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;
32  import org.springframework.boot.context.properties.EnableConfigurationProperties;
33  import org.springframework.context.annotation.Bean;
34  import org.springframework.context.annotation.Conditional;
35  import org.springframework.context.annotation.Configuration;
36  import org.springframework.security.authentication.ReactiveAuthenticationManager;
37  import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
38  import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
39  import org.springframework.security.crypto.password.PasswordEncoder;
40  import org.springframework.util.ClassUtils;
41  
42  /**
43   * The reactive in memory user details auto configuration.
44   *
45   * @author Christian Bremer
46   */
47  @Configuration(proxyBeanMethods = false)
48  @ConditionalOnClass({ReactiveAuthenticationManager.class})
49  @ConditionalOnMissingBean(
50      value = {
51          ReactiveAuthenticationManager.class,
52          ReactiveUserDetailsService.class
53      },
54      type = {
55          "org.springframework.security.oauth2.jwt.ReactiveJwtDecoder",
56          "org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector"
57      })
58  @Conditional(ReactiveInMemoryUserDetailsAutoConfiguration.ReactiveUserDetailsServiceCondition.class)
59  @AutoConfigureAfter(RSocketMessagingAutoConfiguration.class)
60  @AutoConfigureBefore(ReactiveUserDetailsServiceAutoConfiguration.class)
61  @EnableConfigurationProperties({
62      SecurityProperties.class,
63      AuthProperties.class})
64  @Slf4j
65  public class ReactiveInMemoryUserDetailsAutoConfiguration {
66  
67    /**
68     * Creates a reactive user details service bean.
69     *
70     * @param securityProperties the security properties
71     * @param authProperties the auth properties
72     * @param passwordEncoder the password encoder
73     * @return the map reactive user details service
74     */
75    @Bean
76    public MapReactiveUserDetailsService reactiveUserDetailsService(
77        SecurityProperties securityProperties,
78        AuthProperties authProperties,
79        ObjectProvider<PasswordEncoder> passwordEncoder) {
80  
81      log.info("\n"
82              + "*********************************************************************************\n"
83              + "* {} is creating a {}\n"
84              + "*********************************************************************************",
85          ClassUtils.getUserClass(getClass()).getSimpleName(),
86          ClassUtils.getUserClass(MapReactiveUserDetailsService.class).getSimpleName());
87  
88      return Optional.of(authProperties.buildBasicAuthUserDetails(passwordEncoder.getIfAvailable()))
89          .filter(userDetails -> userDetails.length > 0)
90          .map(MapReactiveUserDetailsService::new)
91          .orElseGet(() -> new ReactiveUserDetailsServiceAutoConfiguration()
92              .reactiveUserDetailsService(securityProperties, passwordEncoder));
93    }
94  
95    /**
96     * The reactive user details service condition.
97     */
98    static class ReactiveUserDetailsServiceCondition extends AnyNestedCondition {
99  
100     /**
101      * Instantiates a new Reactive user details service condition.
102      */
103     ReactiveUserDetailsServiceCondition() {
104       super(ConfigurationPhase.REGISTER_BEAN);
105     }
106 
107     /**
108      * The type R socket security enabled condition.
109      */
110     @ConditionalOnBean(type = {
111         "org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler"})
112     @SuppressWarnings("unused")
113     static class RSocketSecurityEnabledCondition {
114 
115     }
116 
117     /**
118      * The type Reactive web application condition.
119      */
120     @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
121     @SuppressWarnings("unused")
122     static class ReactiveWebApplicationCondition {
123 
124     }
125   }
126 
127 }