如何在还使用 keycloak 提供网页的客户端上使用计划任务?

2023-12-06

我在用着Spring Boot and Keycloak开发一个网络应用程序。 然后我写了一个计划任务,我正在使用KeycloakRestTemplate向另一个应用程序询问一些数据,如下所示:

    @Override
    @Scheduled(cron="0 50 09 * * MON-FRI")
    public void concludiCommessa() {

        try {
            FDto[] ftts = new ObjectMapper().readValue(restTemplate.getForEntity(URI.create(MY_URL), String.class).getBody(), FDto[].class);

             ..............................
            }
        } catch (RestClientException | IOException e) {
        }
    }

如果我在服务器上运行它,则会出现以下错误:

2018-04-18 09:50:00.067 ERROR 2503 --- [pool-8-thread-1] o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task.

java.lang.IllegalStateException: Cannot set authorization header because there is no authenticated principal
    at org.keycloak.adapters.springsecurity.client.KeycloakClientRequestFactory.getKeycloakSecurityContext(KeycloakClientRequestFactory.java:70) ~[keycloak-spring-security-adapter-3.4.2.Final.jar:3.4.2.Final]
    at org.keycloak.adapters.springsecurity.client.KeycloakClientRequestFactory.postProcessHttpRequest(KeycloakClientRequestFactory.java:55) ~[keycloak-spring-security-adapter-3.4.2.Final.jar:3.4.2.Final]
    at org.springframework.http.client.HttpComponentsClientHttpRequestFactory.createRequest(HttpComponentsClientHttpRequestFactory.java:207) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.http.client.support.HttpAccessor.createRequest(HttpAccessor.java:85) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:656) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:636) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:336) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at it.edile.service.api.ApiServiceImpl.concludiCommessa(ApiServiceImpl.java:287) ~[classes/:0.0.1-SNAPSHOT]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) ~[spring-context-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) [spring-context-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_161]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_161]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_161]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_161]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_161]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_161]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_161]

Why?

如果我使用异步任务,如何传递主体?

EDIT这是我的安全配置:

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(keycloakAuthenticationProvider());
}

@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
    return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public KeycloakRestTemplate keycloakRestTemplate() {
    return new KeycloakRestTemplate(keycloakClientRequestFactory);
}

@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
    return new KeycloakSpringBootConfigResolver();
}

EDIT这是我的钥匙斗篷属性:

#######################################
#             KEYCLOAK                #
#######################################
keycloak.realm=MY_REALM
keycloak.auth-server-url=MY_URL/auth
keycloak.ssl-required=external
keycloak.resource=EdilGest
keycloak.credentials.jwt.client-key-password=PWD
keycloak.credentials.jwt.client-keystore-file=classpath:CLIENT.jks
keycloak.credentials.jwt.client-keystore-password=PWD
keycloak.use-resource-role-mappings=true
keycloak.principal-attribute=preferred_username

EDIT:

我现在正在尝试使用服务帐户,但目前不起作用...阅读此处:https://www.keycloak.org/docs/latest/server_admin/index.html#_service_accounts

我必须发送一个请求,例如:

POST /auth/realms/demo/protocol/openid-connect/token
    Authorization: Basic cHJvZHVjdC1zYS1jbGllbnQ6cGFzc3dvcmQ=
    Content-Type: application/x-www-form-urlencoded

    grant_type=client_credentials

到 keycloak,但我如何使用 Spring 发送它?我怎样才能设置 jks 而不是客户端和秘密?

EDIT 2

我的安全配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {  

    @Autowired
    public KeycloakClientRequestFactory keycloakClientRequestFactory;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);

        http
            .httpBasic()
            .disable();

        http
        .authorizeRequests()
            .antMatchers("/webjars/**").permitAll()
            .antMatchers("/resources/**").permitAll()
            .anyRequest().hasAuthority("......")
        .and()
        .logout()
            .logoutUrl("/logout")
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
            .permitAll()
            .logoutSuccessUrl(mux)
            .invalidateHttpSession(true);

    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public KeycloakRestTemplate keycloakRestTemplate() {
        return new KeycloakRestTemplate(keycloakClientRequestFactory);
    }

    @Bean
    public KeycloakConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    @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;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web
           .ignoring()
           .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/webjars/**");
    }

}

EDIT 3这是我尝试过的...它不起作用..我有同样的错误:java.lang.IllegalStateException: Cannot set authorization header because there is no authenticated principal

KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("EdilGest.jks"), "EdilGest".toCharArray());

JWTClientCredentialsProvider jwtClientCredentialsProvider = new JWTClientCredentialsProvider();
jwtClientCredentialsProvider.setupKeyPair(keyStoreKeyFactory.getKeyPair("MyClient"));
String token = jwtClientCredentialsProvider.createSignedRequestToken("MyClient", "http://myKeycloak/auth/");

String data = "grant_type=client_credentials" ;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add(HttpHeaders.AUTHORIZATION, "Bearer " +token);

HttpEntity<String> requestEntity = new HttpEntity<String>(data, headers);
String ftt = keycloakRestTemplate.exchange(URI.create(MyUrl), HttpMethod.POST, requestEntity, String.class).getBody();

我究竟做错了什么?


如果你想通过 spring 发送如下请求

POST /auth/realms/demo/protocol/openid-connect/token
    Authorization: Basic cHJvZHVjdC1zYS1jbGllbnQ6cGFzc3dvcmQ=
    Content-Type: application/x-www-form-urlencoded

    grant_type=client_credentials

你需要的是类似的东西

RestTemplate template = new RestTemplate();
String uri = "https://host:port/auth/realms/demo/protocol/openid-connect/token";
String data = "grant_type=client_credentials" ;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add(HttpHeaders.AUTHORIZATION, "Basic " + Base64Utils.encodeToString("clientId:clientSecret".getBytes()));
HttpEntity<String> requestEntity = new HttpEntity<String>(data, headers);

ResponseEntity<JsonNode> result = template.exchange(uri, HttpMethod.POST, requestEntity, JsonNode.class);
JsonNode jn = result.getBody();
String access_token = jn.get("access_token").asText();

Replace clientId and clientSecret与实际值。

Update:-

参考您在问题中提到的链接,您可以通过 keycloak 本身生成 jwt 持有者令牌(客户端访问令牌)。获得 jwt 令牌后,对资源服务器的后续请求应包含标头

Authorization: Bearer <jwt bearer token>

在帖子中由@极限骑行者就主题Keycloak Spring Security 客户端凭证授予,他提供了一些示例代码,也许您可​​以使用拦截器方法来实现这一点。

更多更新:-

根据 keycloak 文档 -https://www.keycloak.org/docs/3.1/secure_apps/topics/oidc/java/java-adapter-config.html

要设置 jks,您的 application.properties 文件中应该具有以下属性。

keycloak.client-keystore-password=PWD
keycloak.client-keystore=classpath:CLIENT.jks
keycloak.client-key-password=PWD

并根据 -https://www.keycloak.org/docs/3.1/secure_apps/topics/oidc/java/spring-security-adapter.html,一旦你正确设置了 jks,KeycloakRestTemplate应该能够向您的请求添加正确的身份验证标头。

更新3:-

经过之后——https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/java/spring-security-adapter.adoc,我坚信KeycloakRestTemplate应该能够将所需的 jwt 令牌添加到请求的授权标头中,因为它使用KeycloakClientRequestFactory它从获取令牌字符串KeycloakSecurityContext.

请尝试添加本文档中建议的所有配置,例如所有过滤器 bean 配置

@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;
    }

并确保在 application.properties 文件中提供所有必需的属性。

由于您正在拍摄来自计划任务的请求,该任务本质上可能是异步的,因此您可能必须更改构造函数中的策略SecurityConfig,这样就可以代替ThreadLocal SecurityContext an InheritableThreadLocal使用,它在生成子线程时传递此信息。

public SecurityConfig ( KeycloakClientRequestFactory keycloakClientRequestFactory ) {
         this.keycloakClientRequestFactory = keycloakClientRequestFactory ;

         // to use principal and authentication together with @ async
         SecurityContextHolder.setStrategyName ( SecurityContextHolder.MODE_INHERITABLETHREADLOCAL ) ;

     }

Ref - 更多细节。

希望这能在某种程度上帮助您解决问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在还使用 keycloak 提供网页的客户端上使用计划任务? 的相关文章

随机推荐

  • 如何从 Google Apps 脚本发出 Gmail API 批量请求?

    例如 我需要在以下场景中进行批量请求 使用后Gmail Users Threads list 我想做几个Gmail Users Threads get threadId 批量操作 我正在谈论类似的事情gapi client newBatch
  • Xcode:Interface Builder XIB 编译器错误

    Xcode 5 1 1 和 OS X 10 9 3 我打开 Xcode 并启动一个新的 Cocoa 项目 然后我点击运行 构建失败 我收到以下错误消息 Interface Builder XIB Compiler Error Interfa
  • if else 语句反转

    我想要的结果是第二个 if else 语句 如果代码不在列表中 则发出警报 我不明白为什么第一个 if else 语句失败 我认为只需反转第二个 if else 语句 我误解了什么吗 https jsfiddle net e6qohvhc
  • 如果值比闭包寿命更长,那么在闭包之间共享引用的正确方法是什么?

    我想分享两个闭包之间的引用 在一个闭包中可变地 这是一种人为的情况 但我发现在学习 Rust 的背景下它很有趣 为了使它工作 我必须利用Rc Weak and RefCell 有没有更简单的方法来实现这一目标 use std cell Re
  • 多个 和 标签

    我正在尝试创建一个非常简单的 Web 应用程序 基本上是为了了解 HTML5 CSS 和 JavaScript 编码的最佳实践 我的应用程序有 3 4 页 每一页都使用相同的菜单标题 所以我想通过将其写入单独的文件 PHP 或 HTML 来
  • get_headers 不一致[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 运行以下代码 var dump get headers
  • 在 Makefile 中链接 cURL

    通过源代码安装 cURL 后 我需要在 Ubuntu 11 04 中链接 cURL 问题的修正 首先我发现 l必须先于 L然后发现我没有在 makefile 中输入变量 获取 cURL 配置 在我的终端上 curl config libs
  • UIBezierPath + CAShapeLayer - 动画填充一个圆圈[重复]

    这个问题在这里已经有答案了 我正在尝试对 CAShapeLayer 的路径进行动画处理 以便获得圆形 填充 到特定数量的效果 问题 它 有效 但不是AS尽管我认为它可能很顺利 但我想对其进行一些缓和 但因为我正在单独为每个 设置动画 所以我
  • JSONEncoder 不允许将类型编码为原始值

    我正在致力于实施Codable for an enum类型与可能的关联值 由于这些对于每种情况都是独特的 所以我认为我可以在编码期间不带密钥地输出它们 然后简单地看看在解码时可以得到什么 以恢复正确的情况 这是一个非常精简的 人为的示例 演
  • pip install 生成“找不到满足要求的版本”[重复]

    这个问题在这里已经有答案了 我有一个包已上传到 test pypi 我可以在我的计算机上的虚拟环境中安装这个软件包 没有任何问题 pip install index url https test pypi org simple packag
  • 对于某些在线编译器,扫描器 next() 会抛出 NoSuchElementException

    这似乎是一个常见问题 多次询问 但我无法找到这种行为的解释 以下代码可以在一个编译器中运行 但会抛出异常Exception in thread main java util NoSuchElementException在另一个编译器中 Sc
  • Spacy -- ImportError:preshed.maps 未导出预期的 C 函数 map_clear

    我正在尝试导入spacy徒然 gt gt gt import spacy Traceback most recent call last File
  • 如何将Flash(.swf)放入页面中?

    我计划使用 swf 文件作为标题 这是行不通的 img src exactheader swf width 650 height 140 你能告诉我怎么做吗 试试这个
  • 集群内无法访问 Kubernetes POD

    我尝试安装Kubernetes with kubeadm在 3 个虚拟机上Debian我的笔记本电脑上的操作系统 一个作为主节点 另外两个作为工作节点 我完全按照教程做的kubernetes io建议 我用命令初始化了集群kubeadm i
  • NSUserDefaults 无法在带有 Watch OS2 的 Xcode beta 上工作

    我刚刚安装了最新的 Xcode 测试版来尝试Swift 2以及对 Apple Watch 开发部分所做的改进 我实际上很难弄清楚为什么这个基本NSUserDefaults之间共享信息的方法iOS and 观看 OS2不工作 我跟着this
  • Python - 查找第二小的数字

    我在这个网站上找到了这段代码来查找第二大数字 def second largest numbers m1 m2 None None for x in numbers if x gt m1 m1 m2 x m1 elif x gt m2 m2
  • 在测试类中模拟私有静态最终变量

    我想测试的类中有一些私有静态最终字段 就像下面这样 public class ClassToTest private static final Myclass myclass MyClassFactory getMyClass type f
  • ffmpeg 在 iOS5.1 中不会 ./configure

    我试图在 iOS5 1 armv7 上构建 ffmpeg 当我尝试像这样运行 configure 时 configure disable doc disable ffmpeg disable ffplay disable ffserver
  • Pyodbc - “未找到数据源名称,并且未指定默认驱动程序”

    我很难得到pyodbc工作 我有unixodbc unixodbc dev odbc postgresql pyodbc安装在我的 Linux Mint 14 上的软件包 我对自己找到解决方案失去了希望 感谢任何帮助 详情请参阅下面 Run
  • 如何在还使用 keycloak 提供网页的客户端上使用计划任务?

    我在用着Spring Boot and Keycloak开发一个网络应用程序 然后我写了一个计划任务 我正在使用KeycloakRestTemplate向另一个应用程序询问一些数据 如下所示 Override Scheduled cron