日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

通过Java Hipster升级Spring Security OAuth和JUnit测试

發(fā)布時(shí)間:2023/12/3 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 通过Java Hipster升级Spring Security OAuth和JUnit测试 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

“我喜歡編寫身份驗(yàn)證和授權(quán)代碼。” ?從來(lái)沒(méi)有Java開發(fā)人員。 厭倦了一次又一次地建立相同的登錄屏幕? 嘗試使用Okta API進(jìn)行托管身份驗(yàn)證,授權(quán)和多因素身份驗(yàn)證。

使用單元測(cè)試和集成測(cè)試來(lái)驗(yàn)證代碼質(zhì)量是一種很好的方式來(lái)表明您對(duì)代碼的關(guān)心。 我最近在受歡迎的JHipster開源項(xiàng)目中做了很多工作,以將其升級(jí)為使用最新版本的Spring Security。

Spring Security 5.1+將OAuth 2.0和OIDC添加為一流公民,您可以使用其優(yōu)雅的DSL(又稱酷方法鏈接,又稱生成器模式)對(duì)其進(jìn)行配置。 自從Rob Winch和工作人員首次啟動(dòng)它以來(lái),我一直有使用它的動(dòng)力。 與他們合作進(jìn)行非常創(chuàng)新的項(xiàng)目很有趣。 Spring Security使OAuth很棒!

我在2017年秋天向JHipster添加了OAuth 2.0支持。這種經(jīng)歷對(duì)我產(chǎn)生了很大的影響。 我學(xué)到了很多有關(guān)Keycloak,Docker Compose以及如何在身份提供者(IdP)之間切換的知識(shí)。

我花了最后一個(gè)月升級(jí)JHipster以使用Spring Security 5.1(Spring Boot 2.1中的默認(rèn)設(shè)置)。 在此過(guò)程中,我遇到了一些挫折,在Travis CI上搖了搖拳,當(dāng)我想出解決方案時(shí)感到很高興。 在此過(guò)程中,我也學(xué)到了很多東西。 今天,我將與您分享這些經(jīng)驗(yàn)。

使用OAuth 2.0和OIDC注銷

在JHipster中集成了對(duì)Keycloak和Okta的支持后不久,該項(xiàng)目收到了很多用戶的抱怨,他們無(wú)法注銷。 JHipster用戶熟悉單擊注銷 (檢查最新信息)并完全注銷。 使用默認(rèn)的Spring Security支持,用戶將注銷本地應(yīng)用程序,而不是IdP。

我花了一年的時(shí)間,但終于在今年早些時(shí)候添加了全球SSO注銷 。 Keycloak和Okta都要求您將GET請(qǐng)求發(fā)送到具有ID令牌和重定向到的URL的端點(diǎn)。 因此,我創(chuàng)建了一個(gè)LogoutResource來(lái)返回這些值。

@RestController public class LogoutResource {private final Logger log = LoggerFactory.getLogger(LogoutResource.class);private final UserInfoRestTemplateFactory templateFactory;private final String accessTokenUri;public LogoutResource(UserInfoRestTemplateFactory templateFactory,@Value("${security.oauth2.client.access-token-uri}") String accessTokenUri) {this.templateFactory = templateFactory;this.accessTokenUri = accessTokenUri;}/*** POST /api/logout : logout the current user** @return the ResponseEntity with status 200 (OK) and a body with a global logout URL and ID token*/@PostMapping("/api/logout")public ResponseEntity<?> logout(HttpServletRequest request, Authentication authentication) {log.debug("REST request to logout User : {}", authentication);OAuth2RestTemplate oauth2RestTemplate = this.templateFactory.getUserInfoRestTemplate();String idToken = (String) oauth2RestTemplate.getAccessToken().getAdditionalInformation().get("id_token");String logoutUrl = accessTokenUri.replace("token", "logout");Map<String, String> logoutDetails = new HashMap<>();logoutDetails.put("logoutUrl", logoutUrl);logoutDetails.put("idToken", idToken);request.getSession().invalidate();return ResponseEntity.ok().body(logoutDetails);} }

Angular客戶端調(diào)用/api/logout端點(diǎn)并構(gòu)造IdP注銷URL。

this.authServerProvider.logout().subscribe(response => {const data = response.body;let logoutUrl = data.logoutUrl;// if Keycloak, uri has protocol/openid-connect/tokenif (logoutUrl.indexOf('/protocol') > -1) {logoutUrl = logoutUrl + '?redirect_uri=' + window.location.origin;} else {// OktalogoutUrl = logoutUrl + '?id_token_hint=' +data.idToken + '&post_logout_redirect_uri=' + window.location.origin;}window.location.href = logoutUrl; });

測(cè)試LogoutResource非常簡(jiǎn)單。 大部分工作涉及模擬UserInfoRestTemplateFactory以便它返回ID令牌。

@RunWith(SpringRunner.class) @SpringBootTest(classes = JhipsterApp.class) public class LogoutResourceIntTest {@Autowiredprivate MappingJackson2HttpMessageConverter jacksonMessageConverter;private final static String ID_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" +".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsIm" +"p0aSI6ImQzNWRmMTRkLTA5ZjYtNDhmZi04YTkzLTdjNmYwMzM5MzE1OSIsImlhdCI6MTU0M" +"Tk3MTU4MywiZXhwIjoxNTQxOTc1MTgzfQ.QaQOarmV8xEUYV7yvWzX3cUE_4W1luMcWCwpr" +"oqqUrg";@Value("${security.oauth2.client.access-token-uri}")private String accessTokenUri;private MockMvc restLogoutMockMvc;@Beforepublic void before() {LogoutResource logoutResource = new LogoutResource(restTemplateFactory(), accessTokenUri);this.restLogoutMockMvc = MockMvcBuilders.standaloneSetup(logoutResource).setMessageConverters(jacksonMessageConverter).build();}@Testpublic void getLogoutInformation() throws Exception {String logoutUrl = accessTokenUri.replace("token", "logout");restLogoutMockMvc.perform(post("/api/logout")).andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)).andExpect(jsonPath("$.logoutUrl").value(logoutUrl)).andExpect(jsonPath("$.idToken").value(ID_TOKEN));}private UserInfoRestTemplateFactory restTemplateFactory() {UserInfoRestTemplateFactory factory = mock(UserInfoRestTemplateFactory.class);Map<String, Object> idToken = new HashMap<>();idToken.put("id_token", ID_TOKEN);DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken("my-fun-token");token.setAdditionalInformation(idToken);when(factory.getUserInfoRestTemplate()).thenReturn(mock(OAuth2RestTemplate.class));when(factory.getUserInfoRestTemplate().getAccessToken()).thenReturn(token);return factory;} }

我在1月下旬將全局注銷支持合并到JHipster的master分支中,并在幾周后開始升級(jí)Spring Security的OIDC支持。

升級(jí)Spring Security的OIDC支持
我從創(chuàng)建問(wèn)題#9276開始,以跟蹤我的目標(biāo),動(dòng)機(jī)和已知問(wèn)題。

在這一點(diǎn)上,如果您不熟悉Spring Security,您可能想知道:為什么升級(jí)到Spring Security的最新版本如此酷? 長(zhǎng)話短說(shuō):它們已經(jīng)棄用了注釋,增加了功能,并使將OAuth 2.0和OIDC集成到您的應(yīng)用程序中變得更加容易。 謝謝,Spring Security團(tuán)隊(duì)!

在Spring Boot 2.1+(即Spring Security 5.1+)中,不再建議使用@ EnableOAuth2Sso和@EnableResourceServer。 更改的原因可以在2019年1月25日發(fā)布的Josh Long的Bootiful Podcast中找到。這是Madhura Bhave的訪談,討論從21:30開始。

除了將所有Java代碼和YAML配置轉(zhuǎn)換為使用最新的Spring Security比特之外,我還決定默認(rèn)情況下將每個(gè)JHipster應(yīng)用程序都配置為資源服務(wù)器。 這是JHipster的SecurityConfiguration.java.ejs模板中的邏輯:

@Override public void configure(HttpSecurity http) throws Exception {// @formatter:offhttp...<%_ } else if (authenticationType === 'oauth2') { _%><%_ if (['monolith', 'gateway'].includes(applicationType)) { _%>.and().oauth2Login()<%_ } _%>.and().oauth2ResourceServer().jwt();<%_ } _%>// @formatter:on} }

為了確保實(shí)現(xiàn)與OIDC兼容,我用進(jìn)行觀眾驗(yàn)證的JwtDecoder bean覆蓋了默認(rèn)的JwtDecoder bean。

@Value("${spring.security.oauth2.client.provider.oidc.issuer-uri}") private String issuerUri;@Bean JwtDecoder jwtDecoder() {NimbusJwtDecoderJwkSupport jwtDecoder = (NimbusJwtDecoderJwkSupport)JwtDecoders.fromOidcIssuerLocation(issuerUri);OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator();OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);jwtDecoder.setJwtValidator(withAudience);return jwtDecoder; }

在所有運(yùn)行時(shí)代碼正常工作之后,我開始進(jìn)行重構(gòu)測(cè)試。 測(cè)試是重構(gòu)成功的最可靠指標(biāo),尤其是對(duì)于像JHipster這樣具有26,000個(gè)組合的項(xiàng)目而言!

在此過(guò)程中,我遇到了許多挑戰(zhàn)。 由于我學(xué)到了很多解決這些挑戰(zhàn)的知識(shí),所以我認(rèn)為對(duì)它們進(jìn)行說(shuō)明以及如何解決它們會(huì)很有趣。

如何模擬具有ID令牌的AuthenticatedPrincipal

我遇到的第一個(gè)挑戰(zhàn)是更新的LogoutResource 。 下面是我將其重構(gòu)為使用Spring Security的ClientRegistrationRepository之后的代碼。

@RestController public class LogoutResource {private ClientRegistration registration;public LogoutResource(ClientRegistrationRepository registrations) {this.registration = registrations.findByRegistrationId("oidc");}/*** {@code POST /api/logout} : logout the current user.** @param request the {@link HttpServletRequest}.* @param idToken the ID token.* @return the {@link ResponseEntity} with status {@code 200 (OK)} and a body with a global logout URL and ID token.*/@PostMapping("/api/logout")public ResponseEntity<?> logout(HttpServletRequest request,@AuthenticationPrincipal(expression = "idToken") OidcIdToken idToken) {String logoutUrl = this.registration.getProviderDetails().getConfigurationMetadata().get("end_session_endpoint").toString();Map<String, String> logoutDetails = new HashMap<>();logoutDetails.put("logoutUrl", logoutUrl);logoutDetails.put("idToken", idToken.getTokenValue());request.getSession().invalidate();return ResponseEntity.ok().body(logoutDetails);} }

我試圖在LogoutResourceIT.java模擬OAuth2AuthenticationToken ,認(rèn)為這將導(dǎo)致AuthenticationPrincipal的填充。

@RunWith(SpringRunner.class) @SpringBootTest(classes = JhipsterApp.class) public class LogoutResourceIT {@Autowiredprivate ClientRegistrationRepository registrations;@Autowiredprivate MappingJackson2HttpMessageConverter jacksonMessageConverter;private final static String ID_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" +".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsIm" +"p0aSI6ImQzNWRmMTRkLTA5ZjYtNDhmZi04YTkzLTdjNmYwMzM5MzE1OSIsImlhdCI6MTU0M" +"Tk3MTU4MywiZXhwIjoxNTQxOTc1MTgzfQ.QaQOarmV8xEUYV7yvWzX3cUE_4W1luMcWCwpr" +"oqqUrg";private MockMvc restLogoutMockMvc;@Beforepublic void before() {LogoutResource logoutResource = new LogoutResource(registrations);this.restLogoutMockMvc = MockMvcBuilders.standaloneSetup(logoutResource).setMessageConverters(jacksonMessageConverter).build();}@Testpublic void getLogoutInformation() throws Exception {Map<String, Object> claims = new HashMap<>();claims.put("groups", "ROLE_USER");claims.put("sub", 123);OidcIdToken idToken = new OidcIdToken(ID_TOKEN, Instant.now(),Instant.now().plusSeconds(60), claims);String logoutUrl = this.registrations.findByRegistrationId("oidc").getProviderDetails().getConfigurationMetadata().get("end_session_endpoint").toString();restLogoutMockMvc.perform(post("/api/logout").with(authentication(createMockOAuth2AuthenticationToken(idToken)))).andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)).andExpect(jsonPath("$.logoutUrl").value(logoutUrl));}private OAuth2AuthenticationToken createMockOAuth2AuthenticationToken(OidcIdToken idToken) {Collection<GrantedAuthority> authorities = new ArrayList<>();authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.USER));OidcUser user = new DefaultOidcUser(authorities, idToken);return new OAuth2AuthenticationToken(user, authorities, "oidc");} }

但是,這導(dǎo)致以下錯(cuò)誤:

Caused by: java.lang.IllegalArgumentException: tokenValue cannot be emptyat org.springframework.util.Assert.hasText(Assert.java:284)at org.springframework.security.oauth2.core.AbstractOAuth2Token.<init>(AbstractOAuth2Token.java:55)at org.springframework.security.oauth2.core.oidc.OidcIdToken.<init>(OidcIdToken.java:53)at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:172)

我將此問(wèn)題發(fā)布到Stack Overflow上,并且還向Spring Security團(tuán)隊(duì)發(fā)送了電子郵件。 Joe Grandja對(duì)此問(wèn)題做出了回應(yīng)。

AuthenticationPrincipalArgumentResolver未在測(cè)試中注冊(cè)。
啟用“完整” spring-web-mvc時(shí),它將自動(dòng)注冊(cè),例如@EnableWebMvc 。

但是,在您的@Before ,您具有:

MockMvcBuilders.standaloneSetup() –這不會(huì)初始化完整的web-mvc基礎(chǔ)結(jié)構(gòu)–只是一個(gè)子集。

嘗試以下方法:
MockMvcBuilders.webAppContextSetup(this.context) –這將注冊(cè)AuthenticationPrincipalArgumentResolver并且您的測(cè)試應(yīng)解析OidcIdToken 。

喬是正確的。 我將測(cè)試更改為以下內(nèi)容,并通過(guò)了測(cè)試。 ?

@RunWith(SpringRunner.class) @SpringBootTest(classes = JhipsterApp.class) public class LogoutResourceIT {@Autowiredprivate ClientRegistrationRepository registrations;@Autowiredprivate WebApplicationContext context;private final static String ID_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" +".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsIm" +"p0aSI6ImQzNWRmMTRkLTA5ZjYtNDhmZi04YTkzLTdjNmYwMzM5MzE1OSIsImlhdCI6MTU0M" +"Tk3MTU4MywiZXhwIjoxNTQxOTc1MTgzfQ.QaQOarmV8xEUYV7yvWzX3cUE_4W1luMcWCwpr" +"oqqUrg";private MockMvc restLogoutMockMvc;@Beforepublic void before() throws Exception {Map<String, Object> claims = new HashMap<>();claims.put("groups", "ROLE_USER");claims.put("sub", 123);OidcIdToken idToken = new OidcIdToken(ID_TOKEN, Instant.now(),Instant.now().plusSeconds(60), claims);SecurityContextHolder.getContext().setAuthentication(authenticationToken(idToken));SecurityContextHolderAwareRequestFilter authInjector = new SecurityContextHolderAwareRequestFilter();authInjector.afterPropertiesSet();this.restLogoutMockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();}@Testpublic void getLogoutInformation() throws Exception {String logoutUrl = this.registrations.findByRegistrationId("oidc").getProviderDetails().getConfigurationMetadata().get("end_session_endpoint").toString();restLogoutMockMvc.perform(post("/api/logout")).andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)).andExpect(jsonPath("$.logoutUrl").value(logoutUrl)).andExpect(jsonPath("$.idToken").value(ID_TOKEN));}private OAuth2AuthenticationToken authenticationToken(OidcIdToken idToken) {Collection<GrantedAuthority> authorities = new ArrayList<>();authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.USER));OidcUser user = new DefaultOidcUser(authorities, idToken);return new OAuth2AuthenticationToken(user, authorities, "oidc");} }

正確測(cè)試注銷功能是一個(gè)重要的里程碑。 我繼續(xù)升級(jí)JHipster的微服務(wù)架構(gòu)。

如何使用Zuul將OAuth 2.0訪問(wèn)令牌傳遞給下游微服務(wù)

JHipster使用Netflix Zuul代理從網(wǎng)關(guān)到下游微服務(wù)的請(qǐng)求。 我創(chuàng)建了一個(gè)AuthorizationHeaderFilter來(lái)處理訪問(wèn)令牌傳播。

public class AuthorizationHeaderFilter extends ZuulFilter {private final AuthorizationHeaderUtil headerUtil;public AuthorizationHeaderFilter(AuthorizationHeaderUtil headerUtil) {this.headerUtil = headerUtil;}@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 = headerUtil.getAuthorizationHeader();authorizationHeader.ifPresent(s -> ctx.addZuulRequestHeader(TokenRelayRequestInterceptor.AUTHORIZATION, s));return null;} }

但是,添加它不會(huì)導(dǎo)致成功的訪問(wèn)令牌傳播。 在Jon Ruddell的幫助下 ,我發(fā)現(xiàn)這是因?yàn)镴Hipster有一個(gè)LazyInitBeanFactoryPostProcessor導(dǎo)致所有bean都被延遲加載。 該ZuulFilterInitializer中包括ZuulFilterInitializer 。 將ZuulFilterInitializer為熱切加載的bean,可以使一切正常工作。

至此,我一切正常,因此我創(chuàng)建了一個(gè)pull請(qǐng)求來(lái)升級(jí)JHipster的模板 。

我知道我簽入的內(nèi)容需要運(yùn)行Keycloak才能通過(guò)集成測(cè)試。 這是由于OIDC發(fā)現(xiàn)以及如何從.well-known/openid-configuration查找端點(diǎn)。

在Spring Boot集成測(cè)試中如何處理OIDC發(fā)現(xiàn)

我不太擔(dān)心Keycloak是否需要運(yùn)行才能通過(guò)集成測(cè)試。 然后,我們的某些Azure和Travis構(gòu)建開始失敗。 JHipster開發(fā)人員指出,當(dāng)Keycloak不運(yùn)行時(shí),他們會(huì)看到類似以下的錯(cuò)誤。

Factory method 'clientRegistrationRepository' threw exception; nested exception is java.lang.IllegalArgumentException: Unable to resolve the OpenID Configuration with the provided Issuer of "http://localhost:9080/auth/realms/jhipster"

我通過(guò)Spring Security的OAuth和OIDC測(cè)試進(jìn)行了一些摸索,并提出了一個(gè)解決方案 。 該修復(fù)程序涉及添加一個(gè)TestSecurityConfiguration類,該類將覆蓋默認(rèn)的Spring Security設(shè)置并模擬Bean,從而不會(huì)發(fā)生OIDC發(fā)現(xiàn)。

@TestConfiguration public class TestSecurityConfiguration {private final ClientRegistration clientRegistration;public TestSecurityConfiguration() {this.clientRegistration = clientRegistration().build();}@BeanClientRegistrationRepository clientRegistrationRepository() {return new InMemoryClientRegistrationRepository(clientRegistration);}private ClientRegistration.Builder clientRegistration() {Map<String, Object> metadata = new HashMap<>();metadata.put("end_session_endpoint", "https://jhipster.org/logout");return ClientRegistration.withRegistrationId("oidc").redirectUriTemplate("{baseUrl}/{action}/oauth2/code/{registrationId}").clientAuthenticationMethod(ClientAuthenticationMethod.BASIC).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).scope("read:user").authorizationUri("https://jhipster.org/login/oauth/authorize").tokenUri("https://jhipster.org/login/oauth/access_token").jwkSetUri("https://jhipster.org/oauth/jwk").userInfoUri("https://api.jhipster.org/user").providerConfigurationMetadata(metadata).userNameAttributeName("id").clientName("Client Name").clientId("client-id").clientSecret("client-secret");}@BeanJwtDecoder jwtDecoder() {return mock(JwtDecoder.class);}@Beanpublic OAuth2AuthorizedClientService authorizedClientService(ClientRegistrationRepository clientRegistrationRepository) {return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);}@Beanpublic OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService authorizedClientService) {return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);} }

然后,在使用@SpringBootTest類中,我將其配置為配置源。

@SpringBootTest(classes = {MicroApp.class, TestSecurityConfiguration.class})

在使用OAuth 2.0保護(hù)的JHipster微服務(wù)上運(yùn)行端到端測(cè)試

最終問(wèn)題很快就浮出水面了。 jhipster-daily-builds (在Azure DevOps上運(yùn)行)在嘗試測(cè)試微服務(wù)時(shí)失敗。

Caused by: java.lang.IllegalArgumentException: Unable to resolve the OpenID Configurationwith the provided Issuer of "http://localhost:9080/auth/realms/jhipster"

我們不包括用于微服務(wù)的Keycloak Docker Compose文件,因?yàn)槲覀儾幌M鼈儶?dú)立運(yùn)行。 它們需要網(wǎng)關(guān)才能訪問(wèn)它們,因此它們的OAuth 2.0設(shè)置應(yīng)與您的網(wǎng)關(guān)匹配,并且網(wǎng)關(guān)項(xiàng)目包含Keycloak文件。

在Azure上運(yùn)行的端到端測(cè)試,其中1)啟動(dòng)微服務(wù),以及2)達(dá)到其運(yùn)行狀況終結(jié)點(diǎn)以確保其成功啟動(dòng)。 為了解決此問(wèn)題, Pascal Grimaud 禁用了啟動(dòng)/測(cè)試微服務(wù) 。 他還創(chuàng)建了一個(gè)新問(wèn)題來(lái)改進(jìn)流程,因此可以使用JHipster的JDL生成完整的微服務(wù)堆棧。

升級(jí)到Spring Security 5.1及其一流的OIDC支持

我希望這些挑戰(zhàn)和修復(fù)方法列表對(duì)您有所幫助。 如果您使用不推薦使用的@EnableOAuth2Sso或@EnableResourceServer ,我建議您嘗試升級(jí)到Spring Security 5.1。 我用來(lái)跟蹤升級(jí)的問(wèn)題包含顯示所有必需的代碼更改的鏈接。

  • 整體所需的代碼更改
  • 微服務(wù)架構(gòu)所需的代碼更改

使用JHipster 6生成帶有OIDC進(jìn)行身份驗(yàn)證的Spring Boot + React應(yīng)用

JHipster 6使用最新和最好的Spring Boot和Spring Security版本。 它的前端支持Angular和React。 它也支持Vue ,它不是主要生成器的一部分。

如果使用JHipster 6生成應(yīng)用程序,則本文中提到的所有測(cè)試功能都將包含在您的應(yīng)用程序中。 你是怎樣做的? 我很高興你問(wèn)!
首先安裝JHipster 6 Beta:

npm install -g generator-jhipster@beta

npm命令是Node.js的一部分。 您將需要Node 10.x來(lái)安裝JHipster并運(yùn)行有用的命令。

JHipster 6支持Java 8、11和12(感謝Spring Boot 2.1)。 我建議使用SDKMAN管理Java SDK ! 例如,您可以安裝Java 12并將其設(shè)置為默認(rèn)值。

sdk install java 12.0.0-open sdk default java 12.0.0-open

您可以創(chuàng)建一個(gè)使用React和OIDC的JHipster應(yīng)用,只需幾個(gè)命令:

mkdir app && cd appecho "application { config { baseName reactoidc, \authenticationType oauth2, clientFramework react } }" >> app.jhjhipster import-jdl app.jh

下面是顯示這些命令結(jié)果的終端記錄。

必須已配置的OIDC提供程序正在運(yùn)行,JHipster生成的Spring Boot應(yīng)用程序才能成功啟動(dòng)。 您可以使用Docker Compose啟動(dòng)Keycloak:

docker-compose -f src/main/docker/keycloak.yml up -d

然后使用Maven啟動(dòng)您的應(yīng)用程序:

./mvnw

啟動(dòng)完成后,打開http://localhost:8080 ,然后單擊登錄 。 您將被重定向到Keycloak,您可以在其中輸入admin/admin登錄。

為什么用Okta代替Keycloak?

Keycloak的效果很好,但這是Okta開發(fā)人員博客上的帖子,所以讓我向您展示如何使用Okta! 為什么要使用Okta? 這是一個(gè)很好的問(wèn)題。
Okta是永遠(yuǎn)在線的身份提供商,為開發(fā)人員提供身份驗(yàn)證和授權(quán)服務(wù)。 它還允許您管理用戶。 我喜歡將其稱為“用戶作為軟件服務(wù)”,但是UASS并不是一個(gè)很好的縮寫。 用戶管理作為軟件服務(wù)(UMASS)可以輕松解決。 無(wú)論如何,這是一項(xiàng)很棒的服務(wù),您應(yīng)該嘗試一下。

注冊(cè)您的安全Spring Boot應(yīng)用程序

首先,注冊(cè)一個(gè)免費(fèi)的Okta開發(fā)者帳戶 (如果已經(jīng)有一個(gè)帳戶,則登錄)。

登錄Okta后,注冊(cè)您的Spring Boot應(yīng)用程序。

  • 在頂部菜單中,單擊“ 應(yīng)用程序”
  • 點(diǎn)擊添加應(yīng)用
  • 選擇網(wǎng)站 ,然后單擊下一步。
  • 輸入名字
  • 將登錄重定向URI更改為http://localhost:8080/login/oauth2/code/oidc
  • 點(diǎn)擊完成 ,然后點(diǎn)擊編輯 ,然后添加http://localhost:8080作為注銷重定向URI。
  • 點(diǎn)擊保存

完成后,您的設(shè)置應(yīng)類似于以下屏幕截圖。

在項(xiàng)目的根目錄中創(chuàng)建okta.env文件,并將{..}值替換為Okta應(yīng)用程序中的值:

export SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=https://{yourOktaDomain}/oauth2/default export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID={clientId} export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET={clientSecret}

在您的.gitignore文件中添加*.env ,這樣該文件就不會(huì)在GitHub上結(jié)束。

創(chuàng)建組并將其作為聲明添加到ID令牌

默認(rèn)情況下,JHipster配置為與兩種類型的用戶一起使用:管理員和用戶。 Keycloak會(huì)自動(dòng)為用戶和組配置,但是您需要為Okta組織進(jìn)行一些一次性配置。

創(chuàng)建一個(gè)ROLE_ADMIN和ROLE_USER組(“ 用戶” >“ 組” >“ 添加組” )并將用戶添加到其中。 您可以使用注冊(cè)時(shí)使用的帳戶,也可以創(chuàng)建一個(gè)新用戶(“ 用戶” >“ 添加人” )。 導(dǎo)航到API > 授權(quán)服務(wù)器 ,然后單擊default服務(wù)器。 點(diǎn)擊索賠標(biāo)簽,然后添加索賠 。 將其命名為groups ,并將其包含在ID令牌中。 將值類型設(shè)置為Groups并將過(guò)濾器設(shè)置為.*的正則表達(dá)式。 點(diǎn)擊創(chuàng)建

使用以下命令啟動(dòng)您的應(yīng)用程序:

source okta.env ./mvnw

導(dǎo)航到http://localhost:8080并使用Okta憑據(jù)登錄。

漂亮的臀部,你不覺得嗎? 🤓

使用JHipster進(jìn)行更好的Java測(cè)試

JHipster為您生成了一個(gè)具有開箱即用的良好測(cè)試覆蓋范圍的應(yīng)用程序。 使用自動(dòng)為您配置的SonarCloud分析代碼覆蓋率。 運(yùn)行以下命令以在Docker容器中啟動(dòng)Sonar。

docker-compose -f src/main/docker/sonar.yml up -d

然后運(yùn)行以下Maven命令:

./mvnw -Pprod clean test sonar:sonar -Dsonar.host.url=http://localhost:9001

該過(guò)程完成后,導(dǎo)航至http://localhost:9001/projects ,您將看到項(xiàng)目的報(bào)告。

代碼覆蓋率比本報(bào)告中顯示的要高得多。 我們最近更改了許多測(cè)試以在集成測(cè)試階段運(yùn)行,并且還沒(méi)有弄清楚如何將此數(shù)據(jù)報(bào)告給Sonar。

有關(guān)此功能的更多信息,請(qǐng)參見JHipster的代碼質(zhì)量文檔 。

對(duì)JHipster中的JUnit 5的支持也在進(jìn)行中 。

了解有關(guān)Spring Security,Spring Boot和JHipster的更多信息

我希望您喜歡我的有關(guān)升級(jí)JHipster以使用Spring Security 5.1及其出色的OAuth 2.0 + OIDC支持的故事。 我真的很喜歡Spring Security團(tuán)隊(duì)所做的工作,以簡(jiǎn)化其配置并使OIDC發(fā)現(xiàn)(以及其他功能)正常工作。

我沒(méi)有為該示例創(chuàng)建GitHub存儲(chǔ)庫(kù),因?yàn)镴Hipster生成了所有代碼,并且不需要修改任何內(nèi)容。

如果您想了解有關(guān)JHipster 6的更多信息,請(qǐng)參閱Java 12和JHipster 6更好,更快,更輕便的Java 。 如果您對(duì)JHipster的CRUD生成功能和PWA支持感興趣,我鼓勵(lì)您查看有關(guān)如何使用React,Spring Boot和JHipster構(gòu)建Photo Gallery PWA的博客文章。

我們還發(fā)布了許多有關(guān)測(cè)試和Spring Security 5.1的文章:

  • 使用JUnit 5測(cè)試您的Spring Boot應(yīng)用程序
  • 使用WireMock,Jest,Protractor和Travis CI測(cè)試Spring Boot API和Angular組件的Hitchhiker指南
  • 帶有Spring Security的OAuth 2.0快速指南
  • 將您的Spring Boot應(yīng)用程序遷移到最新和最新的Spring Security和OAuth 2.0

需要更多技術(shù)提示嗎? 在社交網(wǎng)絡(luò){ Twitter , LinkedIn , Facebook , YouTube }上關(guān)注我們,以便在我們發(fā)布新內(nèi)容時(shí)得到通知。
是否有與Okta無(wú)關(guān)的問(wèn)題? 請(qǐng)?jiān)谖覀兊拈_發(fā)者論壇上提問(wèn)。

“通過(guò)Java Hipster升級(jí)Spring Security OAuth和JUnit測(cè)試”最初于2019年4月15日發(fā)布在Okta開發(fā)人員博客上。

“我喜歡編寫身份驗(yàn)證和授權(quán)代碼?!??從來(lái)沒(méi)有Java開發(fā)人員。 厭倦了一次又一次地建立相同的登錄屏幕? 嘗試使用Okta API進(jìn)行托管身份驗(yàn)證,授權(quán)和多因素身份驗(yàn)證。

翻譯自: https://www.javacodegeeks.com/2019/05/spring-security-oauth-through-java-hipster.html

總結(jié)

以上是生活随笔為你收集整理的通过Java Hipster升级Spring Security OAuth和JUnit测试的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。