简体   繁体   中英

How to call rest endpoint via Spring restTemplate that is protected by Keycloak

Preconditions

I have two Java Spring applications(App 'A' and App 'B') that were created via JHipster(monolithic application). Both applications uses keycloak for authentication/authorization. Both applications have an angular frontend and support login via ouath (spring-security). Here ist my SecurityConfiguration of Application A and B:

@Configuration
@Import(SecurityProblemSupport.class)
@EnableOAuth2Sso
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final CorsFilter corsFilter;

    private final SecurityProblemSupport problemSupport;

    public SecurityConfiguration(CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
        this.corsFilter = corsFilter;
        this.problemSupport = problemSupport;
    }

    @Bean
    public AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler() {
        return new AjaxLogoutSuccessHandler();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
        return new SecurityEvaluationContextExtension();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
            .antMatchers(HttpMethod.OPTIONS, "/**")
            .antMatchers("/app/**/*.{js,html}")
            .antMatchers("/i18n/**")
            .antMatchers("/content/**")
            .antMatchers("/swagger-ui/index.html")
            .antMatchers("/test/**")
            .antMatchers("/h2-console/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
        .and()
            .addFilterBefore(corsFilter, CsrfFilter.class)
            .exceptionHandling()
            .authenticationEntryPoint(problemSupport)
            .accessDeniedHandler(problemSupport)
        .and()
            .logout()
            .logoutUrl("/api/logout")
            .logoutSuccessHandler(ajaxLogoutSuccessHandler())
            .permitAll()
        .and()
            .headers()
            .frameOptions()
            .disable()
        .and()
            .authorizeRequests()
            .antMatchers("/api/profile-info").permitAll()
            .antMatchers("/api/**").authenticated()
            .antMatchers("/management/health").permitAll()
            .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
            .antMatchers("/v2/api-docs/**").permitAll()
            .antMatchers("/swagger-resources/configuration/ui").permitAll()
            .antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN);
    }


}

In App B i also have an ResourceServerConfiguration . This checks if the header contains an "Authorization" key. If true, the user can login via JWT(Bearer Authentication). I tested this via Postman and it works fine:

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(final HttpSecurity http) throws Exception {
        http
            .requestMatcher(new RequestHeaderRequestMatcher("Authorization")).authorizeRequests()
            .anyRequest().authenticated();
    }

}

Further more both apps are in the same keycloak realm and have the access-type "public".

Problem:

Now i want to call an endpoint of App B via an Spring RestTemplate from App A. The problem is, that i do not have an access_token that i can put in my rest request/restTemplate. When i look in my request that is send from my frontend, i only got an JSESSIONID. There is no access_token/JWT in the header.

Question

Is there a way to get the access_token of the current user out of the JSESSIONID/the HttpSession or the spring security context? Do i need something like a Tokenstore where i store every token that comes from keycloak?

Did anyone else have similar problems or any idea how i could solve that problem?

After some research it turns out that the problem lies within the generated jhipster code. I followed the authentication process in the application and saw, that there was a call to the /account endpoint directly after authentication, where the user information were retrieved. The call is triggerd by the frontend. First time this endpoint is called, there is a principal with a bearer token available. Within the /account endpoint, a call to the userService with the principal object is performed. More precisley

getUserFromAuthentication(OAuth2Authentication authentication)

is called. Within this method there is a part that replaces the OAuth2Authentication with a new UsernamePasswordAuthenticationToken and inserts it into the SecurityContext:

UsernamePasswordAuthenticationToken token = getToken(details, user, 
grantedAuthorities);
authentication = new OAuth2Authentication(authentication.getOAuth2Request(), token);
SecurityContextHolder.getContext().setAuthentication(authentication);

So after that, the access_token is lost. I am not quite sure, why it was replaced with the new OAuth2Authentication, but i tend to extend this part and keep the access_token in my securityContext for further restcalls.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM