前置条件
我有两个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的每个令牌?
其他人有类似的问题吗?或者知道如何解决这个问题吗?
经过一些研究,发现问题出在生成的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中,以便进一步重新调用。