使用Spring Boot Actuator监视Java应用程序
朋友不允許朋友寫用戶身份驗證。 厭倦了管理自己的用戶? 立即嘗試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;} }為httpTrace , principal , requestBody和responseBody添加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应用程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: camel 调用soap_使用Apach
- 下一篇: 中国备案中心官网(全球备案中心)