From f26c514b88e3a19d9da0bfa21d4bf6b0cad027cc Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 10 Aug 2016 18:14:01 +0800 Subject: [PATCH] add consumer audit --- .../apollo/openapi/entity/ConsumerAudit.java | 110 ++++++++++++++++++ .../filter/ConsumerAuthenticationFilter.java | 6 +- .../repository/ConsumerAuditRepository.java | 11 ++ .../openapi/service/ConsumerService.java | 9 ++ .../openapi/util/ConsumerAuditUtil.java | 79 +++++++++++++ .../configuration/AuthConfiguration.java | 6 +- .../ConsumerAuthenticationFilterTest.java | 8 +- 7 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/entity/ConsumerAudit.java create mode 100644 apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/repository/ConsumerAuditRepository.java create mode 100644 apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/ConsumerAuditUtil.java diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/entity/ConsumerAudit.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/entity/ConsumerAudit.java new file mode 100644 index 000000000..6944ae7aa --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/entity/ConsumerAudit.java @@ -0,0 +1,110 @@ +package com.ctrip.framework.apollo.openapi.entity; + +import com.google.common.base.MoreObjects; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.PrePersist; +import javax.persistence.Table; + +/** + * @author Jason Song(song_s@ctrip.com) + */ +@Entity +@Table(name = "ConsumerAudit") +public class ConsumerAudit { + @Id + @GeneratedValue + @Column(name = "Id") + private long id; + + @Column(name = "ConsumerId", nullable = false) + private long consumerId; + + @Column(name = "Uri", nullable = false) + private String uri; + + @Column(name = "Method", nullable = false) + private String method; + + @Column(name = "DataChange_CreatedTime") + private Date dataChangeCreatedTime; + + @Column(name = "DataChange_LastTime") + private Date dataChangeLastModifiedTime; + + @PrePersist + protected void prePersist() { + if (this.dataChangeCreatedTime == null) { + this.dataChangeCreatedTime = new Date(); + } + if (this.dataChangeLastModifiedTime == null) { + dataChangeLastModifiedTime = this.dataChangeCreatedTime; + } + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getConsumerId() { + return consumerId; + } + + public void setConsumerId(long consumerId) { + this.consumerId = consumerId; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public Date getDataChangeCreatedTime() { + return dataChangeCreatedTime; + } + + public void setDataChangeCreatedTime(Date dataChangeCreatedTime) { + this.dataChangeCreatedTime = dataChangeCreatedTime; + } + + public Date getDataChangeLastModifiedTime() { + return dataChangeLastModifiedTime; + } + + public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) { + this.dataChangeLastModifiedTime = dataChangeLastModifiedTime; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .omitNullValues() + .add("id", id) + .add("consumerId", consumerId) + .add("uri", uri) + .add("method", method) + .add("dataChangeCreatedTime", dataChangeCreatedTime) + .add("dataChangeLastModifiedTime", dataChangeLastModifiedTime) + .toString(); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/filter/ConsumerAuthenticationFilter.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/filter/ConsumerAuthenticationFilter.java index 3b94a2996..adec13dcb 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/filter/ConsumerAuthenticationFilter.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/filter/ConsumerAuthenticationFilter.java @@ -1,5 +1,6 @@ package com.ctrip.framework.apollo.openapi.filter; +import com.ctrip.framework.apollo.openapi.util.ConsumerAuditUtil; import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; import java.io.IOException; @@ -18,9 +19,11 @@ import javax.servlet.http.HttpServletResponse; */ public class ConsumerAuthenticationFilter implements Filter { private ConsumerAuthUtil consumerAuthUtil; + private ConsumerAuditUtil consumerAuditUtil; - public ConsumerAuthenticationFilter(ConsumerAuthUtil consumerAuthUtil) { + public ConsumerAuthenticationFilter(ConsumerAuthUtil consumerAuthUtil, ConsumerAuditUtil consumerAuditUtil) { this.consumerAuthUtil = consumerAuthUtil; + this.consumerAuditUtil = consumerAuditUtil; } @Override @@ -44,6 +47,7 @@ public class ConsumerAuthenticationFilter implements Filter { } consumerAuthUtil.storeConsumerId(request, consumerId); + consumerAuditUtil.audit(request, consumerId); chain.doFilter(req, resp); } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/repository/ConsumerAuditRepository.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/repository/ConsumerAuditRepository.java new file mode 100644 index 000000000..c281659db --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/repository/ConsumerAuditRepository.java @@ -0,0 +1,11 @@ +package com.ctrip.framework.apollo.openapi.repository; + +import com.ctrip.framework.apollo.openapi.entity.ConsumerAudit; + +import org.springframework.data.repository.PagingAndSortingRepository; + +/** + * @author Jason Song(song_s@ctrip.com) + */ +public interface ConsumerAuditRepository extends PagingAndSortingRepository { +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java index 22dead926..b6bf44c90 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java @@ -7,7 +7,9 @@ import com.google.common.base.Strings; import com.google.common.hash.Hashing; import com.ctrip.framework.apollo.openapi.entity.Consumer; +import com.ctrip.framework.apollo.openapi.entity.ConsumerAudit; import com.ctrip.framework.apollo.openapi.entity.ConsumerToken; +import com.ctrip.framework.apollo.openapi.repository.ConsumerAuditRepository; import com.ctrip.framework.apollo.openapi.repository.ConsumerRepository; import com.ctrip.framework.apollo.openapi.repository.ConsumerTokenRepository; import com.ctrip.framework.apollo.portal.service.ServerConfigService; @@ -34,6 +36,8 @@ public class ConsumerService implements InitializingBean { @Autowired private ConsumerRepository consumerRepository; @Autowired + private ConsumerAuditRepository consumerAuditRepository; + @Autowired private ServerConfigService serverConfigService; private String consumerTokenSalt; @@ -71,6 +75,11 @@ public class ConsumerService implements InitializingBean { return consumerTokenRepository.save(entity); } + @Transactional + public void createConsumerAudits(Iterable consumerAudits) { + consumerAuditRepository.save(consumerAudits); + } + String generateConsumerToken(String consumerAppId, Date generationTime, String consumerTokenSalt) { return Hashing.sha1().hashString(KEY_JOINER.join(consumerAppId, TIMESTAMP_FORMAT.format diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/ConsumerAuditUtil.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/ConsumerAuditUtil.java new file mode 100644 index 000000000..7376c6710 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/ConsumerAuditUtil.java @@ -0,0 +1,79 @@ +package com.ctrip.framework.apollo.openapi.util; + +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.collect.Queues; + +import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory; +import com.ctrip.framework.apollo.openapi.entity.ConsumerAudit; +import com.ctrip.framework.apollo.openapi.service.ConsumerService; +import com.dianping.cat.Cat; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author Jason Song(song_s@ctrip.com) + */ +@Service +public class ConsumerAuditUtil implements InitializingBean { + private static final int CONSUMER_AUDIT_MAX_SIZE = 10000; + private BlockingQueue audits = Queues.newLinkedBlockingQueue(CONSUMER_AUDIT_MAX_SIZE); + private final ExecutorService auditExecutorService; + private final AtomicBoolean auditStopped; + + @Autowired + private ConsumerService consumerService; + + public ConsumerAuditUtil() { + auditExecutorService = Executors.newSingleThreadExecutor( + ApolloThreadFactory.create("ConsumerAuditUtil", true)); + auditStopped = new AtomicBoolean(false); + } + + public boolean audit(HttpServletRequest request, long consumerId) { + String uri = request.getRequestURI(); + if (!Strings.isNullOrEmpty(request.getQueryString())) { + uri += "?" + request.getQueryString(); + } + + ConsumerAudit consumerAudit = new ConsumerAudit(); + Date now = new Date(); + consumerAudit.setConsumerId(consumerId); + consumerAudit.setUri(uri); + consumerAudit.setMethod(request.getMethod()); + consumerAudit.setDataChangeCreatedTime(now); + consumerAudit.setDataChangeLastModifiedTime(now); + + //throw away audits if exceeds the max size + return this.audits.offer(consumerAudit); + } + + @Override + public void afterPropertiesSet() throws Exception { + auditExecutorService.submit(() -> { + while (!auditStopped.get() && !Thread.currentThread().isInterrupted()) { + List toAudit = Lists.newArrayList(); + try { + Queues.drain(audits, toAudit, 100, 5, TimeUnit.SECONDS); + if (!toAudit.isEmpty()) { + consumerService.createConsumerAudits(toAudit); + } + } catch (Throwable ex) { + Cat.logError(ex); + } + } + }); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/configuration/AuthConfiguration.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/configuration/AuthConfiguration.java index 2763bfd07..4c9401b7d 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/configuration/AuthConfiguration.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/configuration/AuthConfiguration.java @@ -3,6 +3,7 @@ package com.ctrip.framework.apollo.portal.configuration; import com.google.common.collect.Maps; import com.ctrip.framework.apollo.openapi.filter.ConsumerAuthenticationFilter; +import com.ctrip.framework.apollo.openapi.util.ConsumerAuditUtil; import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; import com.ctrip.framework.apollo.portal.auth.LogoutHandler; import com.ctrip.framework.apollo.portal.auth.SsoHeartbeatHandler; @@ -177,10 +178,11 @@ public class AuthConfiguration { static class DefaultAuthAutoConfiguration { @Bean - public FilterRegistrationBean openApiAuthenticationFilter(ConsumerAuthUtil consumerAuthUtil) { + public FilterRegistrationBean openApiAuthenticationFilter(ConsumerAuthUtil consumerAuthUtil, + ConsumerAuditUtil consumerAuditUtil) { FilterRegistrationBean openApiFilter = new FilterRegistrationBean(); - openApiFilter.setFilter(new ConsumerAuthenticationFilter(consumerAuthUtil)); + openApiFilter.setFilter(new ConsumerAuthenticationFilter(consumerAuthUtil, consumerAuditUtil)); openApiFilter.addUrlPatterns("/openapi/*"); return openApiFilter; diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/filter/ConsumerAuthenticationFilterTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/filter/ConsumerAuthenticationFilterTest.java index 1ceab098c..03b8a4adb 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/filter/ConsumerAuthenticationFilterTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/filter/ConsumerAuthenticationFilterTest.java @@ -1,5 +1,6 @@ package com.ctrip.framework.apollo.openapi.filter; +import com.ctrip.framework.apollo.openapi.util.ConsumerAuditUtil; import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; import org.junit.Before; @@ -12,7 +13,6 @@ import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import static org.junit.Assert.*; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -30,6 +30,8 @@ public class ConsumerAuthenticationFilterTest { @Mock private ConsumerAuthUtil consumerAuthUtil; @Mock + private ConsumerAuditUtil consumerAuditUtil; + @Mock private HttpServletRequest request; @Mock private HttpServletResponse response; @@ -38,7 +40,7 @@ public class ConsumerAuthenticationFilterTest { @Before public void setUp() throws Exception { - authenticationFilter = new ConsumerAuthenticationFilter(consumerAuthUtil); + authenticationFilter = new ConsumerAuthenticationFilter(consumerAuthUtil, consumerAuditUtil); } @Test @@ -52,6 +54,7 @@ public class ConsumerAuthenticationFilterTest { authenticationFilter.doFilter(request, response, filterChain); verify(consumerAuthUtil, times(1)).storeConsumerId(request, someConsumerId); + verify(consumerAuditUtil, times(1)).audit(request, someConsumerId); verify(filterChain, times(1)).doFilter(request, response); } @@ -66,6 +69,7 @@ public class ConsumerAuthenticationFilterTest { verify(response, times(1)).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED), anyString()); verify(consumerAuthUtil, never()).storeConsumerId(eq(request), anyLong()); + verify(consumerAuditUtil, never()).audit(eq(request), anyLong()); verify(filterChain, never()).doFilter(request, response); } } \ No newline at end of file -- GitLab