提问者:小点点

如何通过受Keyclock保护的SpringrestTemplate调用restendpoint


前置条件

我有两个Java Spring应用程序(应用程序“A”和应用程序“B”),它们是通过JHipster(单片应用程序)创建的。这两个应用程序都使用keyclock进行身份验证/授权。这两个应用程序都有一个有角度的前端,并支持通过ouath(Spring安全)登录。下面是应用程序A和B的SecurityConfiguration

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


}

在应用程序B中,我还有一个< code > resources server配置。这将检查报头是否包含“授权”密钥。如果为真,用户可以通过JWT(不记名身份验证)登录。我通过Postman测试了这一点,效果很好:

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

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

}

此外,这两个应用程序都位于同一密钥斗篷领域,并且具有访问类型的“公共”。

问题:

现在,我想从应用程序a通过Spring RestTemplate调用应用程序B的endpoint,问题是,我没有可以放入rest请求/restTemplate的access_token。当我查看从我的前端发送的请求时,我只得到一个JSESSIONID。报头中没有访问令牌/JWT。

问题

有没有办法从JSESSIONID/http session或者Spring Security上下文中获取当前用户的access_token?我是否需要一个像Tokenstore这样的东西来存储来自keycloak的每个令牌?

其他人有类似的问题吗?或者知道如何解决这个问题吗?


共1个答案

匿名用户

经过一些研究,发现问题出在生成的jhipster代码中。我跟踪了应用程序中的身份验证过程,发现在身份验证之后有一个对/accountendpoint的调用,在那里检索用户信息。呼叫由前端触发。第一次调用此endpoint时,有一个具有可用承载令牌的主体。在/account终结点中,使用主体对象执行对userService的调用。更准确地说

getUserFromAuthentication(OAuth2Authentication authentication)

被调用。在此方法中,有一个部分用新的UsernamePasswordAuthenticationToken替换OAuth2Authentication并将其插入SecurityContext:

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

所以在那之后,access_token丢失了。我不太确定,为什么它被新的OAuth2Authentication替换了,但是我倾向于扩展这部分,并将access_token保存在我的securityContext中,以便进一步重新调用。