本文主要介紹Spring Cloud Gateway過濾器全家桶,GlobalFilter局過濾器接口與 GatewayFilter 網關過濾器接口具有相同的方法定義,全局過濾器是一系列特殊的過濾器,會根據條件應用到所有路由中。網關過濾器是更細粒度的過濾器,作用於指定的路由中。
網關鑑權
通過自定義過濾器 AuthFilter 實現 GlobalFilter 實現網關鑑權:
/**
* 網關鑑權
*
* @author jacklin
*/
@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);
boolean islogin = redisService.hasKey(getTokenKey(userkey));
if (!islogin) {
return unauthorizedResponse(exchange, "登錄狀態已過期");
}
String userid = JwtUtils.getUserId(claims);
String username = JwtUtils.getUserName(claims);
if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) {
return unauthorizedResponse(exchange, "令牌驗證失敗");
}
// 設置用戶信息到請求
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());
}
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 Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
log.error("[鑑權異常處理]請求路徑:{}", exchange.getRequest().getPath());
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
}
/**
* 獲取緩存key
*/
private String getTokenKey(String token) {
return CacheConstants.LOGIN_TOKEN_KEY + token;
}
/**
* 獲取請求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;
}
}