package com.newfiber.gateway.filter; import com.alibaba.fastjson2.JSONArray; import com.newfiber.common.core.constant.HttpStatus; import com.newfiber.common.core.constant.SecurityConstants; import com.newfiber.common.core.constant.TokenConstants; import com.newfiber.common.core.domain.ClientInfo; import com.newfiber.common.core.domain.ClientResource; import com.newfiber.common.core.enums.EAuthenticationSource; import com.newfiber.common.core.enums.EBoolean; import com.newfiber.common.core.enums.EClientResourceType; import com.newfiber.common.core.utils.JwtUtils; import com.newfiber.common.core.utils.ServletUtils; import com.newfiber.common.core.utils.StringUtils; import com.newfiber.common.core.utils.ip.IpUtils; import com.newfiber.common.redis.service.RedisService; import com.newfiber.gateway.config.properties.IgnoreWhiteProperties; import com.newfiber.gateway.enums.EGatewayRedisKey; import io.jsonwebtoken.Claims; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * 网关鉴权 * * @author newfiber */ @Component public class AuthFilter implements GlobalFilter, Ordered{ private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); // 排除过滤的 uri 地址,nacos自行添加 @Autowired private IgnoreWhiteProperties ignoreWhite; @Autowired private RedisService redisService; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){ ServerHttpRequest request = exchange.getRequest(); ServerHttpRequest.Builder mutate = request.mutate(); String url = request.getURI().getPath(); // 跳过不需要验证的路径 if (StringUtils.matches(url, ignoreWhite.getWhites())){ return chain.filter(exchange); } String token = getToken(request); if (StringUtils.isEmpty(token)){ return unauthorizedResponse(exchange, "令牌不能为空"); } Claims claims = JwtUtils.parseToken(token); if (claims == null){ return unauthorizedResponse(exchange, "令牌已过期或验证不正确!"); } String userkey = JwtUtils.getUserKey(claims); EAuthenticationSource authenticationSource = loginCheck(userkey); if (null == authenticationSource){ return unauthorizedResponse(exchange, "登录状态已过期"); } String userid = JwtUtils.getUserId(claims); String username = JwtUtils.getUserName(claims); if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)){ return unauthorizedResponse(exchange, "令牌验证失败"); } if(EAuthenticationSource.Client.equals(authenticationSource)){ Mono<Void> x = clientCheck(exchange, url, userid); if (x != null){ return x; } } // 设置用户信息到请求 addHeader(mutate, SecurityConstants.AUTHORIZATION_SOURCE, authenticationSource.getCode()); addHeader(mutate, SecurityConstants.USER_KEY, userkey); addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid); addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username); // 内部请求来源参数清除 removeHeader(mutate, SecurityConstants.FROM_SOURCE); return chain.filter(exchange.mutate().request(mutate.build()).build()); } @Nullable public Mono<Void> clientCheck(ServerWebExchange exchange, String url, String userid) { ClientInfo clientInfo = redisService.getCacheObject(EGatewayRedisKey.ClientInfo, userid); if(null != clientInfo && !EBoolean.True.getStringValue().equals(clientInfo.getStatus())){ return unauthorizedResponse(exchange, "客户端未启用:" + clientInfo.getClientKey()); } if(null != clientInfo && !StringUtils.isBlank(clientInfo.getIpWhiteList()) && !"*".equals(clientInfo.getIpWhiteList())){ List<String> ipWhiteList = Arrays.asList(clientInfo.getIpWhiteList().split(",")); String clientIp = IpUtils.getIpAddr(exchange.getRequest()); if(!ipWhiteList.contains(clientIp)){ return unauthorizedResponse(exchange, "客户端IP未授权:" + clientIp); } } JSONArray clientResources = redisService.getCacheObject(EGatewayRedisKey.ClientResource, userid); List<ClientResource> clientResourceList = new ArrayList<>(); if (StringUtils.isNotNull(clientResources)){ clientResourceList = clientResources.toList(ClientResource.class); } Optional<ClientResource> clientResourceOptional = clientResourceList.stream().filter(t -> EClientResourceType.URL.getCode().equals(t.getResourceType()) && url.equals(t.getResourceContent())).findFirst(); // 匹配全限定和通配符 if(!clientResourceOptional.isPresent()){ clientResourceOptional = clientResourceList.stream().filter(t -> EClientResourceType.URL.getCode().equals(t.getResourceType()) && StringUtils.isMatch(t.getResourceContent(), url)).findFirst(); if(!clientResourceOptional.isPresent()){ return unauthorizedResponse(exchange, "客户端资源未授权:" + url); } } ClientResource clientResource = clientResourceOptional.get(); if(null != clientResource.getHourFrequency()){ String clientFrequencyKey = String.format("%s - %s", userid, url); Integer clientFrequency = redisService.getCacheObject(EGatewayRedisKey.ClientFrequency, clientFrequencyKey); if (clientFrequency == null){ clientFrequency = 0; } if(clientFrequency >= clientResource.getHourFrequency()){ return unauthorizedResponse(exchange, "小时内请求次数已达限流量:" + clientResource.getHourFrequency()); } clientFrequency = clientFrequency + 1; redisService.setCacheObject(EGatewayRedisKey.ClientFrequency, clientFrequencyKey, clientFrequency, 60L, TimeUnit.MINUTES); } return null; } private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value){ if (value == null){ return; } String valueStr = value.toString(); String valueEncode = ServletUtils.urlEncode(valueStr); mutate.header(name, valueEncode); } private void removeHeader(ServerHttpRequest.Builder mutate, String name){ mutate.headers(httpHeaders -> httpHeaders.remove(name)).build(); } private EAuthenticationSource loginCheck(String userkey){ // boolean islogin = redisService.hasKey(EGatewayRedisKey.Token, userkey); // if (!islogin){ // islogin = redisService.hasKey(EGatewayRedisKey.ClientToken, userkey); // if (!islogin){ // return null; // }else{ // return EAuthenticationSource.Client; // } // }else{ return EAuthenticationSource.User; // } } private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg){ log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED); } /** * 获取请求token */ private String getToken(ServerHttpRequest request){ String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION); // 如果前端设置了令牌前缀,则裁剪掉前缀 if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)){ token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY); } return token; } @Override public int getOrder(){ return -200; } }