【问题标题】:Extra authentication step for OAuth2/OIDC client authorisations from multiple auth servers来自多个身份验证服务器的 OAuth2/OIDC 客户端授权的额外身份验证步骤
【发布时间】:2020-01-10 00:20:20
【问题描述】:

我目前设置了一个 Spring Cloud Gateway 反向代理,目的是:

a) 处理多个 OAuth/OIDC 提供者的身份验证,包括获取 Token

b) 在本地查找提供商的详细信息,确保 OAuth 用户 x Oauth 提供商组合已获得授权

c) 如果获得授权,请查看 Grants/Permissions,并将请求转发给 SCG,并使用包含授权委托人详细信息的 JWT。

d) 如果未经授权,则显示一个页面,显示来自 OAuth2 Auth 的相关详细信息,并说明它们未经授权。

我已经完成了大部分步骤,但是我无法将步骤 c) 合并到 Spring Security Webflux

我要做的是获取从 Authentication 交换中获得的 OAuth2AuthenticationToken,在 step 中执行查找,并返回一个 根据结果​​定制校长。

这将通过代码用于触发 SCG 行为或显示页面。

spring-boot.version>2.1.6.RELEASE spring-cloud.version>Greenwich.SR2

我的问题是我不知道最好的方法。

  1. 在 OAuth2 客户端中使用一些挂钩来执行额外的身份验证步骤。在这种情况下,我可能需要返回 OAuth2Principal

  2. 在身份验证后将额外的安全过滤器添加到链中。 这将用我自己的 prncipal 替换 OAuth2Principal。我不确定在经过身份验证后替换 Principal 是否合法,可能会删除身份验证状态

  3. 编写将代理到 OAuth 客户端的自定义 AuthN 提供程序,并在完成后运行它自己的逻辑,然后发出已通过身份验证的信号。这似乎是一种复杂的方法,我不确定我会为此使用哪些类。

我已阅读 Spring Security 文档,了解 Spring Security 的一般架构,但无法找到解决此问题的最佳方法。

这是我的 spring 安全过滤器逻辑

@EnableWebFluxSecurity
public class SecurityConfig {
    @Bean
    @Profile("oauth")
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        return addAuthZ(http)
            .oauth2Login()
            .and().build();
    }

    private ServerHttpSecurity addAuthZ(ServerHttpSecurity http) {
        return http.authorizeExchange()
                .anyExchange().authenticated().and();
    }
}

这是配置,我正在使用示例 OAuth2 提供程序 Google 和 Facebook,以及使用 CAS 提供的自定义 OAuth2 提供程序

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: SET_ME
            client-secret: SET_ME
          facebook:
            client-id: SET_ME
            client-secret: SET_ME
          sgd-authn:
            provider: sgd-authn
            client-id: SET_ME
            client-secret: SET_ME
            scope: openid
            client-authentication-method: secret
            authorization-grant-type: authorization_code
            #redirect-uri: "{baseUrl}/oauth2/
            redirect-uri-template: "{baseUrl}/{action}/oauth2/code/{registrationId}"
        provider:
          #  These are needed for talking to CAS OIDC
          sgd-authn:
            authorization-uri: ${cas.url}/oidc/authorize
            token-uri: ${cas.url}/oidc/accessToken
            jwk-set-uri: ${cas.url}/oidc/jwks
            user-info-uri: ${cas.url}/oidc/profile
            user-name-attribute: sub

【问题讨论】:

    标签: java spring spring-security spring-security-oauth2


    【解决方案1】:

    好吧,我设法使用选项 2 使某些东西正常工作,在身份验证后添加一个过滤器。

    • 编写一个接受身份验证 (OAuth) 的过滤器,通过一些逻辑运行它,并返回一个新的身份验证对象,该对象是特定超类的实例

    • 如果我们无法查找 OAuth2 详细信息,请安排它返回 isAuthenticated = false。

    • 为 NotAuthenticated 编写定制处理程序

    过滤器是这样注册的:

        @Bean
        public SecurityWebFilterChain springSecurityFilterChain(
                ServerHttpSecurity http,
                WebFilter proxyAuthFilter
        ) {
            return http.addFilterAt(proxyAuthFilter, SecurityWebFiltersOrder.AUTHENTICATION); // Do configuration ...
        }
    

    过滤器是这样的:

    public class ProxyAuthFilter implements WebFilter {
        static private Logger LOGGER = LoggerFactory.getLogger(ProxyAuthFilter.class);
    
        private final AuthZClientReactive authZClient;
    
        public ProxyAuthFilter(AuthZClientReactive authZClient) {
            this.authZClient = authZClient;
        }
    
    
    
        /**
         * Process the Web request and (optionally) delegate to the next
         * {@code WebFilter} through the given {@link WebFilterChain}.
         *
         * @param exchange the current server exchange
         * @param chain    provides a way to delegate to the next filter
         * @return {@code Mono<Void>} to indicate when request processing is complete
         */
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            return ReactiveSecurityContextHolder.getContext()
                    .flatMap(this::transform)
                    .then(chain.filter(exchange));
        }
    
        private Mono<Authentication> transform(SecurityContext securityContext) {
            Authentication authentication = securityContext.getAuthentication();
            if (authentication.isAuthenticated()) {
                return authenticate(authentication)
                        .map(a -> {
                            securityContext.setAuthentication(a);
                            return a;
                        });
            } else {
                LOGGER.info("ProxyFilter - not authenticated {}", authentication);
                return Mono.just(authentication);
            }
        }
    
        //  Runs the chain, then returns a Mono with the exchange object which completes
        //  when the auth header is added to the request.
        private Mono<Authentication> authenticate(Authentication authentication) {
            LOGGER.info("ProxyFilter - Checking authorisation for {}", authentication);
            Mono<AuthTokenInfo.Builder> authInfo = null;
            //  Catch if this filter runs twice
            if (authentication instanceof ProxyAuthentication) {
                LOGGER.info("ProxyAuthentication already found");
                return Mono.just(authentication);
            } else if (authentication instanceof UsernamePasswordAuthenticationToken) {
                //  If lookup is successful, returns instance of  ProxyAuthentication
                return getUsernamePasswordAuth((UsernamePasswordAuthenticationToken) authentication);
            } else if (authentication instanceof OAuth2AuthenticationToken) {
                //  If lookup is successful, returns instance of  ProxyAuthentication
                return getOAuthAuth((OAuth2AuthenticationToken) authentication);
            } else {
                LOGGER.info("Unknown principal {}", authentication);
                //  Signals a failed authentication, can be picked up by error page
                //  to display bespoke information 
                return Mono.just(new ProxyAuthenticationNotAuthenticated(
                        authentication, ProxyAuthenticationNotAuthenticated.Reason.UnknownAuthenticationType)
                );
            }
        }
    
    

    【讨论】:

      猜你喜欢
      • 2022-06-23
      • 2011-11-04
      • 1970-01-01
      • 2012-08-28
      • 1970-01-01
      • 2018-07-14
      • 2013-07-20
      • 2021-02-27
      • 2012-04-15
      相关资源
      最近更新 更多