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
更多推荐
已为社区贡献1条内容
所有评论(0)