How can I use a scheduled task with a client that provides also web-page using keycloak?

后端 未结 3 1779
逝去的感伤
逝去的感伤 2021-01-21 07:03

I\'m using Spring Boot and Keycloak to develop a web-app. Then I wrote a scheduled task where I\'m using the KeycloakRestTemplate to ask s

相关标签:
3条回答
  • 2021-01-21 07:40

    If you want to send a request like below through spring

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

    what you need is something like

    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 with actual values.

    Update:-

    Referring to the link that you have mentioned in your question, you can generate the jwt bearer token (client access token) by keycloak itself. Once you get the jwt token, your subsequent requests to resource server should contain header

    Authorization: Bearer <jwt bearer token>
    

    In the post by @Xtreme Biker on topic Keycloak spring security client credential grant , he has provided some sample code for perhaps how you can achieve this using interceptor approach.

    MORE UPDATE :-

    According to keycloak docs - https://www.keycloak.org/docs/3.1/securing_apps/topics/oidc/java/java-adapter-config.html

    to set the jks you should have following properties in your application.properties file.

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

    and according to - https://www.keycloak.org/docs/3.1/securing_apps/topics/oidc/java/spring-security-adapter.html , once you set the jks properly,KeycloakRestTemplate should be able to add proper authentication header to your request.

    UPDATE 3 :-

    After going through - https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/java/spring-security-adapter.adoc , I firmly believe that KeycloakRestTemplate should be able to add the required jwt token to the authorization header of the request because it uses KeycloakClientRequestFactory which gets token string from KeycloakSecurityContext.

    Please try to add all the configuration suggested in this doc like all the filter bean configurations

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

    and ensure you provide all the required properties in the application.properties file.

    Since you are shooting the request from scheduled task which may be asyc in nature so you may have to change the strategy in the constructor of your SecurityConfig, so that instead of a ThreadLocal SecurityContext an InheritableThreadLocal is used, which passes this information when spawning childthreads.

    public SecurityConfig ( KeycloakClientRequestFactory keycloakClientRequestFactory ) {
             this.keycloakClientRequestFactory = keycloakClientRequestFactory ;
    
             // to use principal and authentication together with @ async
             SecurityContextHolder.setStrategyName ( SecurityContextHolder.MODE_INHERITABLETHREADLOCAL ) ;
    
         }
    

    Ref - https://translate.google.co.in/translate?hl=en&sl=de&u=https://blog.codecentric.de/2017/09/keycloak-und-spring-security-teil-3-kommunikation-via-keycloakresttemplate/&prev=search for more details.

    Hope this will help you some way in resolving your issue.

    0 讨论(0)
  • 2021-01-21 07:42

    By default SecurityContextHolder use a THREAD_LOCAL strategy, and thus other threads don't inherit the context from their parent. Look at the Javadoc for MODE_INHERITABLETHREADLOCAL to see if it corresponds to your needs.

    0 讨论(0)
  • 2021-01-21 07:49

    Your method being an @Scheduled it is executed in a different thread than the caller thread. SecurityContextHolder being a Thread Local will not propagate the security context to the scheduled thread. One method would be to change the default strategy of the SecurityContextHolder from THREAD_LOCAL, to MODE_INHERITABLETHREADLOCAL, by adding this line in the default constructor of a @Configuration class.

    SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    

    But depending on your use case, you should be aware that the security context might contain sensitive information that you might not want to be passed down.

    0 讨论(0)
提交回复
热议问题