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.AccessLevel;
20 import lombok.Getter;
21 import lombok.extern.slf4j.Slf4j;
22 import org.bremersee.security.authentication.AuthProperties.PathMatcherProperties;
23 import org.bremersee.web.CorsProperties;
24 import org.springframework.beans.factory.ObjectProvider;
25 import org.springframework.boot.autoconfigure.security.SecurityProperties;
26 import org.springframework.core.Ordered;
27 import org.springframework.core.env.Environment;
28 import org.springframework.http.HttpMethod;
29 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
30 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
31 import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
32 import org.springframework.security.crypto.password.PasswordEncoder;
33 import org.springframework.util.ClassUtils;
34 import org.springframework.util.StringUtils;
35
36
37
38
39
40
41 @Slf4j
42 public abstract class AbstractResourceServerAutoConfiguration extends WebSecurityConfigurerAdapter
43 implements Ordered {
44
45 @Getter(AccessLevel.PROTECTED)
46 private final Environment environment;
47
48 @Getter(AccessLevel.PROTECTED)
49 private final SecurityProperties securityProperties;
50
51 @Getter(AccessLevel.PROTECTED)
52 private final AuthProperties authProperties;
53
54 @Getter(AccessLevel.PROTECTED)
55 private final CorsProperties corsProperties;
56
57 @Getter(AccessLevel.PROTECTED)
58 private final ObjectProvider<JsonPathJwtConverter> jwtConverterProvider;
59
60 @Getter(AccessLevel.PROTECTED)
61 private final ObjectProvider<PasswordEncoder> passwordEncoderProvider;
62
63
64
65
66
67
68
69
70
71
72
73 protected AbstractResourceServerAutoConfiguration(
74 Environment environment,
75 SecurityProperties securityProperties,
76 AuthProperties authProperties,
77 CorsProperties corsProperties,
78 ObjectProvider<JsonPathJwtConverter> jwtConverterProvider,
79 ObjectProvider<PasswordEncoder> passwordEncoderProvider) {
80
81 this.environment = environment;
82 this.securityProperties = securityProperties;
83 this.authProperties = authProperties;
84 this.corsProperties = corsProperties;
85 this.jwtConverterProvider = jwtConverterProvider;
86 this.passwordEncoderProvider = passwordEncoderProvider;
87 }
88
89 @Override
90 public int getOrder() {
91 return authProperties.getResourceServerOrder();
92 }
93
94
95
96
97 protected void init() {
98 final boolean hasJwkUriSet = StringUtils
99 .hasText(environment.getProperty("spring.security.oauth2.resourceserver.jwt.jwk-set-uri"));
100 log.info("\n"
101 + "*********************************************************************************\n"
102 + "* {}\n"
103 + "*********************************************************************************\n"
104 + "* enable = {}\n"
105 + "* order = {}\n"
106 + "* jwt = {}\n"
107 + "* cors = {}\n"
108 + "*********************************************************************************",
109 ClassUtils.getUserClass(getClass()).getSimpleName(),
110 authProperties.getResourceServer().name(),
111 authProperties.getResourceServerOrder(),
112 hasJwkUriSet,
113 corsProperties.isEnable());
114 }
115
116
117
118
119
120
121
122
123 protected abstract ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
124 init(HttpSecurity httpSecurity) throws Exception;
125
126 @Override
127 protected void configure(HttpSecurity httpSecurity) throws Exception {
128
129 HttpSecurity http = httpSecurity;
130 ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry reg = init(http);
131 if (authProperties.getResourceServer() == AutoSecurityMode.NONE) {
132 http = reg
133 .anyRequest().permitAll()
134 .and()
135 .httpBasic().disable();
136 } else {
137 reg = configurePathMatchers(reg);
138 http = configureAuthenticationProvider(reg.and());
139 }
140 http = http
141 .headers().frameOptions(customizer -> {
142 switch (authProperties.getFrameOptionsMode()) {
143 case DISABLE: {
144 customizer.disable();
145 break;
146 }
147 case SAMEORIGIN: {
148 customizer.sameOrigin();
149 }
150 default:
151 customizer.deny();
152 }
153 })
154 .and()
155 .csrf().disable();
156 if (corsProperties.isEnable()) {
157 http.cors();
158 } else {
159 http.cors().disable();
160 }
161 }
162
163 private ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
164 configurePathMatchers(
165 ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry reg) {
166
167 for (PathMatcherProperties props : authProperties.preparePathMatchers(corsProperties)) {
168 log.info("Securing requests to {}", props);
169 HttpMethod httpMethod = props.httpMethod();
170 if (httpMethod == null) {
171 reg = reg.antMatchers(props.getAntPattern())
172 .access(props.accessExpression(
173 authProperties::ensureRolePrefix));
174 } else {
175 reg = reg.antMatchers(httpMethod, props.getAntPattern())
176 .access(props.accessExpression(
177 authProperties::ensureRolePrefix));
178 }
179 }
180 return reg;
181 }
182
183 private HttpSecurity configureAuthenticationProvider(HttpSecurity http) throws Exception {
184
185 if (jwtConverterProvider.getIfAvailable() != null) {
186 log.info("Configure authentication provider with JWT.");
187 return http
188 .oauth2ResourceServer((rs) -> rs
189 .jwt()
190 .jwtAuthenticationConverter(jwtConverterProvider.getIfAvailable())
191 .and());
192 }
193 log.info("Configure authentication provider with basic auth and user details service.");
194 String realm = environment.getProperty("spring.application.name", "Restricted area");
195 return http
196 .formLogin().disable()
197 .httpBasic().realmName(realm)
198 .and();
199 }
200
201 }