Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Y
yudao-cloud
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
hblj
yudao-cloud
Commits
bfb15aea
提交
bfb15aea
authored
6月 04, 2022
作者:
YunaiV
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
gateway 完成使用 TokenAuthenticationFilter 实现身份验证的功能
上级
e5fed46a
隐藏空白字符变更
内嵌
并排
正在显示
8 个修改的文件
包含
146 行增加
和
80 行删除
+146
-80
TokenAuthenticationFilter.java
...ework/security/core/filter/TokenAuthenticationFilter.java
+29
-17
SecurityFrameworkUtils.java
.../framework/security/core/util/SecurityFrameworkUtils.java
+2
-0
pom.xml
yudao-gateway/pom.xml
+27
-4
GatewayServerApplication.java
...va/cn/iocoder/yudao/gateway/GatewayServerApplication.java
+0
-5
TmpConfiguration.java
...ava/cn/iocoder/yudao/gateway/config/TmpConfiguration.java
+0
-16
TokenAuthenticationFilter.java
...coder/yudao/gateway/filter/TokenAuthenticationFilter.java
+20
-36
SecurityFrameworkUtils.java
...cn/iocoder/yudao/gateway/util/SecurityFrameworkUtils.java
+27
-2
WebFrameworkUtils.java
...java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java
+41
-0
没有找到文件。
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
浏览文件 @
bfb15aea
...
...
@@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil;
import
cn.hutool.core.util.StrUtil
;
import
cn.iocoder.yudao.framework.common.exception.ServiceException
;
import
cn.iocoder.yudao.framework.common.pojo.CommonResult
;
import
cn.iocoder.yudao.framework.common.util.json.JsonUtils
;
import
cn.iocoder.yudao.framework.common.util.servlet.ServletUtils
;
import
cn.iocoder.yudao.framework.security.config.SecurityProperties
;
import
cn.iocoder.yudao.framework.security.core.LoginUser
;
...
...
@@ -41,28 +42,34 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
@SuppressWarnings
(
"NullableProblems"
)
protected
void
doFilterInternal
(
HttpServletRequest
request
,
HttpServletResponse
response
,
FilterChain
chain
)
throws
ServletException
,
IOException
{
String
token
=
SecurityFrameworkUtils
.
obtainAuthorization
(
request
,
securityProperties
.
getTokenHeader
());
if
(
StrUtil
.
isNotEmpty
(
token
))
{
Integer
userType
=
WebFrameworkUtils
.
getLoginUserType
(
request
);
try
{
// 1.1 基于 token 构建登录用户
LoginUser
loginUser
=
buildLoginUserByToken
(
token
,
userType
);
// 1.2 模拟 Login 功能,方便日常开发调试
if
(
loginUser
==
null
)
{
loginUser
=
mockLoginUser
(
request
,
token
,
userType
);
}
// 情况一,基于 header[login-user] 获得用户,例如说来自 Gateway 或者其它服务透传
LoginUser
loginUser
=
buildLoginUserByHeader
(
request
);
// 2. 设置当前用户
if
(
loginUser
!=
null
)
{
SecurityFrameworkUtils
.
setLoginUser
(
loginUser
,
request
);
// 情况二,基于 Token 获得用户
// 注意,这里主要满足直接使用 Nginx 直接转发到 Spring Cloud 服务的场景。
if
(
loginUser
==
null
)
{
String
token
=
SecurityFrameworkUtils
.
obtainAuthorization
(
request
,
securityProperties
.
getTokenHeader
());
if
(
StrUtil
.
isNotEmpty
(
token
))
{
Integer
userType
=
WebFrameworkUtils
.
getLoginUserType
(
request
);
try
{
// 1.1 基于 token 构建登录用户
loginUser
=
buildLoginUserByToken
(
token
,
userType
);
// 1.2 模拟 Login 功能,方便日常开发调试
if
(
loginUser
==
null
)
{
loginUser
=
mockLoginUser
(
request
,
token
,
userType
);
}
}
catch
(
Throwable
ex
)
{
CommonResult
<?>
result
=
globalExceptionHandler
.
allExceptionHandler
(
request
,
ex
);
ServletUtils
.
writeJSON
(
response
,
result
);
return
;
}
}
catch
(
Throwable
ex
)
{
CommonResult
<?>
result
=
globalExceptionHandler
.
allExceptionHandler
(
request
,
ex
);
ServletUtils
.
writeJSON
(
response
,
result
);
return
;
}
}
// 设置当前用户
if
(
loginUser
!=
null
)
{
SecurityFrameworkUtils
.
setLoginUser
(
loginUser
,
request
);
}
// 继续过滤链
chain
.
doFilter
(
request
,
response
);
}
...
...
@@ -113,4 +120,9 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
.
setTenantId
(
WebFrameworkUtils
.
getTenantId
(
request
));
}
private
LoginUser
buildLoginUserByHeader
(
HttpServletRequest
request
)
{
String
loginUserStr
=
request
.
getHeader
(
SecurityFrameworkUtils
.
LOGIN_USER_HEADER
);
return
StrUtil
.
isNotEmpty
(
loginUserStr
)
?
JsonUtils
.
parseObject
(
loginUserStr
,
LoginUser
.
class
)
:
null
;
}
}
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java
浏览文件 @
bfb15aea
...
...
@@ -22,6 +22,8 @@ public class SecurityFrameworkUtils {
public
static
final
String
AUTHORIZATION_BEARER
=
"Bearer"
;
public
static
final
String
LOGIN_USER_HEADER
=
"login-user"
;
private
SecurityFrameworkUtils
()
{}
/**
...
...
yudao-gateway/pom.xml
浏览文件 @
bfb15aea
...
...
@@ -35,10 +35,10 @@
<artifactId>
spring-cloud-starter-loadbalancer
</artifactId>
</dependency>
<dependency
>
<groupId>
org.springframework.cloud
</groupId
>
<artifactId>
spring-cloud-starter-openfeign
</artifactId
>
</dependency
>
<!-- <dependency>--
>
<!-- <groupId>org.springframework.cloud</groupId>--
>
<!-- <artifactId>spring-cloud-starter-openfeign</artifactId>--
>
<!-- </dependency>--
>
<!-- Registry 注册中心相关 -->
<dependency>
...
...
@@ -48,4 +48,27 @@
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>
${project.artifactId}
</finalName>
<plugins>
<!-- 打包 -->
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
<version>
2.6.7
</version>
<!-- 如果 spring.boot.version 版本修改,则这里也要跟着修改 -->
<configuration>
<fork>
true
</fork>
</configuration>
<executions>
<execution>
<goals>
<goal>
repackage
</goal>
<!-- 将引入的 jar 打入其中 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/GatewayServerApplication.java
浏览文件 @
bfb15aea
package
cn
.
iocoder
.
yudao
.
gateway
;
import
cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.cloud.openfeign.EnableFeignClients
;
@SpringBootApplication
@EnableFeignClients
(
clients
=
{
OAuth2TokenApi
.
class
})
// TODO 芋艿:需要改下
public
class
GatewayServerApplication
{
public
static
void
main
(
String
[]
args
)
{
...
...
yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/config/TmpConfiguration.java
deleted
100644 → 0
浏览文件 @
e5fed46a
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
();
}
}
yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/TokenAuthenticationFilter.java
浏览文件 @
bfb15aea
...
...
@@ -4,11 +4,11 @@ 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.gateway.util.WebFrameworkUtils
;
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.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction
;
import
org.springframework.cloud.gateway.filter.GatewayFilterChain
;
import
org.springframework.cloud.gateway.filter.GlobalFilter
;
import
org.springframework.core.Ordered
;
...
...
@@ -29,14 +29,21 @@ import java.util.function.Function;
*
* @author 芋道源码
*/
@Component
// TODO 芋艿:要改成 configuration
@Component
public
class
TokenAuthenticationFilter
implements
GlobalFilter
,
Ordered
{
// @Resource
// private OAuth2TokenApi oauth2TokenApi
;
private
static
final
TypeReference
<
CommonResult
<
OAuth2AccessTokenCheckRespDTO
>>
CHECK_RESULT_TYPE_REFERENCE
=
new
TypeReference
<
CommonResult
<
OAuth2AccessTokenCheckRespDTO
>>()
{}
;
@Resource
private
WebClient
webClient
;
private
final
WebClient
webClient
;
public
TokenAuthenticationFilter
(
ReactorLoadBalancerExchangeFilterFunction
lbFunction
)
{
// Q:为什么不使用 OAuth2TokenApi 进行调用?
// A1:Spring Cloud OpenFeign 官方未内置 Reactive 的支持 https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#reactive-support
// A2:校验 Token 的 API 需要使用到 header[tenant-id] 传递租户编号,暂时不想编写 RequestInterceptor 实现
// 因此,这里采用 WebClient,通过 lbFunction 实现负载均衡
this
.
webClient
=
WebClient
.
builder
().
filter
(
lbFunction
).
build
();
}
@Override
public
Mono
<
Void
>
filter
(
final
ServerWebExchange
exchange
,
GatewayFilterChain
chain
)
{
...
...
@@ -46,46 +53,23 @@ public class TokenAuthenticationFilter implements GlobalFilter, Ordered {
return
chain
.
filter
(
exchange
);
}
// exchange = exchange.mutate().request(r -> r.headers(new Consumer<HttpHeaders>() {
// @Override
// public void accept(HttpHeaders headers) {
// headers.set("user-id", "1");
// }
// })).build();
// return Mono.fromCallable(new Callable<CommonResult<OAuth2AccessTokenCheckRespDTO>>() {
// @Override
// public CommonResult<OAuth2AccessTokenCheckRespDTO> call() throws Exception {
//// return oauth2TokenApi.checkAccessToken("1234");
// return CommonResult.success(new OAuth2AccessTokenCheckRespDTO().setUserId(1L));
// }
// }).subscribeOn(Schedulers.boundedElastic()).flatMap(new Function<CommonResult<OAuth2AccessTokenCheckRespDTO>, Mono<Void>>() {
// @Override
// public Mono<Void> apply(CommonResult<OAuth2AccessTokenCheckRespDTO> 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
)
.
header
s
(
httpHeaders
->
WebFrameworkUtils
.
setTenantIdHeader
(
exchange
,
httpHeaders
))
// 设置租户的 Header
.
retrieve
().
bodyToMono
(
String
.
class
)
// 发起请求,设置 body 为 String 结果
// 处理请求的结果
.
flatMap
((
Function
<
String
,
Mono
<
Void
>>)
body
->
chain
.
filter
(
buildNewServerWebExchange
(
exchange
,
body
)));
.
flatMap
((
Function
<
String
,
Mono
<
Void
>>)
body
->
chain
.
filter
(
buildNewServerWebExchange
(
exchange
,
body
)));
// 处理请求的结果
}
private
ServerWebExchange
buildNewServerWebExchange
(
ServerWebExchange
exchange
,
String
body
)
{
// 校验 Token 令牌失败,则直接返回
CommonResult
<
?>
result
=
JsonUtils
.
parseObject
(
body
,
CommonResult
.
class
);
CommonResult
<
OAuth2AccessTokenCheckRespDTO
>
result
=
JsonUtils
.
parseObject
(
body
,
CHECK_RESULT_TYPE_REFERENCE
);
if
(
result
==
null
||
result
.
isError
())
{
return
exchange
;
}
// 创建新的 exchange 对象
return
exchange
.
mutate
().
request
(
builder
->
builder
.
header
(
"login-user"
,
result
.
getData
().
toString
())).
build
();
// 将访问令牌封装成 LoginUser,并设置到 login-user 的请求头,使用 json 存储值
return
exchange
.
mutate
().
request
(
builder
->
SecurityFrameworkUtils
.
setLoginUserHeader
(
builder
,
result
.
getData
())).
build
();
}
@Override
...
...
yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/SecurityFrameworkUtils.java
浏览文件 @
bfb15aea
package
cn
.
iocoder
.
yudao
.
gateway
.
util
;
import
cn.hutool.core.map.MapUtil
;
import
cn.iocoder.yudao.framework.common.util.json.JsonUtils
;
import
cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO
;
import
org.springframework.http.server.reactive.ServerHttpRequest
;
import
org.springframework.util.StringUtils
;
import
org.springframework.web.server.ServerWebExchange
;
import
java.util.Map
;
/**
* 安全服务工具类
*
...
...
@@ -12,9 +18,11 @@ import org.springframework.web.server.ServerWebExchange;
*/
public
class
SecurityFrameworkUtils
{
public
static
final
String
AUTHORIZATION_HEADER
=
"Authorization"
;
private
static
final
String
AUTHORIZATION_HEADER
=
"Authorization"
;
private
static
final
String
AUTHORIZATION_BEARER
=
"Bearer"
;
p
ublic
static
final
String
AUTHORIZATION_BEARER
=
"Bear
er"
;
p
rivate
static
final
String
LOGIN_USER_HEADER
=
"login-us
er"
;
private
SecurityFrameworkUtils
()
{}
...
...
@@ -36,4 +44,21 @@ public class SecurityFrameworkUtils {
return
authorization
.
substring
(
index
+
7
).
trim
();
}
/**
* 将访问令牌封装成 LoginUser,并设置到 login-user 的请求头,使用 json 存储值
*
* @param builder 请求
* @param token 访问令牌
*/
public
static
void
setLoginUserHeader
(
ServerHttpRequest
.
Builder
builder
,
OAuth2AccessTokenCheckRespDTO
token
)
{
// 构建 LoginUser 对象。由于 Gateway 没有 loginUser 类,所以使用 Map
Map
<
String
,
Object
>
loginUser
=
MapUtil
.
newHashMap
(
4
);
loginUser
.
put
(
"id"
,
token
.
getUserId
());
loginUser
.
put
(
"userType"
,
token
.
getUserType
());
loginUser
.
put
(
"tenantId"
,
token
.
getTenantId
());
loginUser
.
put
(
"scopes"
,
token
.
getScopes
());
// 设置到 Header 中
builder
.
header
(
LOGIN_USER_HEADER
,
JsonUtils
.
toJsonString
(
loginUser
));
}
}
yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java
0 → 100644
浏览文件 @
bfb15aea
package
cn
.
iocoder
.
yudao
.
gateway
.
util
;
import
cn.hutool.core.map.MapUtil
;
import
cn.hutool.core.util.StrUtil
;
import
cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.server.reactive.ServerHttpRequest
;
import
org.springframework.web.server.ServerWebExchange
;
import
java.util.HashMap
;
import
java.util.Map
;
/**
* Web 工具类
*
* copy from yudao-spring-boot-starter-web 的 WebFrameworkUtils 类
*
* @author 芋道源码
*/
public
class
WebFrameworkUtils
{
@SuppressWarnings
(
"UastIncorrectHttpHeaderInspection"
)
private
static
final
String
HEADER_TENANT_ID
=
"tenant-id"
;
private
WebFrameworkUtils
()
{}
/**
* 将 Gateway 请求中的 header,设置到 HttpHeaders 中
*
* @param exchange Gateway 请求
* @param httpHeaders WebClient 的请求
*/
public
static
void
setTenantIdHeader
(
ServerWebExchange
exchange
,
HttpHeaders
httpHeaders
)
{
String
tenantId
=
exchange
.
getRequest
().
getHeaders
().
getFirst
(
HEADER_TENANT_ID
);
if
(
StrUtil
.
isNotEmpty
(
tenantId
))
{
return
;
}
httpHeaders
.
set
(
HEADER_TENANT_ID
,
tenantId
);
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论