From e5fed46ae14ce1b0ef8878087d8e418e7fb22535 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 4 Jun 2022 10:06:09 +0800 Subject: [PATCH] =?UTF-8?q?gateway=20=E5=BC=95=E5=85=A5=20webclient?= =?UTF-8?q?=EF=BC=8C=E5=AE=9E=E7=8E=B0=E5=AF=B9=20oauth2=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E7=9A=84=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/config/TmpConfiguration.java | 16 +++++ .../filter/TokenAuthenticationFilter.java | 72 ++++++++++++++++--- .../system/api/oauth2/OAuth2TokenApi.java | 18 +++-- .../module/system/enums/ApiConstants.java | 11 ++- .../system/api/oauth2/OAuth2TokenApiImpl.java | 6 +- .../controller/admin/auth/AuthController.http | 6 +- .../config/SecurityConfiguration.java | 2 +- 7 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/config/TmpConfiguration.java diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/config/TmpConfiguration.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/config/TmpConfiguration.java new file mode 100644 index 000000000..0d00d3288 --- /dev/null +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/config/TmpConfiguration.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.gateway.config; + +import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class TmpConfiguration { + + @Bean + public WebClient webClient(ReactorLoadBalancerExchangeFilterFunction lbFunction) { + return WebClient.builder().filter(lbFunction).build(); + } + +} diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/TokenAuthenticationFilter.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/TokenAuthenticationFilter.java index 7113b88da..b7ad27f2e 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/TokenAuthenticationFilter.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/TokenAuthenticationFilter.java @@ -1,16 +1,26 @@ package cn.iocoder.yudao.gateway.filter; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.gateway.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi; +import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO; +import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO; +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.net.HttpHeaders; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; -import org.springframework.http.HttpHeaders; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import javax.annotation.Resource; import java.util.function.Consumer; +import java.util.function.Function; /** * Token 过滤器,验证 token 的有效性 @@ -22,18 +32,60 @@ import java.util.function.Consumer; @Component // TODO 芋艿:要改成 configuration public class TokenAuthenticationFilter implements GlobalFilter, Ordered { +// @Resource +// private OAuth2TokenApi oauth2TokenApi; + @Resource - private OAuth2TokenApi oauth2TokenApi; + private WebClient webClient; @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - exchange = exchange.mutate().request(r -> r.headers(new Consumer() { - @Override - public void accept(HttpHeaders headers) { - headers.set("user-id", "1"); - } - })).build(); - return chain.filter(exchange); + public Mono filter(final ServerWebExchange exchange, GatewayFilterChain chain) { + String token = SecurityFrameworkUtils.obtainAuthorization(exchange); + // 情况一,如果没有 Token 令牌,则直接继续 filter + if (StrUtil.isEmpty(token)) { + return chain.filter(exchange); + } + +// exchange = exchange.mutate().request(r -> r.headers(new Consumer() { +// @Override +// public void accept(HttpHeaders headers) { +// headers.set("user-id", "1"); +// } +// })).build(); + + +// return Mono.fromCallable(new Callable>() { +// @Override +// public CommonResult call() throws Exception { +//// return oauth2TokenApi.checkAccessToken("1234"); +// return CommonResult.success(new OAuth2AccessTokenCheckRespDTO().setUserId(1L)); +// } +// }).subscribeOn(Schedulers.boundedElastic()).flatMap(new Function, Mono>() { +// @Override +// public Mono apply(CommonResult oAuth2AccessTokenCheckRespDTOCommonResult) { +// return chain.filter(exchange); +// } +// }); + + // 情况二,如果有 Token 令牌,则解析对应 userId、userType、tenantId 等字段,并通过 通过 Header 转发给服务 + // TODO 芋艿:tenant-id + String tenantId = exchange.getRequest().getHeaders().getFirst("tenant-id"); + return webClient.get() + .uri(OAuth2TokenApi.URL_CHECK, uriBuilder -> uriBuilder.queryParam("accessToken", token).build()) + .header("tenant-id", tenantId) + .retrieve().bodyToMono(String.class) // 发起请求,设置 body 为 String 结果 + // 处理请求的结果 + .flatMap((Function>) body -> chain.filter(buildNewServerWebExchange(exchange, body))); + } + + private ServerWebExchange buildNewServerWebExchange(ServerWebExchange exchange, String body) { + // 校验 Token 令牌失败,则直接返回 + CommonResult result = JsonUtils.parseObject(body, CommonResult.class); + if (result == null || result.isError()) { + return exchange; + } + // 创建新的 exchange 对象 + return exchange.mutate().request(builder -> builder.header("login-user", result.getData().toString())).build(); } @Override diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApi.java index 1da3958b9..e81869a40 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApi.java @@ -14,27 +14,33 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; -@FeignClient(name = "system-server") // TODO 芋艿:fallbackFactory = +@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = @Api(tags = "RPC 服务 - OAuth2.0 令牌") public interface OAuth2TokenApi { - String API_PREFIX = ApiConstants.API_PREFIX + "/oauth2/token"; + String PREFIX = ApiConstants.PREFIX + "/oauth2/token"; - @PostMapping(API_PREFIX + "/create") + /** + * 校验 Token 的 URL 地址,主要是提供给 Gateway 使用 + */ + @SuppressWarnings("HttpUrlsUsage") + String URL_CHECK = "http://" + ApiConstants.NAME + PREFIX + "/check"; + + @PostMapping(PREFIX + "/create") @ApiOperation("创建访问令牌") CommonResult createAccessToken(@Valid @RequestBody OAuth2AccessTokenCreateReqDTO reqDTO); - @GetMapping(API_PREFIX + "/check") + @GetMapping(PREFIX + "/check") @ApiOperation("校验访问令牌") @ApiImplicitParam(name = "accessToken", value = "访问令牌", required = true, dataTypeClass = String.class, example = "tudou") CommonResult checkAccessToken(@RequestParam("accessToken") String accessToken); - @DeleteMapping(API_PREFIX + "/remove") + @DeleteMapping(PREFIX + "/remove") @ApiOperation("移除访问令牌") @ApiImplicitParam(name = "accessToken", value = "访问令牌", required = true, dataTypeClass = String.class, example = "tudou") CommonResult removeAccessToken(@RequestParam("accessToken") String accessToken); - @PutMapping(API_PREFIX + "/refresh") + @PutMapping(PREFIX + "/refresh") @ApiOperation("刷新访问令牌") @ApiImplicitParams({ @ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class, example = "haha"), diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ApiConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ApiConstants.java index 0b035a93a..7610a6f25 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ApiConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ApiConstants.java @@ -7,8 +7,15 @@ package cn.iocoder.yudao.module.system.enums; */ public class ApiConstants { - public static final String API_PREFIX = "/rpc-api/system"; + /** + * 服务名 + * + * 注意,需要保证和 spring.application.name 保持一致 + */ + public static final String NAME = "system-server"; - public static final String API_VERSION = "1.0.0"; + public static final String PREFIX = "/rpc-api/system"; + + public static final String VERSION = "1.0.0"; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java index 49447b9e6..256c9202c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java @@ -7,20 +7,18 @@ import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert; import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; -import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.dubbo.config.annotation.DubboService; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.module.system.enums.ApiConstants.API_VERSION; +import static cn.iocoder.yudao.module.system.enums.ApiConstants.VERSION; @RestController // 提供 RESTful API 接口,给 Feign 调用 -@DubboService(version = API_VERSION) // 提供 Dubbo RPC 接口,给 Dubbo Consumer 调用 +@DubboService(version = VERSION) // 提供 Dubbo RPC 接口,给 Dubbo Consumer 调用 @Validated public class OAuth2TokenApiImpl implements OAuth2TokenApi { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http index f57ca59a0..a155da83e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http @@ -26,7 +26,7 @@ Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} ### 请求 /list-menus 接口 => 成功 -GET {{systemBaseUrl}}/system/list-menus -Authorization: Bearer {{token}} -#Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a +GET {{systemBaseUrl}}/system/auth/list-menus +#Authorization: Bearer {{token}} +Authorization: Bearer 81e64ecd759a410ca54d3f00bdeb4574 tenant-id: {{adminTenentId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java index 87443acef..e7794420c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java @@ -50,7 +50,7 @@ public class SecurityConfiguration { registry.antMatchers("/actuator").anonymous() .antMatchers("/actuator/**").anonymous(); // RPC 服务的安全配置 - registry.antMatchers(ApiConstants.API_PREFIX + "/**").anonymous(); + registry.antMatchers(ApiConstants.PREFIX + "/**").permitAll(); } };