朋友不允许朋友写用户身份验证。 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK。 在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。

您是否曾与Spring Boot Actuator合作? 这是一个非常有用的库,可帮助您监视应用程序的运行状况以及与应用程序的交互-非常适合投入生产! Spring Boot Actuator包含一个内置端点,该端点用于跟踪对您的应用程序的HTTP调用-对于监视OpenID Connect(OIDC)请求非常有用-但不幸的是,默认实现不跟踪主体内容。 在这篇文章中,我将向您展示如何扩展httptrace端点以捕获内容并跟踪OIDC流。

让我们开始吧!

使用Spring Initializr和Okta创建一个OpenID Connect应用程序

您可以使用出色的Spring Initializr网站或API通过Okta集成创建示例OIDC应用程序:

curl https://start.spring.io/starter.zip \dependencies==web,okta \packageName==com.okta.developer.demo -d

但是,在运行OIDC应用程序之前,您将需要一个Okta帐户。 Okta是一项开发人员服务,可为您处理存储用户帐户和实施用户管理(包括OIDC)。 继续并注册一个免费的开发者帐户以继续。

登录到Okta帐户后,转到仪表板,然后转到“ 应用程序”部分。 添加一个新的Web应用程序,然后在“常规”部分中获取客户端凭据: 客户端ID客户端密钥

您还将需要颁发者 ,它也是组织URL,您可以在仪表板主页的右上角找到它。 注意 :默认情况下,内置的Everyone Okta组已分配给该应用程序,因此Okta组织中的任何用户都可以对其进行身份验证。

使用您的客户ID,客户密码。 然后在适当的位置发行人,通过在命令行中传递凭据来启动您的应用程序:

OKTA_OAUTH2_REDIRECTURI=/authorization-code/callback \
OKTA_OAUTH2_ISSUER=<issuer>/oauth2 \
OKTA_OAUTH2_CLIENT_ID=<client id> \
OKTA_OAUTH2_CLIENT_SECRET=<client secret> \
./mvnw spring-boot:run

将测试控制器添加到Spring Boot App

最好添加一个简单的控制器来测试身份验证流程。 默认情况下,仅允许经过身份验证的用户访问。

@Controller
@RequestMapping(value = "/hello")
public class HelloController {@GetMapping(value = "/greeting")@ResponseBodypublic String getGreeting(Principal user) {return "Good morning " + user.getName();}
}

您可以通过重新启动应用程序并浏览到/ hello / greeting来进行测试 。

添加Spring Boot Actuator依赖关系

通过将启动器Maven依赖项添加到pom.xml file来启用Spring Boot Actuator:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

要启用httptrace端点,请编辑src/main/resources/application.properties并添加以下行:

management.endpoints.web.exposure.include=info,health,httptrace

您可以运行应用程序并浏览到/ hello / greeting并登录,以测试现成的执行器功能。

在自动配置下,Spring Security过滤器的优先级高于httptrace执行器添加的过滤器。

这意味着默认情况下仅跟踪经过身份验证的呼叫。 我们将在此处对此进行更改,但是现在,您可以在/ actuator / httptrace中看到跟踪的内容 。 响应应类似于以下JSON有效负载:

{"traces":[{"timestamp":"2019-05-19T05:38:42.726Z","principal":{"name":"***"},"session":{"id":"***"},"request":{"method":"GET","uri":"http://localhost:8080/","headers":{},"remoteAddress":"0:0:0:0:0:0:0:1"},"response":{"status":200,"headers":{}},"timeTaken":145}]
}

将自定义HTTP跟踪添加到您的Spring Boot应用程序

HTTP跟踪不是很灵活。 httptrace执行器的作者Andy Wilkinson建议,如果需要进行身体跟踪,请实现自己的端点 。

另外,通过一些自定义过滤器,我们无需进行大量工作即可增强基本实现。 在以下各节中,我将向您展示如何:

  • 创建一个过滤器以捕获请求和响应正文
  • 配置过滤器优先级以跟踪OIDC调用
  • 使用自定义跟踪存储库创建httptrace端点扩展以存储其他数据

使用Spring Boot Actuator捕获请求和响应正文内容

接下来,创建一个用于跟踪请求和响应正文内容的过滤器。 此过滤器将优先于httptrace过滤器,因此当执行器保存跟踪时,缓存的正文内容可用。

@Component
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
public class ContentTraceFilter extends OncePerRequestFilter {private ContentTraceManager traceManager;@Value("${management.trace.http.tracebody:false}")private boolean traceBody;public ContentTraceFilter(ContentTraceManager traceManager) {super();this.traceManager = traceManager;}@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {if (!isRequestValid(request) || !traceBody) {filterChain.doFilter(request, response);return;}ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request, 1000);ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response);try {filterChain.doFilter(wrappedRequest, wrappedResponse);traceManager.updateBody(wrappedRequest, wrappedResponse);} finally {wrappedResponse.copyBodyToResponse();}}private boolean isRequestValid(HttpServletRequest request) {try {new URI(request.getRequestURL().toString());return true;} catch (URISyntaxException ex) {return false;}}}

注意对ContentTraceManager的调用,它是一个简单的@RequestScope bean,它将存储其他数据:

@Component
@RequestScope
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
public class ContentTraceManager {private ContentTrace trace;public ContentTraceManager(ContentTrace trace) {this.trace=trace;}protected static Logger logger = LoggerFactory.getLogger(ContentTraceManager.class);public void updateBody(ContentCachingRequestWrapper wrappedRequest,ContentCachingResponseWrapper wrappedResponse) {String requestBody = getRequestBody(wrappedRequest);getTrace().setRequestBody(requestBody);String responseBody = getResponseBody(wrappedResponse);getTrace().setResponseBody(responseBody);}protected String getRequestBody(ContentCachingRequestWrapper wrappedRequest) {try {if (wrappedRequest.getContentLength() <= 0) {return null;}return new String(wrappedRequest.getContentAsByteArray(), 0,wrappedRequest.getContentLength(),wrappedRequest.getCharacterEncoding());} catch (UnsupportedEncodingException e) {logger.error("Could not read cached request body: " + e.getMessage());return null;}}protected String getResponseBody(ContentCachingResponseWrapper wrappedResponse) {try {if (wrappedResponse.getContentSize() <= 0) {return null;}return new String(wrappedResponse.getContentAsByteArray(), 0,wrappedResponse.getContentSize(),wrappedResponse.getCharacterEncoding());} catch (UnsupportedEncodingException e) {logger.error("Could not read cached response body: " + e.getMessage());return null;}}public ContentTrace getTrace() {if (trace == null) {trace = new ContentTrace();}return trace;}
}

为了使用附加数据对跟踪建模,请使用内置的HttpTrace信息组成一个自定义ContentTrace类,并添加用于存储正文内容的属性。

public class ContentTrace {protected HttpTrace httpTrace;protected String requestBody;protected String responseBody;protected Authentication principal;public ContentTrace() {}public void setHttpTrace(HttpTrace httpTrace) {this.httpTrace = httpTrace;}
}

httpTraceprincipalrequestBodyresponseBody添加setter和getter。

配置过滤器优先级

为了捕获对应用程序中OIDC端点的请求,跟踪过滤器必须位于Spring Security过滤器之前。 只要ContentTraceFilter优先级高于HttpTraceFilter ,那么两者都可以放在SecurityContextPersistenceFilter之前或之后,后者是Spring Security过滤器链中的第一个。

@Configuration
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {private HttpTraceFilter httpTraceFilter;private ContentTraceFilter contentTraceFilter;public WebSecurityConfig(HttpTraceFilter httpTraceFilter, ContentTraceFilter contentTraceFilter) {this.httpTraceFilter = httpTraceFilter;this.contentTraceFilter = contentTraceFilter;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(contentTraceFilter,SecurityContextPersistenceFilter.class).addFilterAfter(httpTraceFilter,SecurityContextPersistenceFilter.class).authorizeRequests().anyRequest().authenticated().and().oauth2Client().and().oauth2Login();}
}

跟踪经过身份验证的用户

我们将在Spring Security过滤器链之前安装跟踪过滤器。 这意味着当HttpTraceFilter保存跟踪时,主体不再可用。 我们可以使用新的过滤器和ContentTraceManager还原此跟踪数据。

@Component
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
public class PrincipalTraceFilter extends OncePerRequestFilter {private ContentTraceManager traceManager;private HttpTraceProperties traceProperties;public PrincipalTraceFilter(ContentTraceManager traceManager,HttpTraceProperties traceProperties) {super();this.traceManager = traceManager;this.traceProperties = traceProperties;}@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain)throws ServletException, IOException {if (!isRequestValid(request)) {filterChain.doFilter(request, response);return;}try {filterChain.doFilter(request, response);} finally {if (traceProperties.getInclude().contains(Include.PRINCIPAL)) {traceManager.updatePrincipal();}}}private boolean isRequestValid(HttpServletRequest request) {try {new URI(request.getRequestURL().toString());return true;} catch (URISyntaxException ex) {return false;}}}

添加缺少的ContentTraceManager类以更新主体:

public class ContentTraceManager {public void updatePrincipal() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null) {getTrace().setPrincipal(authentication);}}
}

PrincipalTraceFilter优先级必须低于Spring Security过滤器链的优先级,因此从安全上下文请求身份验证的主体时可用。 修改WebSecurityConfig以将过滤器插入到WebSecurityConfig的最后一个过滤器FilterSecurityInterceptor之后。

@Configuration
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {private HttpTraceFilter httpTraceFilter;private ContentTraceFilter contentTraceFilter;private PrincipalTraceFilter principalTraceFilter;public WebSecurityConfig(HttpTraceFilter httpTraceFilter,ContentTraceFilter contentTraceFilter,PrincipalTraceFilter principalTraceFilter) {super();this.httpTraceFilter = httpTraceFilter;this.contentTraceFilter = contentTraceFilter;this.principalTraceFilter = principalTraceFilter;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(contentTraceFilter,SecurityContextPersistenceFilter.class).addFilterAfter(httpTraceFilter,SecurityContextPersistenceFilter.class).addFilterAfter(principalTraceFilter,FilterSecurityInterceptor.class).authorizeRequests().anyRequest().authenticated().and().oauth2Client().and().oauth2Login();}
}

HTTPTrace端点扩展

最后,使用@EndpointWebExtension批注定义端点增强。 实现CustomHttpTraceRepository以存储和检索带有其他数据的ContentTrace

@Component
@EndpointWebExtension(endpoint = HttpTraceEndpoint.class)
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
public class HttpTraceEndpointExtension {private CustomHttpTraceRepository repository;public HttpTraceEndpointExtension(CustomHttpTraceRepository repository) {super();this.repository = repository;}@ReadOperationpublic ContentTraceDescriptor contents() {List<ContentTrace> traces = repository.findAllWithContent();return new ContentTraceDescriptor(traces);}
}

重新定义端点返回类型的描述符:

public class ContentTraceDescriptor {protected List<ContentTrace> traces;public ContentTraceDescriptor(List<ContentTrace> traces) {super();this.traces = traces;}public List<ContentTrace> getTraces() {return traces;}public void setTraces(List<ContentTrace> traces) {this.traces = traces;}}

创建CustomHttpTraceRepository实现HttpTraceRepository接口:

@Component
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
public class CustomHttpTraceRepository implements HttpTraceRepository {private final List<ContentTrace> contents = new LinkedList<>();private ContentTraceManager traceManager;public CustomHttpTraceRepository(ContentTraceManager traceManager) {super();this.traceManager = traceManager;}@Overridepublic void add(HttpTrace trace) {synchronized (this.contents) {ContentTrace contentTrace = traceManager.getTrace();contentTrace.setHttpTrace(trace);this.contents.add(0, contentTrace);}}@Overridepublic List<HttpTrace> findAll() {synchronized (this.contents) {return contents.stream().map(ContentTrace::getHttpTrace).collect(Collectors.toList());}}public List<ContentTrace> findAllWithContent() {synchronized (this.contents) {return Collections.unmodifiableList(new ArrayList<>(this.contents));}}}

检查OpenID Connect HTTP跟踪

通过添加以下行来修改application.properties文件以跟踪所有可用数据:

management.trace.http.include=request-headers,response-headers,cookie-headers,principal,time-taken,authorization-header,remote-address,session-id

再次运行该应用程序,然后调用安全控制器/ hello / greeting 。 针对Okta进行身份验证,然后检查/ actuator / httptrace中的跟踪 。

现在,您应该在跟踪中看到OIDC调用以及请求和响应内容。 例如,在下面的跟踪中,对应用程序授权端点的请求将重定向到Okta授权服务器,从而启动OIDC授权代码流。

{"httpTrace": {"timestamp": "2019-05-22T00:52:22.383Z","principal": null,"session": {"id": "C2174F5E5F85B313B2284639EE4016E7"},"request": {"method": "GET","uri": "http://localhost:8080/oauth2/authorization/okta","headers": {"cookie": ["JSESSIONID=C2174F5E5F85B313B2284639EE4016E7"],"accept-language": ["en-US,en;q=0.9"],"upgrade-insecure-requests": ["1"],"host": ["localhost:8080"],"connection": ["keep-alive"],"accept-encoding": ["gzip, deflate, br"],"accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"],"user-agent": ["Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"]},"remoteAddress": "0:0:0:0:0:0:0:1"},"response": {"status": 302,"headers": {"X-Frame-Options": ["DENY"],"Cache-Control": ["no-cache, no-store, max-age=0, must-revalidate"],"X-Content-Type-Options": ["nosniff"],"Expires": ["0"],"Pragma": ["no-cache"],"X-XSS-Protection": ["1; mode=block"],"Location": ["https://dev-239352.okta.com/oauth2/default/v1/authorize?response_type=code&client_id=0oalrp4qx3Do43VyI356&scope=openid%20profile%20email&state=1uzHRyaHVmyKcpb7eAvJVrdJTZ6wTgkPv3fsC14qdOk%3D&redirect_uri=http://localhost:8080/authorization-code/callback"]}},"timeTaken": 9},"requestBody": null,"responseBody": null
}

这篇文章中的所有代码都可以在okta-spring-boot-custom-actuator-example存储库的GitHub上找到。

学到更多

这里的所有都是它的! 您刚刚了解了如何配置和扩展httptrace执行器端点以监视OIDC应用程序。 有关Spring Boot Actuator,常规Spring Boot或用户身份验证的更多信息,请查看以下链接:

  • 带有Spring Boot和Spring Cloud的Java微服务
  • 弹簧启动执行器端点
  • 实施自定义端点
  • Okta身份验证快速入门指南Java Spring

与往常一样,如果您对此信息有任何意见或疑问,请在下面发表评论。 将来不要错过Twitter和YouTube上的任何精彩内容。

“使用Spring Boot Actuator监视Java应用程序”最初于2019年7月17日发布在Okta Developer博客上。

朋友不允许朋友写用户身份验证。 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK。 在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。

翻译自: https://www.javacodegeeks.com/2019/09/monitor-your-java-apps-spring-boot-actuator.html

使用Spring Boot Actuator监视Java应用程序相关推荐

  1. actuator的原理_使用Spring Boot Actuator监视Java应用程序

    actuator的原理 朋友不允许朋友写用户身份验证. 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK. 数分钟之内即可在任何应用程序中对用户进行身份验证,管理和保护. 您是否曾与 ...

  2. java局部网内通话杂音_在Spring Boot反应式Web应用程序上启用SSL,并在控制台中对该打印进行http调用时出现异常噪音...

    在我创建了我的spring boot反应式Web应用程序以支持SSL之后,当我尝试对服务器进行http调用时,它会在控制台中的异常跟踪下面打印 . 作为应用程序所有者,我无法阻止任何人使用我的服务 . ...

  3. 服务监控 Spring Boot Actuator 介绍

    服务监控 Spring Boot Actuator 介绍 1. 概述 在本文中,我们将介绍Spring Boot Actuator.首先介绍一些Actuator的基础知识,然后详细讨论Spring B ...

  4. Spring Boot Actuator与Spring Boot Admin详解

    原版文档: 文档:Spring Boot Actuator.note 链接:http://note.youdao.com/noteshare?id=fca965ad3f7ae3f79260d09056 ...

  5. Spring boot——Actuator 详解

    一.什么是 Actuator Spring Boot Actuator 模块提供了生产级别的功能,比如健康检查,审计,指标收集,HTTP 跟踪等,帮助我们监控和管理Spring Boot 应用. 这个 ...

  6. spring boot actuator监控详细介绍一(超级详细)

    spring boot actuator介绍 Spring Boot包含许多其他功能,可帮助您在将应用程序推送到生产环境时监视和管理应用程序. 您可以选择使用HTTP端点或JMX来管理和监视应用程序. ...

  7. 朱晔和你聊Spring系列S1E7:简单好用的Spring Boot Actuator

    本文会来看一下Spring Boot Actuator提供给我们的监控端点Endpoint.健康检查Health和打点指标Metrics等所谓的Production-ready(生产环境必要的一些)功 ...

  8. Spring Boot Actuator:在其顶部具有MVC层的自定义端点

    Spring Boot Actuator端点允许您监视应用程序并与之交互. Spring Boot包含许多内置端点,您也可以添加自己的端点. 添加自定义端点就像创建一个从org.springframe ...

  9. Spring Boot Actuator:自定义端点,其顶部具有MVC层

    Spring Boot Actuator端点允许您监视应用程序并与之交互. Spring Boot包含许多内置端点,您也可以添加自己的端点. 添加自定义端点就像创建一个从org.springframe ...

最新文章

  1. 打通Devops的Scrum敏捷工具
  2. 从零开始用Python实现k近邻算法(附代码、数据集)
  3. 在代码中设置RelativeLayout布局中标签的android:layout_toLeftOf、android:layout_toRightOf等属性...
  4. observeOn()与subscribeOn()的详解
  5. 如何获取文件的完整路径?
  6. 深入理解和使用Oracle中with as语句以及与增删改查的结合使用
  7. 启动项目后,FileItemFactory 错误
  8. 机器学习笔记(十三):降维
  9. python中global的使用_PYTHON中使用GLOBAL引发的一系列问题
  10. 香港中文大学(深圳)吴保元教授课题组博士后招聘
  11. opencv fast角检测
  12. SpringMVC访问流程
  13. RabbitMQ 概念
  14. Java23种设计模式(一)
  15. steamcommunity 302占用端口
  16. 计算机电缆检测报告,计算机用屏蔽双绞线DJYPVP-2*2*1.0mm²
  17. vector vector int的使用
  18. 【电脑使用】Windows 10账户那些事儿
  19. js分享到微信朋友圈、QQ空间、QQ好友、新浪微博、腾讯微博、豆瓣、人人......
  20. 在UE4中改变枢轴位置

热门文章

  1. Codeforces 1176F
  2. 30、JAVA_WEB开发基础之servlet(1)
  3. Hadoop入门(三)HDFS API
  4. Git使用教程:最详细、最傻瓜、最浅显、真正手把手教
  5. 探讨JS合并两个数组的方法
  6. 使用cardme读写VCard文件,实现批量导入导出电话簿
  7. 你胆敢不加break试试?
  8. hibernate在分层架构中修改数据(update)时遇到的问题!!
  9. JDBC登录功能实现
  10. getOrDefault()和subList()