从 springboot(客户端应用程序)调用 "bearer-only" keycloak 端点到同样 spring boot(仅承载应用程序)的问题
Problem calling a "bearer-only" keycloak endpoint from a springboot (client app) to a also spring boot (bearer only app)
基本上,我正在尝试从使用 "KeycloakRestTemplate" 的客户端应用程序访问 bearer-only 端点。
我确实遵循了这个指南1:1(它是德文的):https://blog.codecentric.de/2017/09/keycloak-und-spring-security-teil-1-einrichtung-frontend/
我的问题是,当我看到日志时,仅承载端点一侧的身份验证似乎成功,如下所示:
Found [1] values in authorization header, selecting the first value for Bearer.
o.k.a.BearerTokenRequestAuthenticator : Verifying access_token
o.k.a.BearerTokenRequestAuthenticator : access_token: [LONG TOKEN HERE]
o.k.a.RefreshableKeycloakSecurityContext : checking whether to refresh.
org.keycloak.adapters.AdapterUtils : use realm role mappings
org.keycloak.adapters.AdapterUtils : Setting roles:
org.keycloak.adapters.AdapterUtils : role: create_vouchers
org.keycloak.adapters.AdapterUtils : role: public_realm_access
org.keycloak.adapters.AdapterUtils : role: overview_orders
org.keycloak.adapters.AdapterUtils : role: uma_authorization
User 'c1500da2-855f-4306-ab65-662160558101' invoking 'http://localhost:8082/articles' on client 'articlesBearerOnlyService'
o.k.adapters.RequestAuthenticator : Bearer AUTHENTICATED
.k.a.t.AbstractAuthenticatedActionsValve : AuthenticatedActionsValve.invoke /articles
o.k.a.AuthenticatedActionsHandler : AuthenticatedActionsValve.invoke http://localhost:8082/articles
cors validation not needed as were not a secure session or origin header was null: {0}
o.k.a.AuthenticatedActionsHandler : Policy enforcement is disabled.
但紧接着日志上出现了这个:
o.k.adapters.PreAuthActionsHandler : adminRequest http://localhost:8082/login
o.k.adapters.PreAuthActionsHandler : checkCorsPreflight http://localhost:8082/login
.k.a.t.AbstractAuthenticatedActionsValve : AuthenticatedActionsValve.invoke /login
o.k.a.AuthenticatedActionsHandler : AuthenticatedActionsValve.invoke http://localhost:8082/login
o.k.a.AuthenticatedActionsHandler : Origin: null uri: http://localhost:8082/login
o.k.a.AuthenticatedActionsHandler : cors validation not needed as were not a secure session or origin header was null: {0}
o.k.a.AuthenticatedActionsHandler : Policy enforcement is disabled.
所以,它尝试重定向到 adminRequest http://localhost:8082/login?为什么,以及如何解决这个问题?
我也尝试过邮递员(从令牌 end-point 获取 acces-token)并将其粘贴到此 "bearer-only" 端点的授权 header 上,并且类似地,通过查看日志,用户似乎完全像上面的第一个日志块中一样获得授权,不同之处在于它不会尝试重定向到任何地方,但我收到了 401。
{
"timestamp": "2019-09-05T11:18:51.347+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/articles" }
有人可以为可能的解决方案提供一些指导吗?
提前致谢!
----------------------------------------
已编辑
----------------------------------------
这是应用程序属性文件:
server.port = 8082
spring.application.name = articleBearerOnlyService
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.realm=[REALM]
keycloak.resource=articlesBearerOnlyService
keycloak.bearer-only=true
keycloak.cors=true
keycloak.credentials.secret=[SECRET]
keycloak.ssl-required = external
# access controlled through spring security
#keycloak.security-constraints[0].auth-roles[0]=overview_orders
#keycloak.security-constraints[0].security-collections[0].patterns[0]=/articles
logging.level.org.keycloak=TRACE
这里是安全配置:
@KeycloakConfiguration
@EnableWebSecurity
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
private final KeycloakClientRequestFactory keycloakClientRequestFactory;
public SecurityConfig(KeycloakClientRequestFactory keycloakClientRequestFactory) {
this.keycloakClientRequestFactory = keycloakClientRequestFactory;
//to use principal and authentication together with @async
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
/* remove default spring "ROLE_" prefix appending to keycloak's roles*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
// NullAuthenticatedSessionStrategy() for bearer-only services
return new NullAuthenticatedSessionStrategy();
}
/* configure cors & requests handling behaviour*/
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.cors()
.and()
.csrf()
.disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.sessionAuthenticationStrategy(sessionAuthenticationStrategy())
.and()
.authorizeRequests()
.antMatchers("/articles").hasRole("overview_orders")
.anyRequest().permitAll();
}
// Spring boot integration
@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
// *************************** Avoid Bean redefinition ********************************
@Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(
KeycloakAuthenticatedActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakSecurityContextRequestFilterBean(
KeycloakSecurityContextRequestFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
@Override
@ConditionalOnMissingBean(HttpSessionManager.class)
protected HttpSessionManager httpSessionManager() {
return new HttpSessionManager();
}
}
在这种特殊情况下,我的@KeycloakConfiguration class SecurityConfig{...} 被完全忽略了,因此应用程序的行为就好像完全提供了 none 安全配置一样。
现在,为什么 SecurityConfig 被忽略了?
- 结果是 class 的(我几乎感到羞耻)路径位置;我通常会把这样的 class 放在:
下
com.[company].[domain].configuration
就我而言(因为我现在只是使用 keycloak + spring 进行原型设计,并不特别关心 class 位置)。我确实将我的 SecurityConfig class 置于:
com.[company].configuration
这使得 spring 引导完全忽略此 class。
跟进问题:我是 Sprint 引导的新手,是否 100% 需要将 所有代码 放在“com.[company].[domain].configuration", 无需修改 pom(只需通过 initializr 新建一个 vanilla springboot 项目)?
@SpringBootApplication
注释是这三个注释的组合:@EnableAutoConfiguration
、@ComponentScan
和 @Configuration
。注释 class 例如com.example.demo.DemoApplication
和 @SpringBootApplication
,导致 Spring 在 com.example.demo
及其所有子包中寻找其他组件、配置和服务。
像com.example.config.DemoConfig
这样的class因此无法被Spring自动找到。如果需要,您可以通过 @ComponentScan(basePackages = "com.some.package")
提示 Spring 在何处查找组件。如果您想了解更多,请查看此 article。
基本上,我正在尝试从使用 "KeycloakRestTemplate" 的客户端应用程序访问 bearer-only 端点。
我确实遵循了这个指南1:1(它是德文的):https://blog.codecentric.de/2017/09/keycloak-und-spring-security-teil-1-einrichtung-frontend/
我的问题是,当我看到日志时,仅承载端点一侧的身份验证似乎成功,如下所示:
Found [1] values in authorization header, selecting the first value for Bearer.
o.k.a.BearerTokenRequestAuthenticator : Verifying access_token
o.k.a.BearerTokenRequestAuthenticator : access_token: [LONG TOKEN HERE]
o.k.a.RefreshableKeycloakSecurityContext : checking whether to refresh.
org.keycloak.adapters.AdapterUtils : use realm role mappings
org.keycloak.adapters.AdapterUtils : Setting roles:
org.keycloak.adapters.AdapterUtils : role: create_vouchers
org.keycloak.adapters.AdapterUtils : role: public_realm_access
org.keycloak.adapters.AdapterUtils : role: overview_orders
org.keycloak.adapters.AdapterUtils : role: uma_authorization
User 'c1500da2-855f-4306-ab65-662160558101' invoking 'http://localhost:8082/articles' on client 'articlesBearerOnlyService'
o.k.adapters.RequestAuthenticator : Bearer AUTHENTICATED
.k.a.t.AbstractAuthenticatedActionsValve : AuthenticatedActionsValve.invoke /articles
o.k.a.AuthenticatedActionsHandler : AuthenticatedActionsValve.invoke http://localhost:8082/articles
cors validation not needed as were not a secure session or origin header was null: {0}
o.k.a.AuthenticatedActionsHandler : Policy enforcement is disabled.
但紧接着日志上出现了这个:
o.k.adapters.PreAuthActionsHandler : adminRequest http://localhost:8082/login
o.k.adapters.PreAuthActionsHandler : checkCorsPreflight http://localhost:8082/login
.k.a.t.AbstractAuthenticatedActionsValve : AuthenticatedActionsValve.invoke /login
o.k.a.AuthenticatedActionsHandler : AuthenticatedActionsValve.invoke http://localhost:8082/login
o.k.a.AuthenticatedActionsHandler : Origin: null uri: http://localhost:8082/login
o.k.a.AuthenticatedActionsHandler : cors validation not needed as were not a secure session or origin header was null: {0}
o.k.a.AuthenticatedActionsHandler : Policy enforcement is disabled.
所以,它尝试重定向到 adminRequest http://localhost:8082/login?为什么,以及如何解决这个问题?
我也尝试过邮递员(从令牌 end-point 获取 acces-token)并将其粘贴到此 "bearer-only" 端点的授权 header 上,并且类似地,通过查看日志,用户似乎完全像上面的第一个日志块中一样获得授权,不同之处在于它不会尝试重定向到任何地方,但我收到了 401。
{ "timestamp": "2019-09-05T11:18:51.347+0000", "status": 401, "error": "Unauthorized", "message": "Unauthorized", "path": "/articles" }
有人可以为可能的解决方案提供一些指导吗?
提前致谢!
---------------------------------------- 已编辑 ----------------------------------------
这是应用程序属性文件:
server.port = 8082
spring.application.name = articleBearerOnlyService
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.realm=[REALM]
keycloak.resource=articlesBearerOnlyService
keycloak.bearer-only=true
keycloak.cors=true
keycloak.credentials.secret=[SECRET]
keycloak.ssl-required = external
# access controlled through spring security
#keycloak.security-constraints[0].auth-roles[0]=overview_orders
#keycloak.security-constraints[0].security-collections[0].patterns[0]=/articles
logging.level.org.keycloak=TRACE
这里是安全配置:
@KeycloakConfiguration
@EnableWebSecurity
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
private final KeycloakClientRequestFactory keycloakClientRequestFactory;
public SecurityConfig(KeycloakClientRequestFactory keycloakClientRequestFactory) {
this.keycloakClientRequestFactory = keycloakClientRequestFactory;
//to use principal and authentication together with @async
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
/* remove default spring "ROLE_" prefix appending to keycloak's roles*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
// NullAuthenticatedSessionStrategy() for bearer-only services
return new NullAuthenticatedSessionStrategy();
}
/* configure cors & requests handling behaviour*/
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.cors()
.and()
.csrf()
.disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.sessionAuthenticationStrategy(sessionAuthenticationStrategy())
.and()
.authorizeRequests()
.antMatchers("/articles").hasRole("overview_orders")
.anyRequest().permitAll();
}
// Spring boot integration
@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
// *************************** Avoid Bean redefinition ********************************
@Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(
KeycloakAuthenticatedActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakSecurityContextRequestFilterBean(
KeycloakSecurityContextRequestFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
@Override
@ConditionalOnMissingBean(HttpSessionManager.class)
protected HttpSessionManager httpSessionManager() {
return new HttpSessionManager();
}
}
在这种特殊情况下,我的@KeycloakConfiguration class SecurityConfig{...} 被完全忽略了,因此应用程序的行为就好像完全提供了 none 安全配置一样。
现在,为什么 SecurityConfig 被忽略了? - 结果是 class 的(我几乎感到羞耻)路径位置;我通常会把这样的 class 放在:
下com.[company].[domain].configuration
就我而言(因为我现在只是使用 keycloak + spring 进行原型设计,并不特别关心 class 位置)。我确实将我的 SecurityConfig class 置于:
com.[company].configuration
这使得 spring 引导完全忽略此 class。
跟进问题:我是 Sprint 引导的新手,是否 100% 需要将 所有代码 放在“com.[company].[domain].configuration", 无需修改 pom(只需通过 initializr 新建一个 vanilla springboot 项目)?
@SpringBootApplication
注释是这三个注释的组合:@EnableAutoConfiguration
、@ComponentScan
和 @Configuration
。注释 class 例如com.example.demo.DemoApplication
和 @SpringBootApplication
,导致 Spring 在 com.example.demo
及其所有子包中寻找其他组件、配置和服务。
像com.example.config.DemoConfig
这样的class因此无法被Spring自动找到。如果需要,您可以通过 @ComponentScan(basePackages = "com.some.package")
提示 Spring 在何处查找组件。如果您想了解更多,请查看此 article。