1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.context.annotation.Bean;
31 import org.springframework.context.annotation.Conditional;
32 import org.springframework.context.annotation.Configuration;
33 import org.springframework.context.event.EventListener;
34 import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
35 import org.springframework.util.Assert;
36 import org.springframework.util.ClassUtils;
37 import org.springframework.web.reactive.function.client.WebClient;
38
39
40
41
42
43
44 @ConditionalOnWebApplication(type = Type.REACTIVE)
45 @ConditionalOnClass({
46 JsonPathReactiveJwtConverter.class,
47 WebClientAccessTokenRetriever.class
48 })
49 @Configuration
50 @EnableConfigurationProperties({AuthProperties.class, MessageSourceProperties.class})
51 @Slf4j
52 public class ReactiveJwtSupportAutoConfiguration {
53
54 private final AuthProperties properties;
55
56
57
58
59
60
61 public ReactiveJwtSupportAutoConfiguration(
62 AuthProperties properties) {
63 this.properties = properties;
64 }
65
66
67
68
69 @EventListener(ApplicationReadyEvent.class)
70 public void init() {
71 log.info("\n"
72 + "*********************************************************************************\n"
73 + "* {}\n"
74 + "*********************************************************************************\n"
75 + "* rolesJsonPath = {}\n"
76 + "* rolesValueList = {}\n"
77 + "* rolesValueSeparator = {}\n"
78 + "* rolePrefix = {}\n"
79 + "* nameJsonPath = {}\n"
80 + "*********************************************************************************",
81 ClassUtils.getUserClass(getClass()).getSimpleName(),
82 properties.getRolesJsonPath(),
83 properties.isRolesValueList(),
84 properties.getRolesValueSeparator(),
85 properties.getRolePrefix(),
86 properties.getNameJsonPath());
87 }
88
89
90
91
92
93
94
95 @ConditionalOnMissingBean
96 @Bean
97 public AuthenticationDetails authenticationDetails(
98 MessageSourceProperties messageSourceProperties) {
99 return new JsonPathJwtAuthenticationDetails(
100 messageSourceProperties.defaultLocale(),
101 messageSourceProperties.defaultTimeZone(),
102 properties.getPreferredLanguageJsonPath(),
103 properties.getPreferredTimeZoneJsonPath());
104 }
105
106
107
108
109
110
111 @ConditionalOnProperty(
112 prefix = "spring.security.oauth2.resourceserver.jwt",
113 name = "jwk-set-uri")
114 @ConditionalOnMissingBean
115 @Bean
116 @SuppressWarnings("DuplicatedCode")
117 public JsonPathReactiveJwtConverter jsonPathReactiveJwtConverter() {
118
119 log.info("Creating application {} ...", JsonPathReactiveJwtConverter.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 new JsonPathReactiveJwtConverter(converter);
127 }
128
129
130
131
132
133
134
135 @Conditional(JwtSupportCondition.class)
136 @ConditionalOnMissingBean
137 @Bean
138 public WebClientAccessTokenRetriever webClientAccessTokenRetriever(
139 ObjectProvider<ReactiveAccessTokenCache> accessTokenCache) {
140
141 ReactiveAccessTokenCache cache = accessTokenCache.getIfAvailable();
142 log.info("Creating common {} with cache {} ...",
143 WebClientAccessTokenRetriever.class.getSimpleName(), cache);
144 return new WebClientAccessTokenRetriever(
145 WebClient.builder().build(),
146 cache);
147 }
148
149
150
151
152
153
154
155
156
157 @ConditionalOnProperty(
158 prefix = "bremersee.auth.password-flow",
159 name = {
160 "token-endpoint",
161 "client-id",
162 "client-secret"
163 })
164 @ConditionalOnBean(JsonPathReactiveJwtConverter.class)
165 @ConditionalOnMissingBean
166 @Bean
167 public PasswordFlowReactiveAuthenticationManager passwordFlowReactiveAuthenticationManager(
168 ObjectProvider<ReactiveJwtDecoder> jwtDecoder,
169 JsonPathReactiveJwtConverter jwtConverter,
170 WebClientAccessTokenRetriever tokenRetriever) {
171
172 Assert.notNull(jwtDecoder.getIfAvailable(), "Jwt decoder must be present.");
173 log.info("Creating {} ...", PasswordFlowReactiveAuthenticationManager.class.getSimpleName());
174 return new PasswordFlowReactiveAuthenticationManager(
175 properties.getPasswordFlow(),
176 jwtDecoder.getIfAvailable(),
177 jwtConverter,
178 tokenRetriever);
179 }
180
181 }