带有Spring Boot和Spring Cloud的Java微服务
朋友不允許朋友寫用戶身份驗(yàn)證。 厭倦了管理自己的用戶? 立即嘗試Okta的API和Java SDK。 在幾分鐘之內(nèi)即可對(duì)任何應(yīng)用程序中的用戶進(jìn)行身份驗(yàn)證,管理和保護(hù)。
Java是開發(fā)微服務(wù)架構(gòu)時(shí)使用的一種很棒的語言。 實(shí)際上,我們行業(yè)中的一些知名人士都在使用它。 您是否聽說過Netflix,Amazon或Google? 那eBay,Twitter和LinkedIn呢? 是的,處理不可思議流量的主要公司正在使用Java來實(shí)現(xiàn)。
在Java中實(shí)現(xiàn)微服務(wù)架構(gòu)并不適合每個(gè)人。 因此,通常不需要實(shí)現(xiàn)微服務(wù)。 大多數(shù)公司這樣做都是為了擴(kuò)展人員,而不是系統(tǒng)。 如果要擴(kuò)大人員規(guī)模,雇用Java開發(fā)人員是做到這一點(diǎn)的最佳方法之一。 畢竟,比起其他大多數(shù)語言,流利的Java開發(fā)人員更多-盡管JavaScript似乎正在Swift趕上來!
Java生態(tài)系統(tǒng)具有一些完善的模式來開發(fā)微服務(wù)架構(gòu)。 如果您熟悉Spring,則可以在家中使用Spring Boot和Spring Cloud進(jìn)行開發(fā)。 由于這是上手最快的方法之一,所以我認(rèn)為我將帶您快速入門。
使用Spring Cloud和Spring Boot創(chuàng)建Java微服務(wù)
在我的大多數(shù)教程中,我向您展示了如何從頭開始構(gòu)建所有內(nèi)容。 今天,我想采用一種不同的方法,并與您一起完成一個(gè)預(yù)先構(gòu)建的示例。 希望這會(huì)更短并且更容易理解。
您可以從克隆@ oktadeveloper / java-microservices-examples存儲(chǔ)庫開始。
git clone https://github.com/oktadeveloper/java-microservices-examples.git cd java-microservices-examples/spring-boot+cloud在spring-boot+cloud目錄中,有三個(gè)項(xiàng)目:
- Discovery-service :Netflix Eureka服務(wù)器,用于服務(wù)發(fā)現(xiàn)。
- car-service :一個(gè)簡單的Car Service,使用Spring Data REST來提供汽車的REST API。
- api-gateway :具有/cool-cars端點(diǎn)的API網(wǎng)關(guān),該端點(diǎn)與car-service進(jìn)行對(duì)話并過濾出不涼爽的汽車(當(dāng)然,在我看來)。
我使用start.spring.io的 REST API和HTTPie創(chuàng)建了所有這些應(yīng)用程序。
http https://start.spring.io/starter.zip javaVersion==11 \artifactId==discovery-service name==eureka-service \dependencies==cloud-eureka-server baseDir==discovery-service | tar -xzvf -http https://start.spring.io/starter.zip \artifactId==car-service name==car-service baseDir==car-service \dependencies==actuator,cloud-eureka,data-jpa,h2,data-rest,web,devtools,lombok | tar -xzvf -http https://start.spring.io/starter.zip \artifactId==api-gateway name==api-gateway baseDir==api-gateway \dependencies==cloud-eureka,cloud-feign,data-rest,web,cloud-hystrix,lombok | tar -xzvf -帶有Java 11+的Spring Microservices
為了使discovery-service在Java 11上運(yùn)行,我必須添加對(duì)JAXB的依賴
<dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId> </dependency>其他兩個(gè)應(yīng)用程序可以在Java 11上開箱即用地正常運(yùn)行,而無需更改依賴項(xiàng)。
Netflix Eureka的Java服務(wù)發(fā)現(xiàn)
discovery-service的配置與大多數(shù)Eureka服務(wù)器相同。 它在其主類和設(shè)置其端口并關(guān)閉發(fā)現(xiàn)的屬性上作為@EnableEurekaServer批注。
server.port=8761 eureka.client.register-with-eureka=false car-service和api-gateway項(xiàng)目以類似的方式配置。 兩者都定義了唯一的名稱,并且car-service配置為在端口8090上運(yùn)行,因此它與8080不沖突。
汽車服務(wù)/src/main/resources/application.properties
api-gateway / src / main / resources / application.properties
spring.application.name=api-gateway兩個(gè)項(xiàng)目中的主類都使用@EnableDiscoveryClient注釋。
使用Spring Data REST構(gòu)建Java微服務(wù)
car-service提供了REST API,可讓您CRUD(創(chuàng)建,讀取,更新和刪除)汽車。 當(dāng)應(yīng)用程序使用ApplicationRunner bean加載時(shí),它將創(chuàng)建一組默認(rèn)的汽車。
car-service / src / main / java / com / example / carservice / CarServiceApplication.java
API網(wǎng)關(guān)中的Spring Cloud + Feign和Hystrix
Feign使編寫Java HTTP客戶端更加容易。 Spring Cloud使得僅需幾行代碼即可創(chuàng)建Feign客戶端。 Hystrix可以為您的Feign客戶端添加故障轉(zhuǎn)移功能,從而使它們更具彈性。
api-gateway使用Feign和Hystrix與下游car-service并在不可用時(shí)故障轉(zhuǎn)移到fallback()方法。 它還公開了一個(gè)/cool-cars終結(jié)點(diǎn),該終結(jié)點(diǎn)過濾掉了您可能不想擁有的汽車。
api-gateway / src / main / java / com / example / apigateway / ApiGatewayApplication.java
運(yùn)行Java微服務(wù)架構(gòu)
如果您在單獨(dú)的終端窗口中使用./mvnw運(yùn)行所有這些服務(wù), ./mvnw可以導(dǎo)航至http://localhost:8761并查看它們是否已在Eureka中注冊(cè)。
如果您在瀏覽器中導(dǎo)航到http://localhost:8080/cool-bars ,您將被重定向到Okta。 什么啊
使用OAuth 2.0和OIDC保護(hù)Java微服務(wù)
我已經(jīng)使用OAuth 2.0和OIDC在此微服務(wù)架構(gòu)中配置了安全性。 兩者有什么區(qū)別? OIDC是提供身份的OAuth 2.0的擴(kuò)展。 它還提供發(fā)現(xiàn)功能,因此可以從單個(gè)URL(稱為issuer )發(fā)現(xiàn)所有不同的OAuth 2.0端點(diǎn)。
如何為所有這些微服務(wù)配置安全性? 我很高興你問!
我在api-gateway和car-service的pom.xml中添加了Okta的Spring Boot啟動(dòng)器:
<dependency><groupId>com.okta.spring</groupId><artifactId>okta-spring-boot-starter</artifactId><version>1.2.0</version> </dependency>然后,我在Okta創(chuàng)建了一個(gè)新的OIDC應(yīng)用,并配置了授權(quán)代碼流。 如果要查看運(yùn)行中的所有內(nèi)容,則需要完成以下步驟。
在Okta中創(chuàng)建Web應(yīng)用程序
登錄到您的1563開發(fā)者帳戶(或者注冊(cè) ,如果你沒有一個(gè)帳戶)。
將發(fā)行者(位于API > 授權(quán)服務(wù)器下 ),客戶端ID和客戶端密鑰復(fù)制到兩個(gè)項(xiàng)目的application.properties中。
okta.oauth2.issuer=$issuer okta.oauth2.client-id=$clientId okta.oauth2.client-secret=$clientSecret下一節(jié)中的Java代碼已經(jīng)存在,但是我認(rèn)為我已經(jīng)對(duì)其進(jìn)行了解釋,以便您了解正在發(fā)生的事情。
為OAuth 2.0登錄和資源服務(wù)器配置Spring Security
在ApiGatewayApplication.java ,我添加了Spring Security配置以啟用OAuth 2.0登錄并將網(wǎng)關(guān)作為資源服務(wù)器。
@Configuration static class OktaOAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {// @formatter:offhttp.authorizeRequests().anyRequest().authenticated().and().oauth2Login().and().oauth2ResourceServer().jwt();// @formatter:on} }在此示例中未使用資源服務(wù)器配置,但是我添加了它,以防您想將移動(dòng)應(yīng)用程序或SPA連接到此網(wǎng)關(guān)。 如果您使用的是SPA,則還需要添加一個(gè)bean來配置CORS。
@Bean public FilterRegistrationBean<CorsFilter> simpleCorsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration config = new CorsConfiguration();config.setAllowCredentials(true);config.setAllowedOrigins(Collections.singletonList("*"));config.setAllowedMethods(Collections.singletonList("*"));config.setAllowedHeaders(Collections.singletonList("*"));source.registerCorsConfiguration("/**", config);FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));bean.setOrder(Ordered.HIGHEST_PRECEDENCE);return bean; }如果確實(shí)使用像這樣的CORS過濾器,建議您將源,方法和標(biāo)頭更改為更具體,以提高安全性。
CarServiceApplication.java僅配置為資源服務(wù)器,因?yàn)椴幌M苯釉L問它。
@Configuration static class OktaOAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {// @formatter:offhttp.authorizeRequests().anyRequest().authenticated().and().oauth2ResourceServer().jwt();// @formatter:on} } 為了使API網(wǎng)關(guān)能夠訪問Car Service,我在API網(wǎng)關(guān)項(xiàng)目中創(chuàng)建了UserFeignClientInterceptor.java 。
> api-gateway / src / main / java / com / example / apigateway / UserFeignClientInterceptor.java
我在ApiGatewayApplication.java其配置為RequestInterceptor :
@Bean public RequestInterceptor getUserFeignClientInterceptor(OAuth2AuthorizedClientService clientService) {return new UserFeignClientInterceptor(clientService); }并且,我在api-gateway/src/main/resources/application.properties添加了兩個(gè)屬性,因此Feign支持Spring Security。
feign.hystrix.enabled=true hystrix.shareSecurityContext=true請(qǐng)參閱在啟用安全性的情況下運(yùn)行的Java微服務(wù)
使用./mvnw在單獨(dú)的終端窗口中運(yùn)行所有應(yīng)用程序,或者根據(jù)需要在您的IDE中運(yùn)行。
為了簡化在IDE中的運(yùn)行,根目錄中有一個(gè)聚合器pom.xml 。 如果安裝了IntelliJ IDEA的命令行啟動(dòng)器 , idea pom.xml需要運(yùn)行idea pom.xml 。
導(dǎo)航到http://localhost:8080/cool-cars ,您將被重定向到Okta進(jìn)行登錄。
輸入您的Okta開發(fā)人員帳戶的用戶名和密碼,您應(yīng)該會(huì)看到?jīng)鏊钠嚽鍐巍?
如果您到此為止并成功運(yùn)行了示例應(yīng)用程序,那么恭喜! 你真酷! 😎
使用Netflix Zuul和Spring Cloud代理路由
Netflix Zuul是您可能希望在微服務(wù)體系結(jié)構(gòu)中使用的另一個(gè)便捷功能。 Zuul是一項(xiàng)網(wǎng)關(guān)服務(wù),可提供動(dòng)態(tài)路由,監(jiān)視,彈性等。
為了添加Zuul,我將其作為依賴項(xiàng)添加到api-gateway/pom.xml :
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>然后,我將@EnableZuulProxy添加到ApiGatewayApplication類。
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;@EnableZuulProxy @SpringBootApplication public class ApiGatewayApplication {... }為了將訪問令牌傳遞到代理路由,我創(chuàng)建了一個(gè)AuthorizationHeaderFilter類,該類擴(kuò)展了ZuulFilter 。
package com.example.apigateway;import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.springframework.core.Ordered; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.oidc.OidcUserInfo;import java.util.Optional;import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;public class AuthorizationHeaderFilter extends ZuulFilter {private final OAuth2AuthorizedClientService clientService;public AuthorizationHeaderFilter(OAuth2AuthorizedClientService clientService) {this.clientService = clientService;}@Overridepublic String filterType() {return PRE_TYPE;}@Overridepublic int filterOrder() {return Ordered.LOWEST_PRECEDENCE;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() {RequestContext ctx = RequestContext.getCurrentContext();Optional<String> authorizationHeader = getAuthorizationHeader();authorizationHeader.ifPresent(s -> ctx.addZuulRequestHeader("Authorization", s));return null;}private Optional<String> getAuthorizationHeader() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;OAuth2AuthorizedClient client = clientService.loadAuthorizedClient(oauthToken.getAuthorizedClientRegistrationId(),oauthToken.getName());OAuth2AccessToken accessToken = client.getAccessToken();if (accessToken == null) {return Optional.empty();} else {String tokenType = accessToken.getTokenType().getValue();String authorizationHeaderValue = String.format("%s %s", tokenType, accessToken.getTokenValue());return Optional.of(authorizationHeaderValue);}} }您可能會(huì)注意到, getAuthorizationHeader()方法中的代碼與UserFeignClientInterceptor的代碼非常相似。 由于只有幾行,因此我選擇不將其移至實(shí)用程序類。 Feign攔截器用于@FeignClient ,而Zuul過濾器用于Zuul代理的請(qǐng)求。
為了使Spring Boot和Zuul知道此過濾器,我在主應(yīng)用程序類中將其注冊(cè)為Bean。
public AuthorizationHeaderFilter authHeaderFilter(OAuth2AuthorizedClientService clientService) {return new AuthorizationHeaderFilter(clientService); }為了將請(qǐng)求從API網(wǎng)關(guān)代理到汽車服務(wù),我添加了到api-gateway/src/main/resources/application.properties路由。
zuul.routes.car-service.path=/cars zuul.routes.car-service.url=http://localhost:8090zuul.routes.home.path=/home zuul.routes.home.url=http://localhost:8090zuul.sensitive-headers=Cookie,Set-Cookie我在/home路線的car-service項(xiàng)目中添加了HomeController 。
package com.example.carservice;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import java.security.Principal;@RestController public class HomeController {private final static Logger log = LoggerFactory.getLogger(HomeController.class);@GetMapping("/home")public String howdy(Principal principal) {String username = principal.getName();JwtAuthenticationToken token = (JwtAuthenticationToken) principal;log.info("claims: " + token.getTokenAttributes());return "Hello, " + username;} }確認(rèn)您的Zuul路線工作
由于這些更改已經(jīng)在您克隆的項(xiàng)目中,因此您應(yīng)該能夠在瀏覽器中查看https://localhost:8080/cars和http://localhost:8080/home 。
Spring Cloud Config呢?
在此示例中您可能已經(jīng)注意到的一件事是,您必須在每個(gè)應(yīng)用程序中配置OIDC屬性。 如果您有500個(gè)微服務(wù),這可能是一個(gè)真正的痛苦。 是的,您可以將它們定義為環(huán)境變量,這樣就可以解決問題。 但是,如果您具有使用不同OIDC客戶端ID的不同微服務(wù)堆棧,則此方法將很困難。
Spring Cloud Config是一個(gè)為分布式系統(tǒng)提供外部化配置的項(xiàng)目。 與其在本示例中添加它,不如在以后的教程中介紹它。
那Kotlin呢?
我用Java寫這篇文章是因?yàn)樗荍ava生態(tài)系統(tǒng)中最受歡迎的語言。 然而,根據(jù)RedMonk從2019年1月開始的編程語言排名, Kotlin呈上升趨勢 。
至少在本季度,Kotlin大幅增長,而所有三個(gè)基于JVM的同行均下降。 實(shí)際上,Kotlin跳了這么遠(yuǎn),以至于它最終進(jìn)入了第20名的前20名,并在此同時(shí)超越了Clojure(#24)和Groovy(#24)。 它仍然遠(yuǎn)遠(yuǎn)落后于Scala(#13),但在這些排名的歷史上,Kotlin的增長僅次于Swift,因此很有趣的發(fā)現(xiàn)下一輪還是接下來的比賽。
Spring對(duì)Kotlin有很好的支持,您可以在start.spring.io上選擇它作為語言。 如果您希望我們使用Kotlin寫更多帖子,請(qǐng)?jiān)谠u(píng)論中告訴我們!
刷新令牌的已知問題
默認(rèn)情況下,Okta的訪問令牌在一小時(shí)后過期。 這是預(yù)期的,并且在使用OAuth 2.0時(shí)建議使用短期訪問令牌。 刷新令牌的壽命通常更長(思考幾天或幾個(gè)月),并且可用于獲取新的訪問令牌。 使用Okta的Spring Boot啟動(dòng)器時(shí),這應(yīng)該會(huì)自動(dòng)發(fā)生,但事實(shí)并非如此。
我配置了Okta組織,使其訪問令牌在五分鐘內(nèi)到期。 您可以通過以下方法執(zhí)行此操作:轉(zhuǎn)到“ API” >“ 授權(quán)服務(wù)器” >“ 訪問策略” ,單擊“ 默認(rèn)策略” ,然后編輯其規(guī)則。 然后將訪問令牌的生存期從1小時(shí)更改為5分鐘。
在瀏覽器中點(diǎn)擊http://localhost:8080/cool-cars ,您將被重定向到Okta進(jìn)行登錄。 登錄后,您應(yīng)該會(huì)看到汽車的JSON字符串。
去做其他事情超過5分鐘。
回來,刷新瀏覽器,您會(huì)看到[]而不是所有的汽車。
我仍在努力解決此問題,一旦找到我將更新此帖子。 如果您碰巧知道解決方案,請(qǐng)告訴我!
更新: Spring Security 5.1尚未自動(dòng)刷新OAuth訪問令牌。 它應(yīng)該在Spring Security 5.2中可用 。
通過Spring Boot,Spring Cloud和微服務(wù)獲得更多樂趣
我希望您喜歡這個(gè)關(guān)于如何使用Spring Boot和Spring Cloud構(gòu)建Java微服務(wù)架構(gòu)的教程。 您學(xué)習(xí)了如何以最少的代碼構(gòu)建所有內(nèi)容,然后使用Spring Security,OAuth 2.0和Okta將其配置為安全。
您可以在GitHub上找到本教程中顯示的所有代碼。
我們是此博客上的Spring Boot,Spring Cloud和微服務(wù)的忠實(shí)擁護(hù)者。 這是您可能會(huì)發(fā)現(xiàn)有趣的其他幾篇文章:
- 帶有Spring Cloud Config和JHipster的Java微服務(wù)
- Angular 8 + Spring Boot 2.2:立即構(gòu)建一個(gè)CRUD應(yīng)用程序!
- Spring Boot登錄選項(xiàng)快速指南
- 使用Spring Boot和Kubernetes構(gòu)建微服務(wù)架構(gòu)
- 使用HTTPS和OAuth 2.0保護(hù)服務(wù)到服務(wù)的Spring微服務(wù)
- 構(gòu)建Spring微服務(wù)并對(duì)其進(jìn)行Dockerize生產(chǎn)
請(qǐng)?jiān)赥witter @oktadev上關(guān)注我們,并訂閱我們的YouTube頻道,以了解更多有關(guān)Spring Boot和微服務(wù)的知識(shí)。
“帶有Spring Boot和Spring Cloud的Java微服務(wù)”最初于2019年5月22日發(fā)布在Okta開發(fā)者博客上
朋友不允許朋友寫用戶身份驗(yàn)證。 厭倦了管理自己的用戶? 立即嘗試Okta的API和Java SDK。 在幾分鐘之內(nèi)即可對(duì)任何應(yīng)用程序中的用戶進(jìn)行身份驗(yàn)證,管理和保護(hù)。
翻譯自: https://www.javacodegeeks.com/2019/06/java-microservices-spring-boot-spring-cloud.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的带有Spring Boot和Spring Cloud的Java微服务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 反射是最重要的Java API
- 下一篇: singleton 类_在Java中对S