【问题标题】:Spring Cloud Gateway to enhance JWT tokens from external OAuth Authorization ServerSpring Cloud Gateway 增强来自外部 OAuth 授权服务器的 JWT 令牌
【发布时间】:2020-02-22 16:34:18
【问题描述】:

我有以下Securing Services with Spring Cloud Gateway 与 Keycloak 合作。在此基础上,我将路由到许多 Webflux 资源服务器。

但是,外部授权服务器提供的范围非常简单。因此,我需要从 JWT 获取微服务 ID 和用户 ID,然后检查一个数据库,然后将其转换为他们的团队,然后该团队将提供可访问的静态页面和 URL REST 端点的列表。所以我得到的代码会像这样;

...
  .matcher("/page1.html").hasAuthority("SERVICE1_PAGE1")
  .matcher("/page2.html").hasAuthority("SERVICE1_PAGE2")


@PreXXX("hasAuthority('SERVICE1_getAll'))
public List<String> getAll() {...}

我假设我可以通过Extracting Authorities Manually 在微服务级别完成此操作,但这样做我需要在每个微服务中复制此代码。

编辑:这就是我目前正在做的事情,通过使用WebClient 调用另一个返回更正权限的常见微服务。但是,如果权限微服务的 URL 是网关地址,网关永远不会尝试解析该 URL。如果我使用它的显式 URL,即使我使用 ServerBearerExchangeFilterFunction,我也会从权限微服务中获得 401。如果我将permitAll() 放在返回权限的微服务上,它就可以工作。

我可以建立自己的授权服务器并使用TokenEnhancer。但是,通过这样做,我假设我需要为每个可能的微服务加载用户的所有可能权限(因为我现在不知道用户要去哪里)并且可能会导致大量数据.

理想情况下,我想将其集中在网关中,并让TokenRelay 过滤器识别路由并以某种方式增强令牌。这可能吗?

有人可以推荐最好的方法吗?

【问题讨论】:

    标签: spring cloud spring-webflux api-gateway


    【解决方案1】:

    这是我最终使用 Spring 2.2.2 Cloud Gateway、Eureka 和 KeyCloak 实现的。

    安全微服务。

    @GetMapping(value = "/grantedAuthorities/{applicationName}/{userId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
        // JWT not sent when called within jwtAuthenticationConverter, so pass user id as param !!
    public Flux<String> getUsersApplicationAuthorities(@PathVariable String applicationName, @PathVariable String userId) {
        return Flux.fromIterable(roleRepository.getRolesByUserId(applicationName, userId));
    }
    

    这会返回类似的东西;

    SCREEN1_READ
    SCREEN2_WRITE
    

    我的其他微服务。

    public class SecurityConfig {
        @Bean
        @Order(SecurityProperties.BASIC_AUTH_ORDER)
        SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
            http
                .authorizeExchange(exchanges ->
                    exchanges                   
                        .pathMatchers("/screen1.html").hasAnyAuthority("SCREEN1_READ", "SCREEN1_WRITE")
                        .pathMatchers("/screen2.html").hasAnyAuthority("SCREEN1_READ", "SCREEN2_WRITE")
                        .anyExchange().authenticated()
                )
                .oauth2ResourceServer(spec ->
                    spec.jwt().jwtAuthenticationConverter(jwt -> {
    
                    /* ServerBearerExchangeFilterFunction does not work here! So I have to send userId instead of JWT */
    
                        WebClient webClient = loadBalancedWebClientBuilder().build();
                        String userId =  jwt.getClaimAsString("preferred_username").toUpperCase();
                        String uri = "lb://SECURITY/drs/grantedAuthorities/" + applicationName + "/" + userId;
                        return webClient.get().uri(uri)
                                .retrieve()
                                .bodyToFlux(String.class)
                                .map(s -> new SimpleGrantedAuthority(s))
                                .doOnNext(l -> log.info("Has authority " + l))
                                .collectList()
                                .map(gaList -> new JwtAuthenticationToken(jwt, gaList));
                    })
                )
                .csrf().disable()
                .cors().disable();
    
            return http.build();
        }
    }
    

    用于在 READ/WRITE for Thymeleaf 时启用/禁用按钮;

    @Controller
    public class PageController {
        @GetMapping("/screen1.html")
        public String index(@AuthenticationPrincipal JwtAuthenticationToken jwt, Model model) {
            model.addAttribute("update", jwt.getAuthorities().contains(new SimpleGrantedAuthority("SCREEN1_WRITE"))); 
            return "screen1";
        }
    }
    
    <html>
        <body>
            <div id="updateableScreen" th:attr="data-update=${update}"></div>   
        </body>
    </html>
    

    之后使用 JQuery/Javascript 通过检查 $("#updateableScreen") 来更改对表单控件的访问

    【讨论】:

      猜你喜欢
      • 2019-10-08
      • 2015-07-31
      • 2018-07-19
      • 2014-07-09
      • 1970-01-01
      • 2020-05-09
      • 1970-01-01
      • 2018-02-19
      • 2016-05-28
      相关资源
      最近更新 更多