18
18
19
19
import java .io .IOException ;
20
20
import java .util .ArrayList ;
21
+ import java .util .HashMap ;
21
22
import java .util .List ;
23
+ import java .util .Map ;
22
24
import java .util .concurrent .atomic .AtomicInteger ;
23
25
import java .util .concurrent .atomic .AtomicReference ;
24
26
36
38
import org .apache .commons .logging .LogFactory ;
37
39
38
40
import org .springframework .core .log .LogMessage ;
41
+ import org .springframework .security .web .access .ExceptionTranslationFilter ;
42
+ import org .springframework .security .web .access .channel .ChannelProcessingFilter ;
43
+ import org .springframework .security .web .access .intercept .AuthorizationFilter ;
44
+ import org .springframework .security .web .access .intercept .FilterSecurityInterceptor ;
45
+ import org .springframework .security .web .authentication .AnonymousAuthenticationFilter ;
46
+ import org .springframework .security .web .authentication .UsernamePasswordAuthenticationFilter ;
47
+ import org .springframework .security .web .authentication .logout .LogoutFilter ;
48
+ import org .springframework .security .web .authentication .preauth .AbstractPreAuthenticatedProcessingFilter ;
49
+ import org .springframework .security .web .authentication .preauth .x509 .X509AuthenticationFilter ;
50
+ import org .springframework .security .web .authentication .rememberme .RememberMeAuthenticationFilter ;
51
+ import org .springframework .security .web .authentication .switchuser .SwitchUserFilter ;
52
+ import org .springframework .security .web .authentication .ui .DefaultLoginPageGeneratingFilter ;
53
+ import org .springframework .security .web .authentication .ui .DefaultLogoutPageGeneratingFilter ;
54
+ import org .springframework .security .web .authentication .www .BasicAuthenticationFilter ;
55
+ import org .springframework .security .web .authentication .www .DigestAuthenticationFilter ;
56
+ import org .springframework .security .web .context .SecurityContextHolderFilter ;
57
+ import org .springframework .security .web .context .SecurityContextPersistenceFilter ;
58
+ import org .springframework .security .web .context .request .async .WebAsyncManagerIntegrationFilter ;
59
+ import org .springframework .security .web .csrf .CsrfFilter ;
60
+ import org .springframework .security .web .header .HeaderWriterFilter ;
61
+ import org .springframework .security .web .jaasapi .JaasApiIntegrationFilter ;
62
+ import org .springframework .security .web .savedrequest .RequestCacheAwareFilter ;
63
+ import org .springframework .security .web .servletapi .SecurityContextHolderAwareRequestFilter ;
64
+ import org .springframework .security .web .session .ConcurrentSessionFilter ;
65
+ import org .springframework .security .web .session .DisableEncodeUrlFilter ;
66
+ import org .springframework .security .web .session .ForceEagerSessionCreationFilter ;
67
+ import org .springframework .security .web .session .SessionManagementFilter ;
68
+ import org .springframework .util .Assert ;
69
+ import org .springframework .util .StringUtils ;
70
+ import org .springframework .web .filter .CorsFilter ;
39
71
40
72
/**
41
73
* A {@link org.springframework.security.web.FilterChainProxy.FilterChainDecorator} that
@@ -48,6 +80,12 @@ public final class ObservationFilterChainDecorator implements FilterChainProxy.F
48
80
49
81
private static final Log logger = LogFactory .getLog (FilterChainProxy .class );
50
82
83
+ private static final int OPENTELEMETRY_MAX_NAME_LENGTH = 63 ;
84
+
85
+ private static final int MAX_OBSERVATION_NAME_LENGTH = OPENTELEMETRY_MAX_NAME_LENGTH - ".before" .length ();
86
+
87
+ private static final Map <String , String > OBSERVATION_NAMES = new HashMap <>();
88
+
51
89
private static final String ATTRIBUTE = ObservationFilterChainDecorator .class + ".observation" ;
52
90
53
91
static final String UNSECURED_OBSERVATION_NAME = "spring.security.http.unsecured.requests" ;
@@ -98,10 +136,83 @@ private List<ObservationFilter> wrap(List<Filter> filters) {
98
136
return observableFilters ;
99
137
}
100
138
139
+ static {
140
+ registerName (DisableEncodeUrlFilter .class , "session.encode-url.disable" );
141
+ registerName (ForceEagerSessionCreationFilter .class , "session.create" );
142
+ registerName (ChannelProcessingFilter .class , "web.request.delivery.ensure" );
143
+ registerName (WebAsyncManagerIntegrationFilter .class , "web-async-manager.join.security-context" );
144
+ registerName (SecurityContextHolderFilter .class , "security-context.hold" );
145
+ registerName (SecurityContextPersistenceFilter .class , "security-context.persist" );
146
+ registerName (HeaderWriterFilter .class , "web.response.header.set" );
147
+ registerName (CorsFilter .class , "cors.process" );
148
+ registerName (CsrfFilter .class , "csrf.protect" );
149
+ registerName (LogoutFilter .class , "principal.logout" );
150
+ registerName ("org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter" ,
151
+ "web.request.oauth2.redirect" );
152
+ registerName (
153
+ "org.springframework.security.saml2.provider.service.web." + "Saml2WebSsoAuthenticationRequestFilter" ,
154
+ "web.request.saml2.redirect" );
155
+ registerName (X509AuthenticationFilter .class , "web.request.x509.auth" );
156
+ registerName (AbstractPreAuthenticatedProcessingFilter .class , "web.request.pre-auth.base.process" );
157
+ registerName ("org.springframework.security.cas.web.CasAuthenticationFilter" , "web.request.sas.auth" );
158
+ registerName ("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter" ,
159
+ "web.response.oauth2.process" );
160
+ registerName ("org.springframework.security.saml2.provider.service.web.authentication"
161
+ + ".Saml2WebSsoAuthenticationFilter" , "web.request.saml2.auth" );
162
+ registerName (UsernamePasswordAuthenticationFilter .class , "web.request.username-password.auth" );
163
+ registerName (DefaultLoginPageGeneratingFilter .class , "web.login-page.default.generate" );
164
+ registerName (DefaultLogoutPageGeneratingFilter .class , "web.logout-page.default.generate" );
165
+ registerName (ConcurrentSessionFilter .class , "session.refresh" );
166
+ registerName (DigestAuthenticationFilter .class , "web.request.digest.auth" );
167
+ registerName ("org.springframework.security.oauth2.server.resource.web.authentication."
168
+ + "BearerTokenAuthenticationFilter" , "web.request.bearer.auth" );
169
+ registerName (BasicAuthenticationFilter .class , "web.request.basic.auth" );
170
+ registerName (RequestCacheAwareFilter .class , "web.request.cache.extract" );
171
+ registerName (SecurityContextHolderAwareRequestFilter .class , "web.request.security.wrap" );
172
+ registerName (JaasApiIntegrationFilter .class , "web.request.jass.auth" );
173
+ registerName (RememberMeAuthenticationFilter .class , "web.request.remember-me.auth" );
174
+ registerName (AnonymousAuthenticationFilter .class , "web.request.anonymous.auth" );
175
+ registerName ("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter" ,
176
+ "web.response.oauth2.code-grant.process" );
177
+ registerName (SessionManagementFilter .class , "session.manage" );
178
+ registerName (ExceptionTranslationFilter .class , "exception.translate" );
179
+ registerName (FilterSecurityInterceptor .class , "web.response.security.intercept" );
180
+ registerName (AuthorizationFilter .class , "web.access.auth.restrict" );
181
+ registerName (SwitchUserFilter .class , "session.switch" );
182
+ }
183
+
184
+ public static void registerName (Class clazz , String name ) {
185
+ String keyName = clazz .getName ();
186
+ checkAlreadyRegistered (keyName );
187
+ OBSERVATION_NAMES .put (keyName , limitLength (name ));
188
+ }
189
+
190
+ public static void registerName (String className , String name ) {
191
+ checkAlreadyRegistered (className );
192
+ OBSERVATION_NAMES .put (className , name );
193
+ }
194
+
101
195
static AroundFilterObservation observation (HttpServletRequest request ) {
102
196
return (AroundFilterObservation ) request .getAttribute (ATTRIBUTE );
103
197
}
104
198
199
+ private static String getObservationName (String className ) {
200
+ if (OBSERVATION_NAMES .containsKey (className )) {
201
+ return OBSERVATION_NAMES .get (className );
202
+ }
203
+ throw new IllegalArgumentException ("Class not registered for observation: " + className );
204
+ }
205
+
206
+ private static String limitLength (String s ) {
207
+ Assert .isTrue (s .length () <= MAX_OBSERVATION_NAME_LENGTH ,
208
+ "The name must be less than MAX_OBSERVATION_NAME_LENGTH=" + MAX_OBSERVATION_NAME_LENGTH );
209
+ return s ;
210
+ }
211
+
212
+ private static void checkAlreadyRegistered (String keyName ) {
213
+ Assert .isTrue (!OBSERVATION_NAMES .containsKey (keyName ), "Observation name is registered already: " + keyName );
214
+ }
215
+
105
216
private static final class VirtualFilterChain implements FilterChain {
106
217
107
218
private final FilterChain originalChain ;
@@ -145,6 +256,8 @@ static final class ObservationFilter implements Filter {
145
256
146
257
private final String name ;
147
258
259
+ private final String observationName ;
260
+
148
261
private final int position ;
149
262
150
263
private final int size ;
@@ -155,12 +268,30 @@ static final class ObservationFilter implements Filter {
155
268
this .name = filter .getClass ().getSimpleName ();
156
269
this .position = position ;
157
270
this .size = size ;
271
+ String tempObservationName ;
272
+ try {
273
+ tempObservationName = ObservationFilterChainDecorator .getObservationName (filter .getClass ().getName ());
274
+ }
275
+ catch (IllegalArgumentException ex ) {
276
+ tempObservationName = compressName (this .name );
277
+ logger .warn (
278
+ "Class " + filter .getClass ().getName ()
279
+ + " is not registered for observation and will have name " + tempObservationName
280
+ + ". Please consider of registering this class with "
281
+ + ObservationFilterChainDecorator .class .getSimpleName () + ".registerName(class, name)." ,
282
+ ex );
283
+ }
284
+ this .observationName = tempObservationName ;
158
285
}
159
286
160
287
String getName () {
161
288
return this .name ;
162
289
}
163
290
291
+ String getObservationName () {
292
+ return this .observationName ;
293
+ }
294
+
164
295
@ Override
165
296
public void doFilter (ServletRequest request , ServletResponse response , FilterChain chain )
166
297
throws IOException , ServletException {
@@ -181,15 +312,17 @@ private void wrapFilter(ServletRequest request, ServletResponse response, Filter
181
312
parentBefore .setFilterName (this .name );
182
313
parentBefore .setChainPosition (this .position );
183
314
}
184
- parent .before ().event (Observation .Event .of (this .name + ".before" , "before " + this .name ));
315
+ parent .before ().event (Observation .Event .of (this .observationName + ".before" ,
316
+ "before " + this .name ));
185
317
this .filter .doFilter (request , response , chain );
186
318
parent .start ();
187
319
if (parent .after ().getContext () instanceof FilterChainObservationContext parentAfter ) {
188
320
parentAfter .setChainSize (this .size );
189
321
parentAfter .setFilterName (this .name );
190
322
parentAfter .setChainPosition (this .size - this .position + 1 );
191
323
}
192
- parent .after ().event (Observation .Event .of (this .name + ".after" , "after " + this .name ));
324
+ parent .after ().event (Observation .Event .of (this .observationName + ".after" ,
325
+ "after " + this .name ));
193
326
}
194
327
195
328
private AroundFilterObservation parent (HttpServletRequest request ) {
@@ -202,6 +335,24 @@ private AroundFilterObservation parent(HttpServletRequest request) {
202
335
return parent ;
203
336
}
204
337
338
+ private String compressName (String className ) {
339
+ if (className .length () >= MAX_OBSERVATION_NAME_LENGTH ) {
340
+ return maximalCompressClassName (className , MAX_OBSERVATION_NAME_LENGTH );
341
+ }
342
+ return className ;
343
+ }
344
+
345
+ private String maximalCompressClassName (String className , int maxLength ) {
346
+ String [] names = className .split ("(?=\\ p{Lu})" );
347
+ for (int j = 0 ; j < names .length ; j ++) {
348
+ final int maxPortionLength = maxLength / names .length ;
349
+ if (names [j ].length () > maxPortionLength ) {
350
+ names [j ] = names [j ].substring (0 , maxPortionLength );
351
+ }
352
+ }
353
+ return StringUtils .arrayToDelimitedString (names , "" );
354
+ }
355
+
205
356
}
206
357
207
358
interface AroundFilterObservation extends FilterObservation {
0 commit comments