View Javadoc
1   /*
2    * Copyright 2019-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 lombok.extern.slf4j.Slf4j;
20  import org.bremersee.context.MessageSourceProperties;
21  import org.springframework.beans.factory.ObjectProvider;
22  import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
23  import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
24  import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
25  import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
26  import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
27  import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
28  import org.springframework.boot.context.event.ApplicationReadyEvent;
29  import org.springframework.boot.context.properties.EnableConfigurationProperties;
30  import org.springframework.boot.web.client.RestTemplateBuilder;
31  import org.springframework.context.annotation.Bean;
32  import org.springframework.context.annotation.Conditional;
33  import org.springframework.context.annotation.Configuration;
34  import org.springframework.context.event.EventListener;
35  import org.springframework.security.oauth2.jwt.JwtDecoder;
36  import org.springframework.util.Assert;
37  import org.springframework.util.ClassUtils;
38  
39  /**
40   * The authentication support auto configuration.
41   *
42   * @author Christian Bremer
43   */
44  @ConditionalOnWebApplication(type = Type.SERVLET)
45  @ConditionalOnClass({
46      RestTemplateBuilder.class,
47      JsonPathJwtConverter.class,
48      RestTemplateAccessTokenRetriever.class
49  })
50  @Configuration
51  @EnableConfigurationProperties({AuthProperties.class, MessageSourceProperties.class})
52  @Slf4j
53  public class JwtSupportAutoConfiguration {
54  
55    private final AuthProperties properties;
56  
57    /**
58     * Instantiates a new authentication support auto configuration.
59     *
60     * @param properties the properties
61     */
62    public JwtSupportAutoConfiguration(
63        AuthProperties properties) {
64      this.properties = properties;
65    }
66  
67    /**
68     * Init.
69     */
70    @EventListener(ApplicationReadyEvent.class)
71    public void init() {
72      log.info("\n"
73              + "*********************************************************************************\n"
74              + "* {}\n"
75              + "*********************************************************************************\n"
76              + "* rolesJsonPath = {}\n"
77              + "* rolesValueList = {}\n"
78              + "* rolesValueSeparator = {}\n"
79              + "* rolePrefix = {}\n"
80              + "* nameJsonPath = {}\n"
81              + "*********************************************************************************",
82          ClassUtils.getUserClass(getClass()).getSimpleName(),
83          properties.getRolesJsonPath(),
84          properties.isRolesValueList(),
85          properties.getRolesValueSeparator(),
86          properties.getRolePrefix(),
87          properties.getNameJsonPath());
88    }
89  
90    /**
91     * Creates authentication details bean.
92     *
93     * @param messageSourceProperties the message source properties
94     * @return the authentication details
95     */
96    @ConditionalOnMissingBean
97    @Bean
98    public AuthenticationDetails authenticationDetails(
99        MessageSourceProperties messageSourceProperties) {
100     return new JsonPathJwtAuthenticationDetails(
101         messageSourceProperties.defaultLocale(),
102         messageSourceProperties.defaultTimeZone(),
103         properties.getPreferredLanguageJsonPath(),
104         properties.getPreferredTimeZoneJsonPath());
105   }
106 
107   /**
108    * Creates a json path jwt converter bean.
109    *
110    * @return the json path jwt converter
111    */
112   @ConditionalOnProperty(
113       prefix = "spring.security.oauth2.resourceserver.jwt",
114       name = "jwk-set-uri")
115   @ConditionalOnMissingBean
116   @Bean
117   @SuppressWarnings("DuplicatedCode")
118   public JsonPathJwtConverter jsonPathJwtConverter() {
119     log.info("Creating application {} ...", JsonPathJwtConverter.class.getSimpleName());
120     JsonPathJwtConverter converter = new JsonPathJwtConverter();
121     converter.setNameJsonPath(properties.getNameJsonPath());
122     converter.setRolePrefix(properties.getRolePrefix());
123     converter.setRolesJsonPath(properties.getRolesJsonPath());
124     converter.setRolesValueList(properties.isRolesValueList());
125     converter.setRolesValueSeparator(properties.getRolesValueSeparator());
126     return converter;
127   }
128 
129   /**
130    * Creates access token retriever.
131    *
132    * @param restTemplateBuilder the rest template builder
133    * @param accessTokenCache the access token cache
134    * @return the rest template access token retriever
135    */
136   @Conditional(JwtSupportCondition.class)
137   @ConditionalOnMissingBean
138   @Bean
139   public RestTemplateAccessTokenRetriever restTemplateAccessTokenRetriever(
140       ObjectProvider<RestTemplateBuilder> restTemplateBuilder,
141       ObjectProvider<AccessTokenCache> accessTokenCache) {
142 
143     AccessTokenCache cache = accessTokenCache.getIfAvailable();
144     log.info("Creating common {} with cache {} ...",
145         RestTemplateAccessTokenRetriever.class.getSimpleName(), cache);
146     Assert.notNull(
147         restTemplateBuilder.getIfAvailable(),
148         "Rest template builder must be present.");
149     return new RestTemplateAccessTokenRetriever(
150         restTemplateBuilder.getIfAvailable().build(),
151         cache);
152   }
153 
154   /**
155    * Creates a password flow authentication manager.
156    *
157    * @param jwtDecoder the jwt decoder
158    * @param jwtConverter the jwt converter
159    * @param tokenRetriever the token retriever
160    * @return the password flow authentication manager
161    */
162   @ConditionalOnProperty(
163       prefix = "bremersee.auth.password-flow",
164       name = {
165           "token-endpoint",
166           "client-id",
167           "client-secret"
168       })
169   @ConditionalOnBean(JsonPathJwtConverter.class)
170   @ConditionalOnMissingBean(PasswordFlowAuthenticationManager.class)
171   @Bean
172   public PasswordFlowAuthenticationManager passwordFlowAuthenticationManager(
173       ObjectProvider<JwtDecoder> jwtDecoder,
174       JsonPathJwtConverter jwtConverter,
175       RestTemplateAccessTokenRetriever tokenRetriever) {
176 
177     log.info("Creating application {} ...",
178         PasswordFlowAuthenticationManager.class.getSimpleName());
179     Assert.notNull(
180         jwtDecoder.getIfAvailable(),
181         "Jwt decoder must be present.");
182     return new PasswordFlowAuthenticationManager(
183         properties.getPasswordFlow(),
184         jwtDecoder.getIfAvailable(),
185         jwtConverter,
186         tokenRetriever);
187   }
188 
189 }