Skip to content

Commit 16c0fb2

Browse files
committed
feat: 限制只有application/json格式的接口才支持加解密、签名
1 parent bedca26 commit 16c0fb2

18 files changed

+148
-52
lines changed

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ sign = RSA签名签名(AES加密(head的json串) + AES加密(body json串))
229229
```
230230

231231
# 五、错误码
232-
所属模块 | code | message
233-
---| ---|---
232+
所属模块 | code | message
233+
---|--------|---
234234
basic-service-user | 100001 | 账号不存在
235235
basic-service-user | 100002 | 用户被禁用
236236
basic-service-user | 100003 | 用户已被删除
@@ -256,6 +256,9 @@ support-service-gateway | 400011 | 请求时间戳格式错误
256256
support-service-gateway | 400012 | 请求时间戳非法
257257
support-service-gateway | 400013 | security key过期
258258
support-service-gateway | 400014 | AES key获取失败
259+
support-service-gateway | 400015 | 命中黑名单列表,禁止访问
260+
support-service-gateway | 400016 | 不在白名单中,禁止访问
261+
support-service-gateway | 400017 | 不支持数据安全
259262

260263
# FAQ
261264
## spring cloud gateway集成openfeign启动时卡死

application-module/api-ac-core/src/main/java/org/smartframework/cloud/examples/api/ac/core/util/ApiMetaUtil.java

+34-5
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,14 @@ public ApiMetaFetchRespVO collectApiMetas() {
9090
String[] urlTails = getUrlTails(method);
9191
for (String urlTail : urlTails) {
9292
String urlCode = getUrlCode(urlHeader, urlTail);
93+
DataSecurityMetaRespVO dataSecurityMeta = buildDataSecurityMeta(method);
94+
RepeatSubmitCheckMetaRespVO repeatSubmitCheckMeta = buildRepeatSubmitCheckMeta(method);
9395

9496
ApiAccessMetaRespVO apiAccessMeta = new ApiAccessMetaRespVO();
95-
apiAccessMeta.setAuthMeta(buildAuthMeta(method));
96-
apiAccessMeta.setDataSecurityMeta(buildDataSecurityMeta(method));
97-
apiAccessMeta.setRepeatSubmitCheckMeta(buildRepeatSubmitCheckMeta(method));
97+
apiAccessMeta.setDataSecurityMeta(dataSecurityMeta);
98+
apiAccessMeta.setRepeatSubmitCheckMeta(repeatSubmitCheckMeta);
9899
apiAccessMeta.setRequestValidMillis(getRequestValidMillis(method));
100+
apiAccessMeta.setAuthMeta(buildAuthMeta(method, repeatSubmitCheckMeta.getCheck(), dataSecurityMeta));
99101
apiAccessMap.put(urlCode, apiAccessMeta);
100102
}
101103
}
@@ -161,18 +163,45 @@ private DataSecurityMetaRespVO buildDataSecurityMeta(Method method) {
161163
* @param method
162164
* @return
163165
*/
164-
private AuthMetaRespVO buildAuthMeta(Method method) {
166+
private AuthMetaRespVO buildAuthMeta(Method method, boolean resubmitCheck, DataSecurityMetaRespVO dataSecurityMetaRespVO) {
165167
RequirePermissions requirePermissions = method.getAnnotation(RequirePermissions.class);
166168
RequireRoles requireRoles = method.getAnnotation(RequireRoles.class);
167169
RequireUser requireUser = method.getAnnotation(RequireUser.class);
168170

169171
AuthMetaRespVO authMeta = new AuthMetaRespVO();
170-
authMeta.setRequireUser(requireUser != null);
172+
authMeta.setRequireUser(isRequireUser(requirePermissions, requireRoles, requireUser, resubmitCheck, dataSecurityMetaRespVO));
171173
authMeta.setRequireRoles((requireRoles != null) ? requireRoles.value() : new String[0]);
172174
authMeta.setRequirePermissions((requirePermissions != null) ? requirePermissions.value() : new String[0]);
173175
return authMeta;
174176
}
175177

178+
/**
179+
* 是否需要登录校验
180+
*
181+
* @param requirePermissions
182+
* @param requireRoles
183+
* @param requireUser
184+
* @param resubmitCheck
185+
* @param dataSecurityMetaRespVO
186+
* @return
187+
*/
188+
private boolean isRequireUser(RequirePermissions requirePermissions, RequireRoles requireRoles, RequireUser requireUser,
189+
boolean resubmitCheck, DataSecurityMetaRespVO dataSecurityMetaRespVO) {
190+
if (requirePermissions != null || requireRoles != null || requireUser != null) {
191+
return true;
192+
}
193+
194+
if (resubmitCheck) {
195+
return true;
196+
}
197+
198+
if (dataSecurityMetaRespVO.getRequestDecrypt() || dataSecurityMetaRespVO.getResponseEncrypt()) {
199+
return true;
200+
}
201+
202+
return dataSecurityMetaRespVO.getSign() != SignType.NONE.getType();
203+
}
204+
176205
private Set<Method> getAllApiMethods(Reflections reflections) {
177206
Set<Method> allMappingSet = new HashSet<>();
178207
allMappingSet.addAll(reflections.getMethodsAnnotatedWith(RequestMapping.class));

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/cache/ApiAccessMetaCache.java

-5
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,6 @@ private void initRepeatSubmitCheck(RepeatSubmitCheckMetaRespVO repeatSubmitCheck
157157
}
158158
}
159159

160-
@JsonIgnore
161-
public boolean isAuth() {
162-
return requiresUser || requiresRoles.size() > 0 || requiresPermissions.size() > 0;
163-
}
164-
165160
@JsonIgnore
166161
public boolean isDataSecurity() {
167162
return requestDecrypt || responseEncrypt || signType != SignType.NONE.getType();

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/constants/GatewayReturnCodes.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public interface GatewayReturnCodes {
5454
*/
5555
String LOGIN_CACHE_MISSING = "400009";
5656
/**
57-
* 请求时间戳为空
57+
* 请求时间戳不能为空
5858
*/
5959
String REQUEST_TIMESTAMP_MISSING = "400010";
6060
/**
@@ -81,5 +81,9 @@ public interface GatewayReturnCodes {
8181
* 不在白名单中,禁止访问
8282
*/
8383
String NOT_IN_WHITE_LIST = "400016";
84+
/**
85+
* 不支持数据安全
86+
*/
87+
String NOT_SUPPORT_DATA_SECURITY = "400017";
8488

8589
}

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/exception/BlackListException.java

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright © 2019 collin (1634753825@qq.com)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.smartframework.cloud.examples.support.gateway.exception;
217

318
import io.github.smart.cloud.exception.BaseException;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright © 2019 collin (1634753825@qq.com)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.smartframework.cloud.examples.support.gateway.exception;
17+
18+
import io.github.smart.cloud.exception.BaseException;
19+
20+
/**
21+
* 不支持的功能
22+
*
23+
* @author collin
24+
* @date 2024-04-10
25+
*/
26+
public class UnsupportedFunctionException extends BaseException {
27+
28+
public UnsupportedFunctionException(String code) {
29+
super(code);
30+
}
31+
32+
}

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/exception/WhiteListException.java

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright © 2019 collin (1634753825@qq.com)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.smartframework.cloud.examples.support.gateway.exception;
217

318
import io.github.smart.cloud.exception.BaseException;

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/filter/access/BlackWhiteListFilter.java

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright © 2019 collin (1634753825@qq.com)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.smartframework.cloud.examples.support.gateway.filter.access;
217

318
import lombok.RequiredArgsConstructor;

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/filter/access/core/AuthFilter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ public int getOrder() {
8080
@Override
8181
protected Mono<Void> innerFilter(ServerWebExchange exchange, WebFilterChain chain, FilterContext filterContext) {
8282
ApiAccessMetaCache apiAccessMetaCache = filterContext.getApiAccessMetaCache();
83-
// 判断是否需要登陆、鉴权
84-
if (!apiAccessMetaCache.isAuth()) {
83+
// 判断是否需要登陆校验
84+
if (!apiAccessMetaCache.isRequiresUser()) {
8585
return chain.filter(exchange);
8686
}
8787

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/filter/access/core/RepeatSubmitCheckFilter.java

+8
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
*/
1616
package org.smartframework.cloud.examples.support.gateway.filter.access.core;
1717

18+
import io.github.smart.cloud.exception.DataValidateException;
1819
import io.github.smart.cloud.exception.RepeatSubmitException;
1920
import io.github.smart.cloud.utility.security.Md5Util;
2021
import lombok.RequiredArgsConstructor;
22+
import org.apache.commons.lang3.StringUtils;
2123
import org.redisson.api.RLock;
2224
import org.redisson.api.RedissonClient;
2325
import org.smartframework.cloud.examples.support.gateway.cache.ApiAccessMetaCache;
26+
import org.smartframework.cloud.examples.support.gateway.constants.GatewayReturnCodes;
2427
import org.smartframework.cloud.examples.support.gateway.constants.Order;
2528
import org.smartframework.cloud.examples.support.gateway.filter.FilterContext;
2629
import org.smartframework.cloud.examples.support.gateway.filter.access.AbstractFilter;
@@ -52,6 +55,11 @@ protected Mono<Void> innerFilter(ServerWebExchange exchange, WebFilterChain chai
5255
if (!apiAccessMetaCache.isRepeatSubmitCheck()) {
5356
return chain.filter(exchange);
5457
}
58+
59+
if (StringUtils.isBlank(filterContext.getToken())) {
60+
throw new DataValidateException(GatewayReturnCodes.TOKEN_MISSING);
61+
}
62+
5563
ServerHttpRequest request = exchange.getRequest();
5664

5765
StringBuilder key = new StringBuilder();

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/filter/access/core/datasecurity/DataSecurityFilter.java

+6-29
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
import org.smartframework.cloud.examples.support.gateway.cache.ApiAccessMetaCache;
2323
import org.smartframework.cloud.examples.support.gateway.constants.GatewayReturnCodes;
2424
import org.smartframework.cloud.examples.support.gateway.constants.Order;
25+
import org.smartframework.cloud.examples.support.gateway.exception.UnsupportedFunctionException;
2526
import org.smartframework.cloud.examples.support.gateway.filter.FilterContext;
2627
import org.smartframework.cloud.examples.support.gateway.filter.access.AbstractFilter;
27-
import org.springframework.http.HttpMethod;
28-
import org.springframework.http.MediaType;
28+
import org.smartframework.cloud.examples.support.gateway.util.RewriteHttpUtil;
2929
import org.springframework.stereotype.Component;
3030
import org.springframework.web.server.ServerWebExchange;
3131
import org.springframework.web.server.WebFilterChain;
@@ -55,43 +55,20 @@ protected Mono<Void> innerFilter(ServerWebExchange exchange, WebFilterChain chai
5555
return chain.filter(exchange);
5656
}
5757

58+
if (!RewriteHttpUtil.isSupported(exchange.getRequest().getHeaders().getContentType())) {
59+
throw new UnsupportedFunctionException(GatewayReturnCodes.NOT_SUPPORT_DATA_SECURITY);
60+
}
61+
5862
String token = filterContext.getToken();
5963
if (StringUtils.isBlank(token)) {
6064
throw new ParamValidateException(GatewayReturnCodes.TOKEN_MISSING);
6165
}
6266

63-
HttpMethod httpMethod = exchange.getRequest().getMethod();
64-
MediaType contentType = exchange.getRequest().getHeaders().getContentType();
65-
if (!match(contentType, httpMethod)) {
66-
return chain.filter(exchange);
67-
}
68-
6967
return chain.filter(exchange.mutate()
7068
.request(new DataSecurityServerHttpRequestDecorator(exchange.getRequest(), exchange.getResponse().bufferFactory(), token, apiAccessMetaCache.isRequestDecrypt(),
7169
apiAccessMetaCache.getSignType(), redisAdapter))
7270
.response(new DataSecurityServerHttpResponseDecorator(exchange.getResponse(), apiAccessMetaCache.isResponseEncrypt(), apiAccessMetaCache.getSignType()))
7371
.build());
7472
}
7573

76-
/**
77-
* 加解密、签名匹配条件
78-
*
79-
* @param contentType
80-
* @param httpMethod
81-
* @return
82-
*/
83-
private boolean match(MediaType contentType, HttpMethod httpMethod) {
84-
if (contentType == null) {
85-
return false;
86-
}
87-
88-
String contentTypeStr = contentType.toString();
89-
if (!StringUtils.containsAny(contentTypeStr, MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
90-
return false;
91-
}
92-
93-
// GET、POST以外的请求不做加解密、签名处理
94-
return httpMethod == HttpMethod.GET || httpMethod == HttpMethod.POST;
95-
}
96-
9774
}

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/filter/access/core/datasecurity/DataSecurityServerHttpResponseDecorator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends Data
5050
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
5151
// TODO:
5252
final MediaType contentType = super.getHeaders().getContentType();
53-
if (RewriteHttpUtil.isReadable(contentType)) {
53+
if (RewriteHttpUtil.isSupported(contentType)) {
5454
DataBufferFactory dataBufferFactory = super.bufferFactory();
5555
if (body instanceof Mono) {
5656
((Mono<DataBuffer>) body).subscribe(buffer -> {

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/filter/rewrite/RewriteServerHttpRequestDecorator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class RewriteServerHttpRequestDecorator extends ServerHttpRequestDecorato
4040
RewriteServerHttpRequestDecorator(ServerHttpRequest request, DataBufferFactory dataBufferFactory) {
4141
super(request);
4242
Flux<DataBuffer> flux = super.getBody();
43-
if (RewriteHttpUtil.isReadable(super.getHeaders().getContentType())) {
43+
if (RewriteHttpUtil.isSupported(super.getHeaders().getContentType())) {
4444
this.body = super.getBody().map(dataBuffer -> {
4545
byte[] bytes = RewriteHttpUtil.convert(dataBuffer);
4646
this.bodyStr = new String(bytes, StandardCharsets.UTF_8);

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/filter/rewrite/RewriteServerHttpResponseDecorator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends Data
5252
@Override
5353
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
5454
final MediaType contentType = super.getHeaders().getContentType();
55-
if (RewriteHttpUtil.isReadable(contentType)) {
55+
if (RewriteHttpUtil.isSupported(contentType)) {
5656
DataBufferFactory dataBufferFactory = super.bufferFactory();
5757
if (body instanceof Mono) {
5858
((Mono<DataBuffer>) body).subscribe(buffer -> {

application-module/support-module/support-service-gateway/src/main/java/org/smartframework/cloud/examples/support/gateway/util/RewriteHttpUtil.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ private RewriteHttpUtil() {
3131
}
3232

3333
/**
34-
* 是否可读
34+
* 是否支持
3535
*
3636
* @param contentType
3737
* @return
3838
*/
39-
public static boolean isReadable(MediaType contentType) {
39+
public static boolean isSupported(MediaType contentType) {
4040
return contentType != null && contentType.includes(MediaType.APPLICATION_JSON);
4141
}
4242

application-module/support-module/support-service-gateway/src/main/resources/i18n/gateway_messages.properties

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
400013=security key\u8FC7\u671F\uFF01
1414
400014=AES key\u83B7\u53D6\u5931\u8D25\uFF01
1515
400015=\u547D\u4E2D\u9ED1\u540D\u5355\u5217\u8868\uFF0C\u7981\u6B62\u8BBF\u95EE
16-
400016=\u4E0D\u5728\u767D\u540D\u5355\u4E2D\uFF0C\u7981\u6B62\u8BBF\u95EE
16+
400016=\u4E0D\u5728\u767D\u540D\u5355\u4E2D\uFF0C\u7981\u6B62\u8BBF\u95EE
17+
400017=\u53EA\u6709application/json\u624D\u652F\u6301\u63A5\u53E3\u7B7E\u540D\u6821\u9A8C\u3001\u52A0\u89E3\u5BC6

application-module/support-module/support-service-gateway/src/main/resources/i18n/gateway_messages_en_US.properties

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
400013=Security key expired!
1414
400014=AES key is not found!
1515
400015=Matches the blacklist list, and the access is prohibited
16-
400016=The access is not in the whitelist
16+
400016=The access is not in the whitelist
17+
400017=Only application/json supports interface signature verification, encryption and decryption

application-module/support-module/support-service-gateway/src/main/resources/i18n/gateway_messages_zh_CN.properties

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
400013=security key\u8FC7\u671F\uFF01
1414
400014=AES key\u83B7\u53D6\u5931\u8D25\uFF01
1515
400015=\u547D\u4E2D\u9ED1\u540D\u5355\u5217\u8868\uFF0C\u7981\u6B62\u8BBF\u95EE
16-
400016=\u4E0D\u5728\u767D\u540D\u5355\u4E2D\uFF0C\u7981\u6B62\u8BBF\u95EE
16+
400016=\u4E0D\u5728\u767D\u540D\u5355\u4E2D\uFF0C\u7981\u6B62\u8BBF\u95EE
17+
400017=\u53EA\u6709application/json\u624D\u652F\u6301\u63A5\u53E3\u7B7E\u540D\u6821\u9A8C\u3001\u52A0\u89E3\u5BC6

0 commit comments

Comments
 (0)