繁体   English   中英

网关 + 后端服务的 OAuth2 流

[英]OAuth2 flow for Gateway + Backend Service

我目前正在使用 Spring-Boot-Web 结合 OAuth2 在 AWS 上使用 Amazon-Cognito 构建一个 Web 应用程序。

我的目标设置如下所示: 在此处输入图像描述

在“我的数据中心”中,我运行了 2 个应用程序:

  • 网关应用
  • 后端应用

网关应用程序可从 Internet 访问。 它服务于前端(html、css、js),包含一些显示逻辑,也许还有一些每个人都可以访问的 API。 它是“授权代码授予”-OAuth 2.0 流程的发起者(如果用户尚未登录,它会将用户重定向到 Amazon-Cognito)。

后端应用程序只能由网关应用程序访问。 它无法从外部访问。 在后端应用程序中,我想从 Amazon-Cognito 检索用户详细信息(姓名、电子邮件)。

我现在要做的事情:

我在 Amazon-Cognito 中为 Gateway-Application 注册了一个客户端。 网关应用程序启动“授权代码授予”流程,并可以从 Amazon-Cognito 访问用户信息。

我将网关应用程序配置为将 OAuth2-Authorization-Details 与我的所有 HTTP 请求一起传递给后端应用程序。 Backend-Application 可以成功检查用户是否通过身份验证。

网关-应用程序配置

应用程序.yml

spring:
  security:
    oauth2:
      client:
        registration:
          cognito:
            client-name: frontend-local
            client-id: #######
            client-secret: #########
            scope: openid,backend/read,backend/write
            redirect-uri: http://localhost:8080/login/oauth2/code/cognito
        provider:
          cognito:
            issuer-uri: https://cognito-idp.eu-central-1.amazonaws.com/<my-aws-pool-id>
            user-name-attribute: cognito:username

安全配置.java

package io.share.frontend.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

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

OAuth2WebClientConfiguration.java

package io.share.frontend.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class OAuth2WebClientConfiguration {

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) {

        OAuth2AuthorizedClientProvider authorizedClientProvider =
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .clientCredentials()
                        .build();

        DefaultOAuth2AuthorizedClientManager authorizedClientManager =
                new DefaultOAuth2AuthorizedClientManager(
                        clientRegistrationRepository, authorizedClientRepository);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }

    @Bean
    WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth2Client.setDefaultClientRegistrationId("cognito");
        return WebClient.builder()
                .apply(oauth2Client.oauth2Configuration())
                .build();
    }
}

后端应用程序配置

应用程序.yml

server:
  port: 8081

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://cognito-idp.eu-central-1.amazonaws.com/<my-aws-pool-id>

安全配置.java

package io.share.backend.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests().anyRequest().hasAuthority("SCOPE_backend/read")
                .and()
                .oauth2ResourceServer().jwt();
    }
}

我想上班

在我的Backend-Application中,我希望能够从 Amazon-Cognito 访问用户信息。 目前,我的后端应用程序中有以下基本 RestController:

package io.share.backend.web;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;

@RestController
public class ObjectResource {

    @GetMapping("/username")
    public String greeting(@AuthenticationPrincipal Principal user) {
        return user.getName();// this currently returns some UUID-Like string, I want to be able to access the username and E-Mail here, just like I can in the Gateway
    }
}

但我希望能够在后端访问真实的用户名和电子邮件。

我的问题

我是否必须在 Amazon-Cognito 中为后端创建一个新客户端? 还是我必须为网关和后端配置相同的客户端 ID 和客户端密码?

我必须在我的应用程序(网关和后端)中进行哪些额外的 Spring-Configurations 才能使其工作?

我正在使用 spring-boot 2.3.6.RELEASE

好问题,您在 API 中遇到了以下常见的 OAuth 问题:

  • Cognito 访问令牌包含非常少的用户信息,这是一种很好的做法 - 它们不可自定义,仅包含主题声明
  • Spring 想要仅从 JWT 创建一个 AuthenticatedPrincipal - 但是您的 API 还希望将其他声明添加到 AuthenticatedPrincipal

设计模式

您可以使用一种设计模式,或从中借鉴想法,我的博客文章总结了这种行为。 这个想法是首先定义一个声明主体,然后在运行时使用来自多个源的声明填充它。 在您的情况下,一个来源将是 Cognito 用户信息端点。

工作代码

您可以运行我的Sample Spring Boot API ,它也使用 AWS Cognito,密钥 class 是Authorizer 在某些设置中,您可能可以获得 API 网关来为您完成其中的一些工作:

优点和缺点

此模式将为您提供可扩展的声明,但也会为您的 API 增加一些复杂性,因为您需要覆盖 API 技术堆栈的默认 OAuth 行为。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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