@@ -1,12 +1,13 @@
 
			
		
	
		
		
			
				
					
					package   cn.iocoder.yudao.framework.signature.core.aop ; package   cn.iocoder.yudao.framework.signature.core.aop ;  
			
		
	
		
		
			
				
					
					 
			
		
	
		
		
			
				
					
					import   cn.hutool.core.collection.CollUtil ; import   cn.hutool.core.collection.CollUtil ;  
			
		
	
		
		
			
				
					
					import   cn.hutool.core.lang.Assert ;  
			
		
	
		
		
			
				
					
					import   cn.hutool.core.util.StrUtil ; import   cn.hutool.core.util.StrUtil ;  
			
		
	
		
		
			
				
					
					import   cn.hutool.crypto.SignUtil ; import   cn.hutool.crypto.SignUtil ;  
			
		
	
		
		
			
				
					
					import   cn.iocoder.yudao.framework.common.exception.ServiceException ; import   cn.iocoder.yudao.framework.common.exception.ServiceException ;  
			
		
	
		
		
			
				
					
					import   cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants ; import   cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants ;  
			
		
	
		
		
			
				
					
					import   cn.iocoder.yudao.framework.common.util.servlet.ServletUtils ; import   cn.iocoder.yudao.framework.common.util.servlet.ServletUtils ;  
			
		
	
		
		
			
				
					
					import   cn.iocoder.yudao.framework.signature.core.annotation.Signature ; import   cn.iocoder.yudao.framework.signature.core.annotation.Api Signature ;  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					import   cn.iocoder.yudao.framework.signature.core.redis.SignatureRedisDAO ; import   cn.iocoder.yudao.framework.signature.core.redis.SignatureRedisDAO ;  
			
		
	
		
		
			
				
					
					import   cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyWrapper ; import   cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyWrapper ;  
			
		
	
		
		
			
				
					
					import   jakarta.servlet.http.HttpServletRequest ; import   jakarta.servlet.http.HttpServletRequest ;  
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -15,7 +16,6 @@ import lombok.extern.slf4j.Slf4j;
 
			
		
	
		
		
			
				
					
					import   org.aspectj.lang.JoinPoint ; import   org.aspectj.lang.JoinPoint ;  
			
		
	
		
		
			
				
					
					import   org.aspectj.lang.annotation.Aspect ; import   org.aspectj.lang.annotation.Aspect ;  
			
		
	
		
		
			
				
					
					import   org.aspectj.lang.annotation.Before ; import   org.aspectj.lang.annotation.Before ;  
			
		
	
		
		
			
				
					
					import   org.springframework.util.Assert ;  
			
		
	
		
		
			
				
					
					 
			
		
	
		
		
			
				
					
					import   java.nio.charset.StandardCharsets ; import   java.nio.charset.StandardCharsets ;  
			
		
	
		
		
			
				
					
					import   java.util.Map ; import   java.util.Map ;  
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -25,7 +25,7 @@ import java.util.TreeMap;
 
			
		
	
		
		
			
				
					
					import   java.util.concurrent.TimeUnit ; import   java.util.concurrent.TimeUnit ;  
			
		
	
		
		
			
				
					
					 
			
		
	
		
		
			
				
					
					/** /**  
			
		
	
		
		
			
				
					
					 * 拦截声明了 {@link Signature} 注解的方法,实现签名  * 拦截声明了 {@link Api Signature} 注解的方法,实现签名  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 *  *  
			
		
	
		
		
			
				
					
					 * @author Zhougang  * @author Zhougang  
			
		
	
		
		
			
				
					
					 */  */  
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -37,9 +37,9 @@ public class SignatureAspect {
 
			
		
	
		
		
			
				
					
					     private   final   SignatureRedisDAO   signatureRedisDAO ;      private   final   SignatureRedisDAO   signatureRedisDAO ;  
			
		
	
		
		
			
				
					
					 
			
		
	
		
		
			
				
					
					     @Before ( " @annotation(signature) " )      @Before ( " @annotation(signature) " )  
			
		
	
		
		
			
				
					
					     public   void   beforePointCut ( JoinPoint   joinPoint ,   Signature   signature )   {      public   void   beforePointCut ( JoinPoint   joinPoint ,   Api Signature  signature )   {  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					         if   ( ! verifySignature ( signature ,   Objects . requireNonNull ( ServletUtils . getRequest ( ) ) ) )   {          if   ( ! verifySignature ( signature ,   Objects . requireNonNull ( ServletUtils . getRequest ( ) ) ) )   {  
			
		
	
		
		
			
				
					
					             log . info ( " [beforePointCut][方法{} 参数({}) 签名失败] " ,   joinPoint . getSignature ( ) . toString ( ) ,              log . error ( " [beforePointCut][方法{} 参数({}) 签名失败] " ,   joinPoint . getSignature ( ) . toString ( ) ,  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					                     joinPoint . getArgs ( ) ) ;                      joinPoint . getArgs ( ) ) ;  
			
		
	
		
		
			
				
					
					             String   message   =   StrUtil . blankToDefault ( signature . message ( ) ,              String   message   =   StrUtil . blankToDefault ( signature . message ( ) ,  
			
		
	
		
		
			
				
					
					                     GlobalErrorCodeConstants . BAD_REQUEST . getMsg ( ) ) ;                      GlobalErrorCodeConstants . BAD_REQUEST . getMsg ( ) ) ;  
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -47,25 +47,22 @@ public class SignatureAspect {
 
			
		
	
		
		
			
				
					
					         }          }  
			
		
	
		
		
			
				
					
					     }      }  
			
		
	
		
		
			
				
					
					 
			
		
	
		
		
			
				
					
					     private   boolean   verifySignature ( Signature   signature ,   HttpServletRequest   request )   {      private   boolean   verifySignature ( Api Signature  signature ,   HttpServletRequest   request )   {  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					         if   ( ! verifyHeaders ( signature ,   request ) )   {          if   ( ! verifyHeaders ( signature ,   request ) )   {  
			
		
	
		
		
			
				
					
					             return   false ;              return   false ;  
			
		
	
		
		
			
				
					
					         }          }  
			
		
	
		
		
			
				
					
					         // 校验 appId 是否能获取到对应的 appSecret          // 校验 appId 是否能获取到对应的 appSecret  
			
		
	
		
		
			
				
					
					         String   appId   =   request . getHeader ( signature . appId ( ) ) ;          String   appId   =   request . getHeader ( signature . appId ( ) ) ;  
			
		
	
		
		
			
				
					
					         String   appSecret   =   signatureRedisDAO . getAppSecret ( appId ) ;          String   appSecret   =   signatureRedisDAO . getAppSecret ( appId ) ;  
			
		
	
		
		
			
				
					
					         Assert . notNull ( appSecret ,   " 找不到对应的 appSecret " ) ;          Assert . notNull ( appSecret ,   " [appId({})]  找不到对应的 appSecret" ,   appId ;  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					         // 请求头          // 请求头  
			
		
	
		
		
			
				
					
					         SortedMap < String ,   String >   headersMap   =   getRequestHeaders ( signature ,   request ) ;          SortedMap < String ,   String >   headersMap   =   getRequestHeaders ( signature ,   request ) ;  
			
		
	
		
		
			
				
					
					         // 如:/user/{id} url 带有动态参数的情况  
			
		
	
		
		
			
				
					
					         String   urlParams   =   signature . urlEnable ( )   ?   request . getServletPath ( )   :   " " ;  
			
		
	
		
		
			
				
					
					         // 请求参数          // 请求参数  
			
		
	
		
		
			
				
					
					         String   requestParams   =   getRequestParams ( request ) ;          String   requestParams   =   getRequestParams ( request ) ;  
			
		
	
		
		
			
				
					
					         // 请求体          // 请求体  
			
		
	
		
		
			
				
					
					         String   requestBody   =   getReques tBody( request ) ;          String   requestBody   =   ServletUtils . isJsonRequest ( request )   ?   ServletUtils . ge tBody( request )   :   " " ;  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					         // 生成服务端签名          // 生成服务端签名  
			
		
	
		
		
			
				
					
					         String   serverSignature   =   SignUtil . signParamsSha256 ( headersMap ,          String   serverSignature   =   SignUtil . signParamsSha256 ( headersMap ,   requestParams   +   requestBody   +   appSecret ) ;  
			
				
				
			
		
	
		
		
			
				
					
					                 urlParams   +   requestParams   +   requestBody   +   appSecret ) ;  
			
		
	
		
		
	
		
		
			
				
					
					         // 客户端签名          // 客户端签名  
			
		
	
		
		
			
				
					
					         String   clientSignature   =   request . getHeader ( signature . sign ( ) ) ;          String   clientSignature   =   request . getHeader ( signature . sign ( ) ) ;  
			
		
	
		
		
			
				
					
					         if   ( ! StrUtil . equals ( clientSignature ,   serverSignature ) )   {          if   ( ! StrUtil . equals ( clientSignature ,   serverSignature ) )   {  
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -73,7 +70,7 @@ public class SignatureAspect {
 
			
		
	
		
		
			
				
					
					         }          }  
			
		
	
		
		
			
				
					
					         String   nonce   =   headersMap . get ( signature . nonce ( ) ) ;          String   nonce   =   headersMap . get ( signature . nonce ( ) ) ;  
			
		
	
		
		
			
				
					
					         // 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 )           // 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 )   
			
		
	
		
		
			
				
					
					         signatureRedisDAO . setNonce ( nonce ,   signature . expireTime ( ) ,   TimeUnit . MILLISECONDS ) ;          signatureRedisDAO . setNonce ( nonce ,   signature . timeout ( )   *   2L ,   signature . timeUnit ( ) ) ;  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					         return   true ;          return   true ;  
			
		
	
		
		
			
				
					
					     }      }  
			
		
	
		
		
			
				
					
					 
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -87,7 +84,7 @@ public class SignatureAspect {
 
			
		
	
		
		
			
				
					
					     * @param signature signature      * @param signature signature  
			
		
	
		
		
			
				
					
					     * @param request   request      * @param request   request  
			
		
	
		
		
			
				
					
					     */      */  
			
		
	
		
		
			
				
					
					     private   boolean   verifyHeaders ( Signature   signature ,   HttpServletRequest   request )   {      private   boolean   verifyHeaders ( Api Signature  signature ,   HttpServletRequest   request )   {  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					         String   appId   =   request . getHeader ( signature . appId ( ) ) ;          String   appId   =   request . getHeader ( signature . appId ( ) ) ;  
			
		
	
		
		
			
				
					
					         if   ( StrUtil . isBlank ( appId ) )   {          if   ( StrUtil . isBlank ( appId ) )   {  
			
		
	
		
		
			
				
					
					             return   false ;              return   false ;  
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -97,7 +94,7 @@ public class SignatureAspect {
 
			
		
	
		
		
			
				
					
					             return   false ;              return   false ;  
			
		
	
		
		
			
				
					
					         }          }  
			
		
	
		
		
			
				
					
					         String   nonce   =   request . getHeader ( signature . nonce ( ) ) ;          String   nonce   =   request . getHeader ( signature . nonce ( ) ) ;  
			
		
	
		
		
			
				
					
					         if   ( StrUtil . isBlank ( nonce )   | |   nonce . length ( )   <   10 )   {          if   ( StrUtil . isBlank ( nonce )   | |   StrUtil . length ( nonce )   <   10 )   {  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					             return   false ;              return   false ;  
			
		
	
		
		
			
				
					
					         }          }  
			
		
	
		
		
			
				
					
					         String   sign   =   request . getHeader ( signature . sign ( ) ) ;          String   sign   =   request . getHeader ( signature . sign ( ) ) ;  
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -105,7 +102,7 @@ public class SignatureAspect {
 
			
		
	
		
		
			
				
					
					             return   false ;              return   false ;  
			
		
	
		
		
			
				
					
					         }          }  
			
		
	
		
		
			
				
					
					         // 其他合法性校验          // 其他合法性校验  
			
		
	
		
		
			
				
					
					         long   expireTime   =   signature . expireTime ( ) ;          long   expireTime   =   signature . timeUnit ( ) . toMillis ( signature . timeout ( ) ) ;  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					         long   requestTimestamp   =   Long . parseLong ( timestamp ) ;          long   requestTimestamp   =   Long . parseLong ( timestamp ) ;  
			
		
	
		
		
			
				
					
					         // 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值)          // 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值)  
			
		
	
		
		
			
				
					
					         long   timestampDisparity   =   Math . abs ( System . currentTimeMillis ( )   -   requestTimestamp ) ;          long   timestampDisparity   =   Math . abs ( System . currentTimeMillis ( )   -   requestTimestamp ) ;  
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -122,7 +119,7 @@ public class SignatureAspect {
 
			
		
	
		
		
			
				
					
					     * @param request request      * @param request request  
			
		
	
		
		
			
				
					
					     * @return signature params      * @return signature params  
			
		
	
		
		
			
				
					
					     */      */  
			
		
	
		
		
			
				
					
					     private   SortedMap < String ,   String >   getRequestHeaders ( Signature   signature ,   HttpServletRequest   request )   {      private   SortedMap < String ,   String >   getRequestHeaders ( Api Signature  signature ,   HttpServletRequest   request )   {  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					         SortedMap < String ,   String >   sortedMap   =   new   TreeMap < > ( ) ;          SortedMap < String ,   String >   sortedMap   =   new   TreeMap < > ( ) ;  
			
		
	
		
		
			
				
					
					         sortedMap . put ( signature . appId ( ) ,   request . getHeader ( signature . appId ( ) ) ) ;          sortedMap . put ( signature . appId ( ) ,   request . getHeader ( signature . appId ( ) ) ) ;  
			
		
	
		
		
			
				
					
					         sortedMap . put ( signature . timestamp ( ) ,   request . getHeader ( signature . timestamp ( ) ) ) ;          sortedMap . put ( signature . timestamp ( ) ,   request . getHeader ( signature . timestamp ( ) ) ) ;  
			
		
	
	
		
		
			
				
					
					
						
					 
					@@ -154,17 +151,5 @@ public class SignatureAspect {
 
			
		
	
		
		
			
				
					
					         return   queryString . substring ( 1 ) ;          return   queryString . substring ( 1 ) ;  
			
		
	
		
		
			
				
					
					     }      }  
			
		
	
		
		
			
				
					
					 
			
		
	
		
		
			
				
					
					     /**  
			
		
	
		
		
			
				
					
					     * 获取请求体参数  
			
		
	
		
		
			
				
					
					     *  
			
		
	
		
		
			
				
					
					     * @param request request  
			
		
	
		
		
			
				
					
					     * @return body  
			
		
	
		
		
			
				
					
					     */  
			
		
	
		
		
			
				
					
					     private   String   getRequestBody ( HttpServletRequest   request )   {  
			
		
	
		
		
			
				
					
					         CacheRequestBodyWrapper   requestWrapper   =   new   CacheRequestBodyWrapper ( request ) ;  
			
		
	
		
		
			
				
					
					         // 获取 body  
			
		
	
		
		
			
				
					
					         return   new   String ( requestWrapper . getBody ( ) ,   StandardCharsets . UTF_8 ) ;  
			
		
	
		
		
			
				
					
					     }  
			
		
	
		
		
			
				
					
					 
			
		
	
		
		
			
				
					
					} }