17
17
18
18
import io .github .smart .cloud .api .core .annotation .enums .SignType ;
19
19
import io .github .smart .cloud .common .web .constants .SmartHttpHeaders ;
20
- import io .github .smart .cloud .constants .SymbolConstant ;
21
20
import io .github .smart .cloud .exception .DataValidateException ;
22
21
import io .github .smart .cloud .exception .ParamValidateException ;
23
22
import io .github .smart .cloud .starter .redis .adapter .IRedisAdapter ;
26
25
import lombok .extern .slf4j .Slf4j ;
27
26
import org .apache .commons .lang3 .StringUtils ;
28
27
import org .smartframework .cloud .examples .support .gateway .cache .SecurityKeyCache ;
29
- import org .smartframework .cloud .examples .support .gateway .constants .GatewayConstants ;
30
28
import org .smartframework .cloud .examples .support .gateway .constants .GatewayReturnCodes ;
29
+ import org .smartframework .cloud .examples .support .gateway .dto .DataSecurityParamDTO ;
31
30
import org .smartframework .cloud .examples .support .gateway .exception .AesKeyNotFoundException ;
32
31
import org .smartframework .cloud .examples .support .gateway .exception .RequestSignFailException ;
33
32
import org .smartframework .cloud .examples .support .gateway .exception .UnsupportedFunctionException ;
34
- import org .smartframework .cloud .examples .support .gateway .filter .rewrite .RewriteServerHttpRequestDecorator ;
35
33
import org .smartframework .cloud .examples .support .gateway .util .RedisKeyHelper ;
36
34
import org .smartframework .cloud .examples .support .gateway .util .RewriteHttpUtil ;
35
+ import org .smartframework .cloud .examples .support .gateway .util .SignUtil ;
37
36
import org .smartframework .cloud .examples .support .gateway .util .WebUtil ;
38
37
import org .springframework .core .io .buffer .DataBuffer ;
39
38
import org .springframework .core .io .buffer .DataBufferFactory ;
40
- import org .springframework .http .HttpMethod ;
41
- import org .springframework .http .MediaType ;
42
39
import org .springframework .http .server .reactive .ServerHttpRequest ;
43
40
import org .springframework .http .server .reactive .ServerHttpRequestDecorator ;
44
41
import org .springframework .lang .NonNull ;
42
+ import org .springframework .util .Base64Utils ;
45
43
import org .springframework .util .MultiValueMap ;
44
+ import org .springframework .web .util .UriComponents ;
45
+ import org .springframework .web .util .UriComponentsBuilder ;
46
46
import reactor .core .publisher .Flux ;
47
47
48
- import java .io .UnsupportedEncodingException ;
49
- import java .net .URLDecoder ;
48
+ import java .net .URI ;
50
49
import java .nio .charset .StandardCharsets ;
51
50
import java .security .interfaces .RSAPublicKey ;
52
- import java .util .ArrayList ;
53
- import java .util .Arrays ;
54
- import java .util .List ;
55
51
56
52
/**
57
53
* 请求参数签名校验、解密
62
58
@ Slf4j
63
59
public class DataSecurityServerHttpRequestDecorator extends ServerHttpRequestDecorator {
64
60
65
- private transient Flux <DataBuffer > body ;
66
61
private final IRedisAdapter redisAdapter ;
62
+
63
+ private transient Flux <DataBuffer > body ;
64
+ private transient URI uri ;
65
+ private transient MultiValueMap <String , String > queryParams ;
66
+
67
67
private transient SecurityKeyCache securityKeyCache ;
68
68
69
69
DataSecurityServerHttpRequestDecorator (ServerHttpRequest request , DataBufferFactory dataBufferFactory , String token , boolean requestDecrypt , byte signType , IRedisAdapter redisAdapter ) {
70
70
super (request );
71
71
72
- if ((requestDecrypt || signType == SignType .REQUEST .getType () || signType == SignType .ALL .getType ())
73
- && !RewriteHttpUtil .isSupported (super .getHeaders ().getContentType ())) {
72
+ this .redisAdapter = redisAdapter ;
73
+
74
+ if (!requestDecrypt && signType == SignType .NONE .getType ()) {
75
+ this .body = super .getBody ();
76
+ this .uri = super .getURI ();
77
+ this .queryParams = super .getQueryParams ();
78
+ return ;
79
+ }
80
+
81
+ if ((requestDecrypt || signType == SignType .REQUEST .getType () || signType == SignType .ALL .getType ()) && !RewriteHttpUtil .isSupported (super .getHeaders ().getContentType ())) {
74
82
throw new UnsupportedFunctionException (GatewayReturnCodes .NOT_SUPPORT_DATA_SECURITY );
75
83
}
76
84
77
- Flux <DataBuffer > flux = super .getBody ();
78
- this .redisAdapter = redisAdapter ;
85
+ DataSecurityParamDTO dataSecurityParam = SignUtil .getDataSecurityParams (request );
79
86
80
- final String requestStr = getEncryptedRequestStr (request );
87
+ // 1、请求参数验签
88
+ checkRequestSign (request , signType , token , dataSecurityParam );
81
89
82
- // 请求信息验签
83
- checkRequestSign (request , signType , requestStr , token );
90
+ // 2、param base64 decode
91
+ String base64DecodeUrlParams = StringUtils .isBlank (dataSecurityParam .getUrlParamsBase64 ()) ? null : new String (Base64Utils .decodeFromString (dataSecurityParam .getUrlParamsBase64 ()));
92
+ String base64DecodeBody = StringUtils .isBlank (dataSecurityParam .getBodyBase64 ()) ? null : new String (Base64Utils .decodeFromString (dataSecurityParam .getBodyBase64 ()));
84
93
94
+ if (base64DecodeUrlParams == null && base64DecodeBody == null ) {
95
+ setRealUriData (base64DecodeUrlParams , requestDecrypt , null );
96
+ setRealBody (dataBufferFactory , token , base64DecodeBody , requestDecrypt , null );
97
+
98
+ return ;
99
+ }
100
+
101
+ // 3、解密
85
102
if (requestDecrypt ) {
86
- flux .subscribe (buffer -> {
103
+ super . getBody () .subscribe (buffer -> {
87
104
SecurityKeyCache securityKeyCache = getSecurityKeyCache (token );
88
105
String aesKey = securityKeyCache .getAesKey ();
89
106
if (StringUtils .isBlank (aesKey )) {
90
107
throw new AesKeyNotFoundException ();
91
108
}
92
- String decryptedRequestStr = AesUtil .decrypt (requestStr , aesKey );
93
-
94
- HttpMethod httpMethod = request .getMethod ();
95
- if (httpMethod == HttpMethod .GET ) {
96
- decryptUrlParams (decryptedRequestStr , request .getQueryParams ());
97
- } else if (httpMethod == HttpMethod .POST ) {
98
- MediaType contentType = request .getHeaders ().getContentType ();
99
- if (MediaType .APPLICATION_FORM_URLENCODED_VALUE .equals (contentType .toString ())) {
100
- decryptUrlParams (decryptedRequestStr , request .getQueryParams ());
101
- } else if (MediaType .APPLICATION_JSON_VALUE .equals (contentType .toString ())) {
102
- this .body = Flux .just (dataBufferFactory .wrap (decryptedRequestStr .getBytes (StandardCharsets .UTF_8 )));
103
- }
104
- }
109
+
110
+ setRealUriData (base64DecodeUrlParams , true , aesKey );
111
+ setRealBody (dataBufferFactory , token , base64DecodeBody , true , aesKey );
105
112
});
106
113
} else {
107
- this .body = flux ;
114
+ // 重写base64解密后的参数
115
+ setRealUriData (base64DecodeUrlParams , requestDecrypt , null );
116
+ setRealBody (dataBufferFactory , token , base64DecodeBody , requestDecrypt , null );
108
117
}
109
118
}
110
119
111
120
@ Override
112
121
public Flux <DataBuffer > getBody () {
113
- return body ;
122
+ return this . body ;
114
123
}
115
124
116
- private void decryptUrlParams (String decryptedRequestStr , MultiValueMap <String , String > queryParams ) {
117
- queryParams .remove (GatewayConstants .REQUEST_ENCRYPT_PARAM_NAME );
118
- Arrays .stream (decryptedRequestStr .split (SymbolConstant .AND )).forEach (param -> {
119
- if (param .contains (SymbolConstant .EQUAL )) {
120
- String [] entry = param .split (SymbolConstant .EQUAL );
121
- if (entry .length > 0 ) {
122
- String value = entry [1 ];
123
- if (StringUtils .isNotBlank (value )) {
124
- try {
125
- value = URLDecoder .decode (value , StandardCharsets .UTF_8 .name ());
126
- } catch (UnsupportedEncodingException e ) {
127
- log .error ("decode.error|value={}" , value , e );
128
- }
129
- }
130
- List <String > values = new ArrayList <>(1 );
131
- values .add (value );
132
- queryParams .put (entry [0 ], values );
133
- }
134
- }
135
- });
125
+ @ Override
126
+ public URI getURI () {
127
+ return this .uri ;
128
+ }
129
+
130
+ @ Override
131
+ public MultiValueMap <String , String > getQueryParams () {
132
+ return this .queryParams ;
136
133
}
137
134
138
135
/**
139
- * 获取加密后的请求参数
136
+ * 重写body参数
140
137
*
141
- * @param request
142
- * @return
138
+ * @param dataBufferFactory
139
+ * @param token
140
+ * @param base64DecodeBody
141
+ * @param requestDecrypt
142
+ * @param aesKey
143
143
*/
144
- private String getEncryptedRequestStr (ServerHttpRequest request ) {
145
- String requestStr = null ;
146
- HttpMethod httpMethod = request .getMethod ();
147
- if (httpMethod == HttpMethod .GET ) {
148
- requestStr = request .getQueryParams ().getFirst (GatewayConstants .REQUEST_ENCRYPT_PARAM_NAME );
149
- } else if (httpMethod == HttpMethod .POST ) {
150
- MediaType contentType = request .getHeaders ().getContentType ();
151
- if (MediaType .APPLICATION_FORM_URLENCODED_VALUE .equals (contentType .toString ())) {
152
- requestStr = request .getQueryParams ().getFirst (GatewayConstants .REQUEST_ENCRYPT_PARAM_NAME );
153
- } else if (MediaType .APPLICATION_JSON_VALUE .equals (contentType .toString ())) {
154
- RewriteServerHttpRequestDecorator rewriteServerHttpRequestDecorator = (RewriteServerHttpRequestDecorator ) request ;
155
- requestStr = rewriteServerHttpRequestDecorator .getBodyStr ();
156
- }
144
+ private void setRealBody (DataBufferFactory dataBufferFactory , String token , String base64DecodeBody , boolean requestDecrypt , String aesKey ) {
145
+ if (base64DecodeBody == null ) {
146
+ this .body = super .getBody ();
157
147
}
158
- return requestStr ;
148
+
149
+ String realBody = requestDecrypt ? AesUtil .decrypt (base64DecodeBody , aesKey ) : base64DecodeBody ;
150
+ this .body = Flux .just (dataBufferFactory .wrap (realBody .getBytes (StandardCharsets .UTF_8 )));
151
+ }
152
+
153
+ /**
154
+ * 重写url参数
155
+ *
156
+ * @param base64DecodeUrlParams
157
+ * @param requestDecrypt
158
+ * @param aesKey
159
+ */
160
+ private void setRealUriData (String base64DecodeUrlParams , boolean requestDecrypt , String aesKey ) {
161
+ if (base64DecodeUrlParams == null ) {
162
+ this .uri = super .getURI ();
163
+ this .queryParams = super .getQueryParams ();
164
+ return ;
165
+ }
166
+
167
+ String realUrlParamsStr = requestDecrypt ? AesUtil .decrypt (base64DecodeUrlParams , aesKey ) : base64DecodeUrlParams ;
168
+ UriComponents uriComponents = UriComponentsBuilder .fromUri (super .getURI ()).replaceQuery (realUrlParamsStr ).build ();
169
+
170
+ this .uri = uriComponents .toUri ();
171
+ this .queryParams = uriComponents .getQueryParams ();
159
172
}
160
173
161
174
/**
@@ -164,24 +177,25 @@ private String getEncryptedRequestStr(ServerHttpRequest request) {
164
177
* @param request
165
178
* @param signType
166
179
* @param token
180
+ * @param dataSecurityParam
167
181
*/
168
- private void checkRequestSign (ServerHttpRequest request , byte signType , String requestStr , @ NonNull String token ) {
169
- if (StringUtils .isBlank (requestStr )) {
170
- return ;
171
- }
182
+ private void checkRequestSign (ServerHttpRequest request , byte signType , @ NonNull String token , DataSecurityParamDTO dataSecurityParam ) {
172
183
if (SignType .REQUEST .getType () != signType && SignType .ALL .getType () != signType ) {
173
184
return ;
174
185
}
186
+
175
187
String sign = WebUtil .getFromRequestHeader (request , SmartHttpHeaders .SIGN );
176
188
if (StringUtils .isBlank (sign )) {
177
189
throw new ParamValidateException (GatewayReturnCodes .REQUEST_SIGN_MISSING );
178
190
}
179
191
192
+ String requestSignContent = SignUtil .generateRequestSignContent (request .getMethod (), dataSecurityParam );
193
+
180
194
SecurityKeyCache securityKeyCache = getSecurityKeyCache (token );
181
195
boolean signCheckResult = false ;
182
196
try {
183
197
RSAPublicKey publicKey = RsaUtil .getRsaPublidKey (securityKeyCache .getCpubKeyModulus (), securityKeyCache .getCpubKeyExponent ());
184
- signCheckResult = RsaUtil .checkSign (requestStr , sign , publicKey );
198
+ signCheckResult = RsaUtil .checkSign (requestSignContent , sign , publicKey );
185
199
} catch (Exception e ) {
186
200
log .error ("sign.check.error" , e );
187
201
}
@@ -191,15 +205,17 @@ private void checkRequestSign(ServerHttpRequest request, byte signType, String r
191
205
}
192
206
193
207
private SecurityKeyCache getSecurityKeyCache (@ NonNull String token ) {
194
- if (securityKeyCache != null ) {
195
- return securityKeyCache ;
208
+ if (this . securityKeyCache != null ) {
209
+ return this . securityKeyCache ;
196
210
}
197
211
198
212
SecurityKeyCache securityKeyCache = (SecurityKeyCache ) redisAdapter .get (RedisKeyHelper .getSecurityKey (token ));
199
213
if (securityKeyCache == null ) {
200
214
throw new DataValidateException (GatewayReturnCodes .SECURITY_KEY_EXPIRED );
201
215
}
202
- return securityKeyCache ;
216
+
217
+ this .securityKeyCache = securityKeyCache ;
218
+ return this .securityKeyCache ;
203
219
}
204
220
205
221
}
0 commit comments