spring cloud gateway开发openApi统一鉴权
通过spring cloud gateway开发openApi统一鉴权
通过网关统一鉴权openApi,业务层只用关注具体业务,防止重放攻击、验证签名等
import cn . hutool . core . date . DateUnit ; import cn . hutool . core . date . DateUtil ; import com . alibaba . fastjson . JSON ; import com . alibaba . fastjson . JSONObject ; import com . google . gson . Gson ; import com . * * * * . MD5Util ; import com . * * * * . SignUtils ; import com . * * * * . Result ; import lombok . extern . slf4j . Slf4j ; import org . apache . commons . lang3 . StringUtils ; import org . springframework . beans . factory . annotation . Autowired ; import org . springframework . cloud . gateway . filter . GatewayFilter ; import org . springframework . cloud . gateway . filter . GatewayFilterChain ; import org . springframework . cloud . gateway . filter . factory . AbstractGatewayFilterFactory ; import org . springframework . cloud . gateway . support . ipresolver . XForwardedRemoteAddressResolver ; import org . springframework . core . io . buffer . DataBuffer ; import org . springframework . data . redis . core . RedisTemplate ; import org . springframework . http . HttpHeaders ; import org . springframework . http . HttpStatus ; import org . springframework . http . MediaType ; import org . springframework . stereotype . Component ; import org . springframework . web . server . ServerWebExchange ; import reactor . core . publisher . Mono ; import java . net . InetSocketAddress ; import java . nio . charset . StandardCharsets ; import java . util . Arrays ; import java . util . Date ; import java . util . Map ; import java . util . TreeMap ; import java . util . concurrent . TimeUnit ; import static org . springframework . cloud . gateway . support . GatewayToStringStyler . filterToStringCreator ; * @author hk * @See com.kbd.watermelon.scheduleTask.EnterpriseApiKeyTask @Slf4j @Component public class AuthOpenApiGatewayFilterFactory extends AbstractGatewayFilterFactory < Object > { public static final String PREFIX = "ota:enterprise:api:" ; private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject" ; @Autowired private RedisTemplate < String , Object > redisTemplate ; public GatewayFilter apply ( ) { return apply ( o -> { } ) ; @Override public GatewayFilter apply ( Object config ) { return new GatewayFilter ( ) { @Override public Mono < Void > filter ( ServerWebExchange exchange , GatewayFilterChain chain ) { XForwardedRemoteAddressResolver resolver = XForwardedRemoteAddressResolver . maxTrustedIndex ( 1 ) ; InetSocketAddress inetSocketAddress = resolver . resolve ( exchange ) ; String userIp = inetSocketAddress . getAddress ( ) . getHostAddress ( ) ; HttpHeaders httpHeaders = exchange . getRequest ( ) . getHeaders ( ) ; log . info ( new Gson ( ) . toJson ( httpHeaders ) ) ; String body = exchange . getAttribute ( CACHE_REQUEST_BODY_OBJECT_KEY ) ; log . info ( "userIp:{},body:{}" , userIp , body ) ; TreeMap < String , Object > treeMap = JSONObject . parseObject ( body , TreeMap . class ) ; String timestamp = ( String ) treeMap . get ( SignUtils . TIMESTAMP ) ; if ( StringUtils . isBlank ( timestamp ) ) { log . error ( "timestamp不能为空" ) ; return getErrorMono ( exchange , "timestamp不能为空" ) ; if ( DateUtil . between ( new Date ( ) , new Date ( Long . parseLong ( timestamp ) ) , DateUnit . MINUTE ) > 5 ) { log . error ( "超时" ) ; return getErrorMono ( exchange , "超时" ) ; String accesskey = ( String ) treeMap . get ( SignUtils . ACCESS_KEY ) ; String nonce = ( String ) treeMap . get ( SignUtils . NONCE ) ; String sign = ( String ) treeMap . get ( SignUtils . SIGN ) ; if ( StringUtils . isBlank ( accesskey ) ) { log . error ( "accesskey超时" ) ; return getErrorMono ( exchange , "accesskey超时" ) ; if ( StringUtils . isBlank ( nonce ) ) { log . error ( "nonce超时" ) ; return getErrorMono ( exchange , "nonce超时" ) ; if ( StringUtils . isBlank ( sign ) ) { log . error ( "sign超时" ) ; return getErrorMono ( exchange , "sign超时" ) ; if ( ! redisTemplate . opsForValue ( ) . setIfAbsent ( sign , nonce , 6 , TimeUnit . MINUTES ) ) { log . error ( "超时" ) ; return getErrorMono ( exchange , "超时" ) ; Map map = ( Map ) redisTemplate . opsForValue ( ) . get ( PREFIX + accesskey ) ; if ( map == null || map . get ( SignUtils . SECRET_KEY ) == null || SignUtils . IP == null ) { log . error ( "查询不到密钥{}" , accesskey ) ; return getErrorMono ( exchange , "查询不到密钥" ) ; if ( ! Arrays . asList ( ( map . get ( SignUtils . IP ) + "" ) . split ( "," ) ) . contains ( userIp ) ) { log . error ( "ip不在白名单" ) ; return getErrorMono ( exchange , "ip不在白名单" ) ; if ( ! SignUtils . validSign ( treeMap , map . get ( SignUtils . SECRET_KEY ) + "" ) ) { log . error ( "验证签名失败" ) ; return getErrorMono ( exchange , "验证签名失败" ) ; return chain . filter ( exchange ) ; @Override public String toString ( ) { return filterToStringCreator ( AuthOpenApiGatewayFilterFactory . this ) . toString ( ) ; private Mono < Void > getErrorMono ( ServerWebExchange exchange , String msg ) { exchange . getResponse ( ) . setStatusCode ( HttpStatus . UNAUTHORIZED ) ; exchange . getResponse ( ) . getHeaders ( ) . setContentType ( MediaType . APPLICATION_JSON ) ; String fastResult = JSON . toJSONString ( Result . fail ( msg ) ) ; DataBuffer dataBuffer = exchange . getResponse ( ) . bufferFactory ( ) . allocateBuffer ( ) . write ( fastResult . getBytes ( StandardCharsets . UTF_8 ) ) ; return exchange . getResponse ( ) . writeWith ( Mono . just ( dataBuffer ) ) ; public static void main ( String [ ] args ) { TreeMap < String , String > treeMap = new TreeMap < > ( ) ; treeMap . put ( "timestamp" , System . currentTimeMillis ( ) + "" ) ; treeMap . put ( "nonce" , "1234567" ) ; treeMap . put ( "accessKey" , "1234" ) ; treeMap . put ( "a" , "a" ) ; treeMap . put ( "b" , "b" ) ; StringBuilder stringBuilder = new StringBuilder ( ) ; for ( Map . Entry < String , String > stringStringEntry : treeMap . entrySet ( ) ) { if ( ! stringStringEntry . getKey ( ) . equals ( "sign" ) ) { stringBuilder . append ( stringStringEntry . getKey ( ) ) ; stringBuilder . append ( "=" ) ; stringBuilder . append ( stringStringEntry . getValue ( ) ) ; stringBuilder . append ( "&" ) ; stringBuilder . append ( "secretKey=" ) ; stringBuilder . append ( "123" ) ; System . out . println ( stringBuilder . toString ( ) ) ; String md5 = MD5Util . md5 ( stringBuilder . toString ( ) ) ; treeMap . put ( "sign" , md5 ) ; System . out . println ( JSONObject . toJSON ( treeMap ) ) ;SignUtils
public class SignUtils{
public static final String SIGN = "sign";
public static final String NONCE = "nonce";
public static final String ACCESS_KEY = "accessKey";
public static final String TIMESTAMP = "timestamp";
public static final String SECRET_KEY = "secretKey";
public static final String IP = "ip";
public static boolean validSign(TreeMap<String,Object> treeMap,String secret){
StringBuilder stringBuilder = new StringBuilder();
for(Map.Entry<String,Object
> s :treeMap.entrySet()){
if(!s.getKey().equals(SIGN)){
stringBuilder.append(s.getKey());
stringBuilder.append("=");
stringBuilder.append(s.getValue());
stringBuilder.append("&");
stringBuilder.append(SECRET_KEY );
stringBuilder.append("=");
stringBuilder.append(secret);
String md5 =MD5Util.md5(stringBuilder.toString());
return md5.equalsIgnoreCase(treeMap.get("sign")+"");
import java.util.function.Predicate;
@Configuration
public class PredicateConfig{
@Bean
public Predicate bodPredicate(){
return new Predicate(){
@Override
public boolean test(Object o ){return true;}
最后是yml 网关的配置
id: id
url: http://***
predicates:
- Path=/openApi/**
- name: ReadBody
args:
inClass: '#{T(String)}'
predicate: '#{@bodyPredicate}'
filters:
- AuthOpenApi
# gateway
# java
# redis
所有评论(0)