Spring Cloud Gateway实现网关统一鉴权,网关统一Token认证

需求背景

在微服务的场景下,采用了Spring Cloud Oauth2进行token的管理,实现认证和授权,在这下背景下,有两种解决方案:

网关统一鉴权

此模式适用于网关下的所有模式都是通过一种模式进行鉴权操作,可以统一管理

微服务模块各自鉴权

此模式适用于网关下的各个模块有不同的鉴权模式,针对不同的业务场景需要满足不同的实现,如采用oauth2、shiro、签名等方式。

下文就网关统一鉴权的实现方式提供解决方案,以供参考

实现方案

通过过滤器的方式实现统一拦截,下面以核心代码的方式展示,具体所有代码可以参考Matecloud项目。

过滤器代码

package vip.mate.gateway.filter;

import io.jsonwebtoken.Claims;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import vip.mate.core.cloud.props.MateUaaProperties;
import vip.mate.core.common.constant.MateConstant;
import vip.mate.core.common.constant.Oauth2Constant;
import vip.mate.core.common.util.ResponseUtil;
import vip.mate.core.common.util.SecurityUtil;

/**
 * 网关统一的token验证
 *
 * @author pangu
 */
@Slf4j
@Component
@AllArgsConstructor
public class UaaFilter implements GlobalFilter, Ordered {
     

	private final MateUaaProperties mateUaaProperties;

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
     
		// 如果未启用网关验证,则跳过
		if (!mateUaaProperties.getEnable()) {
     
			return chain.filter(exchange);
		}
		log.error("getIgnoreUrl:{}", mateUaaProperties.getIgnoreUrl());

		// 如果在忽略的url里,则跳过
		String path = replacePrefix(exchange.getRequest().getURI().getPath());
		String requestUrl = exchange.getRequest().getURI().getRawPath();
		if (ignore(path) || ignore(requestUrl)) {
     
			return chain.filter(exchange);
		}

		// 验证token是否有效
		ServerHttpResponse resp = exchange.getResponse();
		String headerToken = exchange.getRequest().getHeaders().getFirst(Oauth2Constant.HEADER_TOKEN);
		if (headerToken == null) {
     
			return unauthorized(resp, "没有携带Token信息!");
		}
		Claims claims = SecurityUtil.getClaims(headerToken.replace("bearer ",""));
		if (claims == null) {
     
			return unauthorized(resp, "token已过期或验证不正确!");
		}
		return chain.filter(exchange);
	}

	/**
	 * 检查是否忽略url
	 * @param path 路径
	 * @return boolean
	 */
	private boolean ignore(String path) {
     
		return mateUaaProperties.getIgnoreUrl().stream()
				.map(url -> url.replace("/**", ""))
				.anyMatch(path::startsWith);
	}

	/**
	 * 移除模块前缀
	 * @param path 路径
	 * @return String
	 */
	private String replacePrefix(String path) {
     
		if (path.startsWith("/mate")) {
     
			return path.substring(path.indexOf("/",1));
		}
		return path;
	}

	private Mono<Void> unauthorized(ServerHttpResponse resp, String msg) {
     
		return ResponseUtil.webFluxResponseWriter(resp, "application/json;charset=UTF-8", HttpStatus.UNAUTHORIZED, msg);
	}

	@Override
	public int getOrder() {
     
		return MateConstant.MATE_UAA_FILTER_ORDER;
	}

	public static void main(String[] args) {
     

	}
}

方法上面也有注解,先通过注解的方式熟悉一下实现流程。

配置类

package vip.mate.core.cloud.props;

import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 验证权限配置
 *
 * @author pangu
 * @date 2020-10-28
 */
@Setter
@RefreshScope
@ConfigurationProperties(prefix = "mate.uaa")
public class MateUaaProperties {
     

	/**
	 * 忽略URL,List列表形式
	 */
	private List<String> ignoreUrl = new ArrayList<>();

	/**
	 * 是否启用网关鉴权模式
	 */
	private Boolean enable = false;

	/**
	 * 监控中心和swagger需要访问的url
	 */
	private static final String[] ENDPOINTS = {
     
			"/oauth/**",
			"/actuator/**",
			"/v2/api-docs/**",
			"/v2/api-docs-ext/**",
			"/swagger/api-docs",
			"/swagger-ui.html",
			"/doc.html",
			"/swagger-resources/**",
			"/webjars/**",
			"/druid/**",
			"/error/**",
			"/assets/**",
			"/auth/logout",
			"/auth/code"
	};

	/**
	 * 自定义getter方法,并将ENDPOINTS加入至忽略URL列表
	 * @return List
	 */
	public List<String> getIgnoreUrl() {
     
		if (!ignoreUrl.contains("/doc.html")) {
     
			Collections.addAll(ignoreUrl, ENDPOINTS);
		}
		return ignoreUrl;
	}

	public Boolean getEnable() {
     
		return enable;
	}
}

此配置类,主要配置忽略鉴权的URL和是否启用网关鉴权的开关。
其中需要在Nacos里增加如下配置,ignore-url配置的是忽略的url地址,在这里可以动态配置,并实时刷新。

mate:
  uaa:
    enable: false
    ignore-url:
      - /auth/login/**
      - /auth/callback/**
      - /auth/sms-code

至此,配置完成。

代码案例

MateCloud微服务:https://github.com/matevip/matecloud

你可能感兴趣的:(微服务,spring,boot,gateway,cloud,gateway,网关统一鉴权,matecloud)