【问题标题】:How to reuse oauth2 token from user (authorization_code) in a Rest Template如何在 Rest 模板中重用来自用户(authorization_code)的 oauth2 令牌
【发布时间】:2017-11-25 14:09:20
【问题描述】:

我有 3 个应用程序

  1. 前端应用程序
  2. OAuth2 认证服务器
  3. REST API (RepositoryRestResources)

我的用户必须先登录才能使用前端应用程序。这通过 SSO 发生。他们收到一个令牌,该令牌在被允许进入之前由客户端验证。

我想重用这个令牌来发出 api 请求。我的 REST api 应用程序使用相同的 SSO 登录(它是前端客户端的资源)进行保护,但我不知道如何在我用于 api 请求的 RestTemplate 中“添加授权标头”。

我这样创建我的restTemplate:

public static RestTemplate build()
    {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.registerModule(new Jackson2HalModule());
        mapper.registerModule(new JavaTimeModule());
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
        converter.setObjectMapper(mapper);
        return new RestTemplate(Arrays.asList(converter));
    }

我的资源服务器配置:

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
{

    @Value("${resource.id}")
    private String resourceId;

    @Override
    public void configure(HttpSecurity http) throws Exception
    {
        http
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated()
                .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception
    {
        resources.resourceId(resourceId);
    }


    @Bean
    public static TokenEnhancer tokenEnhancer()
    {
        return new JwtTokenEnhancer();
    }


    @Bean
    public static JwtAccessTokenConverter accessTokenConverter()
    {
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "somesecret".toCharArray());
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("pair"));
        return converter;
    }

    @Bean
    public static TokenStore tokenStore()
    {
        return new JwtTokenStore(accessTokenConverter());
    }

}

【问题讨论】:

    标签: spring spring-boot spring-security spring-data-rest spring-security-oauth2


    【解决方案1】:

    您可以在您的方法顶部使用@PreAuthorize(ROLE),因此当调用此方法时,他将针对令牌然后检查提供的令牌是否具有使用该方法所需的角色。

    当然,您需要配置 API 以连接到 OAuth 数据库。

    例子:

    @PreAuthorize("ROLE_ADMIN") public void deleteAll(){ ... }

    【讨论】:

    • 我的 api 设置正确。问题是客户端的 rest 模板不“使用”身份验证,或者更好的是不发送授权标头及其请求。
    【解决方案2】:

    我使用拦截器修复它并从安全上下文中手动添加令牌。

    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getInterceptors().add(new OAuthInterceptor());
    

    其中拦截器定义为:

    public class OAuthInterceptor implements ClientHttpRequestInterceptor
    {
    
        @Autowired
        private AuthenticationHolder holder;
    
        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException
        {
            if (holder.getToken() == null)
            {
                //throw new IOException("Token not set");
                System.out.println("##################### Token not set! ###################");
            }
            else
            {
                System.out.println("##################### Token found: " + holder.getToken());
                HttpHeaders headers = request.getHeaders();
                headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + holder.getToken());
            }
    
            return execution.execute(request, body);
        }
    }
    

    我使用我在客户端应用程序中实现的接口:

    public interface AuthenticationHolder
    {
        String getToken();
    }
    
    @Bean
    public AuthenticationHolder getAuthenticationHolder()
    {
        return () ->
        {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if(authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails)
            {
                return ((OAuth2AuthenticationDetails) authentication.getDetails()).getTokenValue();
            }
            return null;
        };
    }
    

    【讨论】:

    • AuthenticationHolder 始终为 null.. 即使在使用 Component 注释之后。这是经过测试的代码还是我做错了?
    • 我猜 AuthenticationHolder 是一些自定义的东西,所以你可能只想使用 SecurityContextHolder.getContext().getAuthentication();取而代之。
    • OAuth2AuthenticationDetails auth = (OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails(); ____ 并由此____ auth.getTokenValue()
    • 为了避免 NPE,我建议: Optional token = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).map(Authentication::getDetails).filter(details -> auth instanceof OAuth2AuthenticationDetails).map(OAuth2AuthenticationDetails.class::cast).map(OAuth2AuthenticationDetails::getToken) (未经测试)
    猜你喜欢
    • 1970-01-01
    • 2020-06-27
    • 1970-01-01
    • 2020-04-13
    • 2020-01-04
    • 2019-07-23
    • 2013-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多