[bs-15] Integrate audit abstraction into Spring Security setup

* By default all authentication events are passed onto the
audit listener
* Access denied exceptions are still not published by Spring
Security because of a bug in the Java config support

[Fixes #48155753]
This commit is contained in:
Dave Syer 2013-05-02 11:35:36 +01:00
parent a310a79909
commit 0a730beb2a
20 changed files with 344 additions and 67 deletions

View File

@ -22,8 +22,9 @@ production, and in other environments.
|Logging |Logback, Log4j or JDK | Whatever is on the classpath. Sensible defaults. |
|Database |HSQLDB or H2 | Per classpath, or define a DataSource to override |
|Externalized configuration | Properties or YAML | Support for Spring profiles. Bind automatically to @Bean. |
|Audit | Spring Security and Spring ApplicationEvent |Flexible abstraction with sensible defaults for security events |
|Validation | JSR-303 | |
|Management endpoints | Spring MVC | Health, basic metrics, request tracing, shutdown |
|Management endpoints | Spring MVC | Health, basic metrics, request tracing, shutdown, thread dumps |
|Error pages | Spring MVC | Sensible defaults based on exception and status code |
|JSON |Jackson 2 | |
|ORM |Spring Data JPA | If on the classpath |
@ -33,7 +34,7 @@ production, and in other environments.
# Getting Started
You will need Java (6 at least) and a build tool (Maven is what we use
below, but you are more than wecome to use gradle). These can be
below, but you are more than welcome to use gradle). These can be
downloaded or installed easily in most operating systems. FIXME:
short instructions for Mac and Linux.
@ -101,12 +102,17 @@ Then in another terminal
you if the application is running and healthy. `/varz` is the default
location for the metrics endpoint - it gives you basic counts and
response timing data by default but there are plenty of ways to
customize it.
customize it. You can also try `/trace` and `/dump` to get some
interesting information about how and what your app is doing.
What about the home page?
$ curl localhost:8080/
{"status": 404, "error": "Not Found", "message": "Not Found"}
That's OK, we haven't added any business content yet.
That's OK, we haven't added any business content yet. But it shows
that there are sensible defaults built in for rendering HTTP and
server-side errors.
## Adding a business endpoint
@ -146,7 +152,48 @@ and re-package:
$ curl localhost:8080/
{"message": "Hello World"}
# Add a database
# Adding security
If you add Spring Security java config to your runtime classpath you
will enable HTTP basic authentication by default on all the endpoints.
In the `pom.xml` it would look like this:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-javaconfig</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
(Spring Security java config is still work in progress so we have used
a snapshot. Beware of sudden changes. FIXME: update to full
release.)
Try it out:
$ curl localhost:8080/
{"status": 403, "error": "Forbidden", "message": "Access Denied"}
$ curl user:password@localhost:8080/
{"message": "Hello World"}
The default auto configuration has an in-memory user database with one
entry. If you want to extend or expand that, or point to a database
or directory server, you only need to provide a `@Bean` definition for
an `AuthenticationManager`, e.g. in your `SampleController`:
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return new AuthenticationBuilder().inMemoryAuthentication().withUser("client")
.password("secret").roles("USER").and().and().build();
}
Try it out:
$ curl user:password@localhost:8080/
{"status": 403, "error": "Forbidden", "message": "Access Denied"}
$ curl client:secret@localhost:8080/
{"message": "Hello World"}
# Adding a database
Just add `spring-jdbc` and an embedded database to your dependencies:

View File

@ -40,11 +40,6 @@
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
@ -65,10 +60,6 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-javaconfig</artifactId>

View File

@ -0,0 +1,59 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.autoconfigure.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.service.audit.AuditEventRepository;
import org.springframework.bootstrap.service.audit.InMemoryAuditEventRepository;
import org.springframework.bootstrap.service.audit.listener.AuditListener;
import org.springframework.bootstrap.service.security.AuthenticationAuditListener;
import org.springframework.bootstrap.service.security.AuthorizationAuditListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Dave Syer
*
*/
@Configuration
public class AuditConfiguration {
@Autowired(required = false)
private AuditEventRepository auditEventRepository = new InMemoryAuditEventRepository();
@Bean
@ConditionalOnMissingBean(AuditEventRepository.class)
public AuditEventRepository auditEventRepository() throws Exception {
return this.auditEventRepository;
}
@Bean
public AuditListener auditListener() throws Exception {
return new AuditListener(this.auditEventRepository);
}
@Bean
public AuthenticationAuditListener authenticationAuditListener() throws Exception {
return new AuthenticationAuditListener();
}
@Bean
public AuthorizationAuditListener authorizationAuditListener() throws Exception {
return new AuthorizationAuditListener();
}
}

View File

@ -27,8 +27,6 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.context.annotation.ConditionalOnBean;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
@ -81,7 +79,7 @@ public class MetricFilterConfiguration {
UrlPathHelper helper = new UrlPathHelper();
String suffix = helper.getPathWithinApplication(servletRequest);
int status = 999;
DateTime t0 = new DateTime();
long t0 = System.currentTimeMillis();
try {
chain.doFilter(request, response);
} finally {
@ -90,7 +88,7 @@ public class MetricFilterConfiguration {
} catch (Exception e) {
// ignore
}
set("response", suffix, new Duration(t0, new DateTime()).getMillis());
set("response", suffix, System.currentTimeMillis() - t0);
increment("status." + status, suffix);
}
}

View File

@ -24,28 +24,39 @@ import org.springframework.bootstrap.context.annotation.EnableConfigurationPrope
import org.springframework.bootstrap.service.properties.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.AuthenticationBuilder;
import org.springframework.security.config.annotation.web.EnableWebSecurity;
import org.springframework.security.config.annotation.web.ExpressionUrlAuthorizations;
import org.springframework.security.config.annotation.web.HttpConfigurator;
import org.springframework.security.config.annotation.web.SpringSecurityFilterChainBuilder.IgnoredRequestRegistry;
import org.springframework.security.config.annotation.web.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.WebSecurityConfigurerAdapter;
import org.springframework.stereotype.Component;
/**
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ EnableWebSecurity.class })
@ConditionalOnMissingBean({ WebSecurityConfiguration.class })
@EnableWebSecurity
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityConfiguration {
@Component
public static class WebSecurityAdapter extends WebSecurityConfigurerAdapter {
@Bean
@ConditionalOnMissingBean({ AuthenticationEventPublisher.class })
public AuthenticationEventPublisher authenticationEventPublisher() {
return new DefaultAuthenticationEventPublisher();
}
@Bean
public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() {
return new BoostrapWebSecurityConfigurerAdapter();
}
private static class BoostrapWebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {
@Value("${endpoints.healthz.path:/healthz}")
private String healthzPath = "/healthz";
@ -53,6 +64,9 @@ public class SecurityConfiguration {
@Autowired
private SecurityProperties security;
@Autowired
private AuthenticationEventPublisher authenticationEventPublisher;
@Override
protected void ignoredRequests(IgnoredRequestRegistry ignoredRequests) {
ignoredRequests.antMatchers(this.healthzPath);
@ -69,6 +83,17 @@ public class SecurityConfiguration {
if (this.security.isRequireSsl()) {
http.requiresChannel().antMatchers("/**").requiresSecure();
}
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
AuthenticationManager manager = super.authenticationManager();
if (manager instanceof ProviderManager) {
((ProviderManager) manager)
.setAuthenticationEventPublisher(this.authenticationEventPublisher);
}
return manager;
}
}
@ -84,4 +109,5 @@ public class SecurityConfiguration {
}
}
}

View File

@ -32,7 +32,6 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.joda.JodaModule;
/**
* {@link EnableAutoConfiguration Auto-configuration} for service apps.
@ -42,7 +41,7 @@ import com.fasterxml.jackson.datatype.joda.JodaModule;
@Configuration
@Import({ ManagementConfiguration.class, MetricConfiguration.class,
ServerConfiguration.class, SecurityConfiguration.class,
MetricFilterConfiguration.class })
MetricFilterConfiguration.class, AuditConfiguration.class })
public class ServiceAutoConfiguration extends WebMvcConfigurationSupport {
@Override
@ -51,7 +50,6 @@ public class ServiceAutoConfiguration extends WebMvcConfigurationSupport {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
jacksonConverter.getObjectMapper().registerModule(new JodaModule());
jacksonConverter.getObjectMapper().disable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}

View File

@ -24,8 +24,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.service.security.SecurityFilterPostProcessor;
import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
import org.springframework.bootstrap.service.trace.SecurityFilterPostProcessor;
import org.springframework.bootstrap.service.trace.TraceEndpoint;
import org.springframework.bootstrap.service.trace.TraceRepository;
import org.springframework.context.annotation.Bean;

View File

@ -90,4 +90,10 @@ public class AuditEvent {
return result;
}
@Override
public String toString() {
return "AuditEvent [timestamp=" + this.timestamp + ", principal="
+ this.principal + ", type=" + this.type + ", data=" + this.data + "]";
}
}

View File

@ -15,6 +15,8 @@
*/
package org.springframework.bootstrap.service.audit.listener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.bootstrap.service.audit.AuditEventRepository;
import org.springframework.context.ApplicationListener;
@ -24,6 +26,8 @@ import org.springframework.context.ApplicationListener;
*/
public class AuditListener implements ApplicationListener<AuditApplicationEvent> {
private static Log logger = LogFactory.getLog(AuditListener.class);
private final AuditEventRepository auditEventRepository;
public AuditListener(AuditEventRepository auditEventRepository) {
@ -32,6 +36,7 @@ public class AuditListener implements ApplicationListener<AuditApplicationEvent>
@Override
public void onApplicationEvent(AuditApplicationEvent event) {
logger.info(event.getAuditEvent());
this.auditEventRepository.add(event.getAuditEvent());
}

View File

@ -16,7 +16,7 @@
package org.springframework.bootstrap.service.metrics;
import org.joda.time.DateTime;
import java.util.Date;
/**
* @author Dave Syer
@ -35,17 +35,17 @@ public class DefaultCounterService implements CounterService {
@Override
public void increment(String metricName) {
this.counterRepository.increment(wrap(metricName), 1, new DateTime());
this.counterRepository.increment(wrap(metricName), 1, new Date());
}
@Override
public void decrement(String metricName) {
this.counterRepository.increment(wrap(metricName), -1, new DateTime());
this.counterRepository.increment(wrap(metricName), -1, new Date());
}
@Override
public void reset(String metricName) {
this.counterRepository.set(wrap(metricName), 0, new DateTime());
this.counterRepository.set(wrap(metricName), 0, new Date());
}
private String wrap(String metricName) {

View File

@ -16,7 +16,7 @@
package org.springframework.bootstrap.service.metrics;
import org.joda.time.DateTime;
import java.util.Date;
/**
* @author Dave Syer
@ -35,7 +35,7 @@ public class DefaultGaugeService implements GaugeService {
@Override
public void set(String metricName, double value) {
this.metricRepository.set(wrap(metricName), value, new DateTime());
this.metricRepository.set(wrap(metricName), value, new Date());
}
private String wrap(String metricName) {

View File

@ -18,11 +18,10 @@ package org.springframework.bootstrap.service.metrics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.joda.time.DateTime;
/**
* @author Dave Syer
*/
@ -31,27 +30,27 @@ public class InMemoryMetricRepository implements MetricRepository {
private ConcurrentMap<String, Measurement> metrics = new ConcurrentHashMap<String, Measurement>();
@Override
public void increment(String metricName, int amount, DateTime dateTime) {
public void increment(String metricName, int amount, Date timestamp) {
Measurement current = this.metrics.get(metricName);
if (current != null) {
Metric metric = current.getMetric();
this.metrics.replace(metricName, current,
new Measurement(dateTime, metric.increment(amount)));
new Measurement(timestamp, metric.increment(amount)));
} else {
this.metrics.putIfAbsent(metricName, new Measurement(dateTime, new Metric(
this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric(
metricName, amount)));
}
}
@Override
public void set(String metricName, double value, DateTime dateTime) {
public void set(String metricName, double value, Date timestamp) {
Measurement current = this.metrics.get(metricName);
if (current != null) {
Metric metric = current.getMetric();
this.metrics.replace(metricName, current,
new Measurement(dateTime, metric.set(value)));
new Measurement(timestamp, metric.set(value)));
} else {
this.metrics.putIfAbsent(metricName, new Measurement(dateTime, new Metric(
this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric(
metricName, value)));
}
}

View File

@ -16,24 +16,24 @@
package org.springframework.bootstrap.service.metrics;
import org.joda.time.DateTime;
import java.util.Date;
/**
* @author Dave Syer
*/
public class Measurement {
private DateTime dateTime;
private Date timestamp;
private Metric metric;
public Measurement(DateTime dateTime, Metric metric) {
this.dateTime = dateTime;
public Measurement(Date timestamp, Metric metric) {
this.timestamp = timestamp;
this.metric = metric;
}
public DateTime getDateTime() {
return this.dateTime;
public Date getTimestamp() {
return this.timestamp;
}
public Metric getMetric() {
@ -42,7 +42,7 @@ public class Measurement {
@Override
public String toString() {
return "Measurement [dateTime=" + this.dateTime + ", metric=" + this.metric + "]";
return "Measurement [dateTime=" + this.timestamp + ", metric=" + this.metric + "]";
}
@Override
@ -50,7 +50,7 @@ public class Measurement {
final int prime = 31;
int result = 1;
result = prime * result
+ ((this.dateTime == null) ? 0 : this.dateTime.hashCode());
+ ((this.timestamp == null) ? 0 : this.timestamp.hashCode());
result = prime * result + ((this.metric == null) ? 0 : this.metric.hashCode());
return result;
}
@ -64,10 +64,10 @@ public class Measurement {
if (getClass() != obj.getClass())
return false;
Measurement other = (Measurement) obj;
if (this.dateTime == null) {
if (other.dateTime != null)
if (this.timestamp == null) {
if (other.timestamp != null)
return false;
} else if (!this.dateTime.equals(other.dateTime))
} else if (!this.timestamp.equals(other.timestamp))
return false;
if (this.metric == null) {
if (other.metric != null)

View File

@ -17,17 +17,16 @@
package org.springframework.bootstrap.service.metrics;
import java.util.Collection;
import org.joda.time.DateTime;
import java.util.Date;
/**
* @author Dave Syer
*/
public interface MetricRepository {
void increment(String metricName, int amount, DateTime dateTime);
void increment(String metricName, int amount, Date timestamp);
void set(String metricName, double value, DateTime dateTime);
void set(String metricName, double value, Date timestamp);
void delete(String metricName);

View File

@ -0,0 +1,78 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.service.security;
import java.util.HashMap;
import java.util.Map;
import org.springframework.bootstrap.service.audit.AuditEvent;
import org.springframework.bootstrap.service.audit.listener.AuditApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.security.web.authentication.switchuser.AuthenticationSwitchUserEvent;
/**
* @author Dave Syer
*
*/
public class AuthenticationAuditListener implements
ApplicationListener<AbstractAuthenticationEvent>, ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public void onApplicationEvent(AbstractAuthenticationEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
if (event instanceof AbstractAuthenticationFailureEvent) {
data.put("type", ((AbstractAuthenticationFailureEvent) event).getException()
.getClass().getName());
data.put("message", ((AbstractAuthenticationFailureEvent) event)
.getException().getMessage());
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHENTICATION_FAILURE", data));
} else if (event instanceof AuthenticationSwitchUserEvent) {
if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails());
}
data.put("target", ((AuthenticationSwitchUserEvent) event).getTargetUser()
.getUsername());
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHENTICATION_SWITCH", data));
} else {
if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails());
}
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHENTICATION_SUCCESS", data));
}
}
private void publish(AuditEvent event) {
if (this.publisher != null) {
this.publisher.publishEvent(new AuditApplicationEvent(event));
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.service.security;
import java.util.HashMap;
import java.util.Map;
import org.springframework.bootstrap.service.audit.AuditEvent;
import org.springframework.bootstrap.service.audit.listener.AuditApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.access.event.AbstractAuthorizationEvent;
import org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent;
import org.springframework.security.access.event.AuthorizationFailureEvent;
/**
* @author Dave Syer
*
*/
public class AuthorizationAuditListener implements
ApplicationListener<AbstractAuthorizationEvent>, ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public void onApplicationEvent(AbstractAuthorizationEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
if (event instanceof AuthenticationCredentialsNotFoundEvent) {
data.put("type", ((AuthenticationCredentialsNotFoundEvent) event)
.getCredentialsNotFoundException().getClass().getName());
data.put("message", ((AuthenticationCredentialsNotFoundEvent) event)
.getCredentialsNotFoundException().getMessage());
publish(new AuditEvent("<unknown>", "AUTHENTICATION_FAILURE", data));
} else if (event instanceof AuthorizationFailureEvent) {
data.put("type", ((AuthorizationFailureEvent) event)
.getAccessDeniedException().getClass().getName());
data.put("message", ((AuthorizationFailureEvent) event)
.getAccessDeniedException().getMessage());
publish(new AuditEvent(((AuthorizationFailureEvent) event)
.getAuthentication().getName(), "AUTHORIZATION_FAILURE", data));
}
}
private void publish(AuditEvent event) {
if (this.publisher != null) {
this.publisher.publishEvent(new AuditApplicationEvent(event));
}
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.service.trace;
package org.springframework.bootstrap.service.security;
import java.io.IOException;
import java.util.Collections;
@ -35,6 +35,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
import org.springframework.bootstrap.service.trace.TraceRepository;
import org.springframework.core.Ordered;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;

View File

@ -17,11 +17,10 @@ package org.springframework.bootstrap.service.trace;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.joda.time.DateTime;
/**
* @author Dave Syer
*
@ -48,7 +47,7 @@ public class InMemoryTraceRepository implements TraceRepository {
@Override
public void add(Map<String, Object> map) {
Trace trace = new Trace(new DateTime(), map);
Trace trace = new Trace(new Date(), map);
synchronized (this.traces) {
while (this.traces.size() >= this.capacity) {
this.traces.remove(0);

View File

@ -15,27 +15,26 @@
*/
package org.springframework.bootstrap.service.trace;
import java.util.Date;
import java.util.Map;
import org.joda.time.DateTime;
/**
* @author Dave Syer
*
*/
public class Trace {
private DateTime timestamp;
private Date timestamp;
private Map<String, Object> info;
public Trace(DateTime timestamp, Map<String, Object> info) {
public Trace(Date timestamp, Map<String, Object> info) {
super();
this.timestamp = timestamp;
this.info = info;
}
public DateTime getTimestamp() {
public Date getTimestamp() {
return this.timestamp;
}

View File

@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.service.trace;
package org.springframework.bootstrap.service.security;
import java.util.Map;
import org.junit.Test;
import org.springframework.bootstrap.service.trace.SecurityFilterPostProcessor.WebRequestLoggingFilter;
import org.springframework.bootstrap.service.security.SecurityFilterPostProcessor;
import org.springframework.bootstrap.service.security.SecurityFilterPostProcessor.WebRequestLoggingFilter;
import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
import org.springframework.mock.web.MockHttpServletRequest;
import static org.junit.Assert.assertEquals;