【问题标题】:Storing JWT tokens on OAuth2 web client using Spring Security使用 Spring Security 在 OAuth2 Web 客户端上存储 JWT 令牌
【发布时间】:2019-10-02 00:37:03
【问题描述】:

我正在使用 Spring Boot 2.1.3 和 Spring Security 5.1.3 实现 OAuth2 Web 应用程序客户端,该客户端通过授权代码授予类型从授权服务器获取 JWT 令牌并调用受保护的资源服务器。

这是目前的实现方式:

安全配置和一个用于调用受保护资源的restTemplate bean:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/").permitAll()
            .anyRequest().authenticated()
            .and()
            .oauth2Login()
            .and()
            .oauth2Client()
            .and().logout().logoutSuccessUrl("/");
    }

    @Bean
    public RestTemplate restTemplate(OAuth2AuthorizedClientService clientService) {
        RestTemplate restTemplate = new RestTemplate();
        List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
        if (CollectionUtils.isEmpty(interceptors)) {
            interceptors = new ArrayList<>();
        }
        interceptors.add(new AuthorizationHeaderInterceptor(clientService));
        restTemplate.setInterceptors(interceptors);
        return restTemplate;
    }
}

在 restTemplate 中添加授权标头(来自框架的 InMemoryOAuth2AuthorizedClientService)的拦截器:

public class AuthorizationHeaderInterceptor implements ClientHttpRequestInterceptor {

    private OAuth2AuthorizedClientService clientService;

    public AuthorizationHeaderInterceptor(OAuth2AuthorizedClientService clientService) {
        this.clientService = clientService;
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] bytes, ClientHttpRequestExecution execution) throws IOException {
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String accessToken = null;
        if (authentication != null && authentication.getClass().isAssignableFrom(OAuth2AuthenticationToken.class)) {
            OAuth2AuthenticationToken auth = (OAuth2AuthenticationToken) authentication;
            String clientRegistrationId = auth.getAuthorizedClientRegistrationId();
            OAuth2AuthorizedClient client = clientService.loadAuthorizedClient(clientRegistrationId, auth.getName());
            accessToken = client.getAccessToken().getTokenValue();
            request.getHeaders().add("Authorization", "Bearer " + accessToken);
        }
        return execution.execute(request, bytes);
    }
}

以及调用受保护资源服务器的控制器:

@Controller
@RequestMapping("/profile")
public class ProfileController {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${oauth.resourceServerBase}")
    private String resourceServerBase;

    @GetMapping
    public String getProfile(Model model) {
        Profile profile = restTemplate.getForEntity(resourceServerBase + "/api/profile/", Profile.class).getBody();
        model.addAttribute("profile", profile);
        return "profile";
    }
}

OAuth2客户端配置直接在application.yml中:

spring:
  security:
    oauth2:
      client:
        registration:
          auth-server:
            client-id: webClient
            client-secret: clientSecret
            scope: read,write
            authorization-grant-type: authorization_code
            redirect-uri: http://localhost:8081/client/login/oauth2/code/auth-server
        provider:
          auth-server:
            authorization-uri: http://localhost:8080/auth-server/oauth/authorize
            token-uri: http://localhost:8080/auth-server/oauth/token
            user-info-uri: http://localhost:8082/resource-server/users/info
            user-name-attribute: user_name

在进行一些调试后,我观察到,在通过 OAuth2LoginAuthtenticationFilter 的成功身份验证流程结束时,框架正在通过提供的 InMemoryOAuth2AuthorizedClientService 将获得的访问权限和刷新 JWT 令牌存储在内存中的 OAuth2AuthorizedClient 模型下。

我正在尝试找出如何覆盖此行为,以便令牌在服务器重新启动后仍然可用。并基于此保持用户登录。

我应该只提供一个自定义的 OAuth2AuthorizedClientService 实现吗?我如何配置 Spring Security 来使用它?这个自定义实现是否应该将令牌存储在 cookie 中?

【问题讨论】:

    标签: spring spring-boot spring-security oauth-2.0 spring-security-oauth2


    【解决方案1】:

    我应该只提供一个自定义的 OAuth2AuthorizedClientService 实施?

    我认为是的,用于解决您的用例

    如何配置 Spring Security 以使用它?

    来自spring doc

    如果您想提供自定义实现 AuthorizationRequestRepository 存储的属性 Cookie 中的 OAuth2AuthorizationRequest,您可以配置它,如下所示 在以下示例中:

    @EnableWebSecurity
    public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .oauth2Client()
                    .authorizationCodeGrant()
                        .authorizationRequestRepository(this.cookieAuthorizationRequestRepository())
                        ...
        }
    
        private AuthorizationRequestRepository<OAuth2AuthorizationRequest> cookieAuthorizationRequestRepository() {
            return new HttpCookieOAuth2AuthorizationRequestRepository();
        }
    }
    

    【讨论】:

    • 事实并非如此,正如文档所述,AuthorizationRequestRepository 用于存储 OAuth2AuthorizationRequest 的属性,即 clientId、redirectUri、范围和状态。这被发送到授权服务器以获得授权代码,然后以 OAuth2AuthorizationCodeGrantRequest 的形式发送以获取 OAuth2AccessTokenResponse 模型中的访问和刷新令牌。在最后一步中,框架使用 InMemoryOAuth2AuthorizedClientService 将令牌保存在 OAuth2AuthorizedClient 模型中,因此这是我希望覆盖的部分。
    猜你喜欢
    • 1970-01-01
    • 2016-08-12
    • 2019-03-03
    • 2020-12-21
    • 2017-03-20
    • 2012-10-22
    • 1970-01-01
    • 2015-11-05
    • 2013-06-20
    相关资源
    最近更新 更多