1.Spring Cloud Gateway简介
Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。
2.创建工程
本文我们采用最新的Spring Cloud 版本“Finchley.SR2”,注意该版本对应Spring Boot为2x。官方推荐的是:2.0.6.RELEASE版本。
父项目导入如下包:

<dependencyManagement>
   <dependencies>
        <!-- spring boot依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.8.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
         <!--mybatis依赖-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.0</version>
            </dependency>
            <!--dubbo依赖-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>dubbo</artifactId>
                <version>2.6.6</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.spring</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>1.0.2</version>
            </dependency>
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-framework</artifactId>
                <version>4.0.1</version>
            </dependency>
            <!--zookeeper依赖-->
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.4.6</version>
            </dependency>
            <!--validator依赖-->
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>6.0.9.Final</version>
            </dependency>
     </dependencies>
</dependencyManagement>

gateway网关服务导入如下jar:

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--dubbo依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.spring</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
        </dependency>
        <!--zookeeper依赖-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.uaf.credit</groupId>
            <artifactId>uaf-credit-api</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

父项目统一管理jar包的版本,子项目就不必再添加jar对应的版本号

网关服务的yml配置如下:

server:
  port: 8817
spring:
  application:
    name: uaf-credit-gateway
  security:
    user:
      name: wxt
      password: wxt2016
  cloud:
    gateway:
      routes:
        - id: credit-auth-route
          uri: http://10.168.xx.xx:8820/credit-auth/v1
          predicates:
            - Path=/credit-auth/v1/*               #路径匹配,匹配所有请求路径以/credit-auth开头的用户请求
logging:
  config: classpath:logback.xml

3.Spring Cloud Gateway过滤器
Spring-Cloud-Gateway的filter包中吉接口有如下三个,GatewayFilter,GlobalFilter,GatewayFilterChain,GlobalGilter 全局过滤器接口与 GatewayFilter 网关过滤器接口具有相同的方法定义。全局过滤器是一系列特殊的过滤器,会根据条件应用到所有路由中。网关过滤器是更细粒度的过滤器,作用于指定的路由中。
在这里插入图片描述我们可以配置多个GlobalFilter过滤器,通过指定getOrder()方法的优先级来配置过滤器的执行顺序。

@Component
public class RequestAuthFilter implements GlobalFilter, Ordered {

	/**
	 * 请求方式验证过滤器
	 * @param exchange
	 * @param chain
	 * @return reactor.core.publisher.Mono<java.lang.Void>
	 * 作者:will
	 * 日期:2019/4/4 14:46
	 */
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		ServerHttpRequest serverHttpRequest = exchange.getRequest();
		String method = serverHttpRequest.getMethodValue();
		if(!"POST".equals(method)){
			ServerHttpResponse response = exchange.getResponse();
			String message= new ResponseUtils().CreditRespMsg(CreditException.ERR_100008,"非法请求",null);
			byte[] bits = message.getBytes(StandardCharsets.UTF_8);
			DataBuffer buffer = response.bufferFactory().wrap(bits);
			response.setStatusCode(HttpStatus.UNAUTHORIZED);
			//指定编码,否则在浏览器中会中文乱码
			response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
			return response.writeWith(Mono.just(buffer));
		}
		return chain.filter(exchange);
	}

	/**
	 * 优先级
	 * @return int 数字越大优先级越低
	 * 作者:will
	 * 日期:2019/4/4 13:36
	 */
	@Override
	public int getOrder() {
		return 0;
	}
}

4.Spring boot Security认证
Spring Security致力于为Java应用提供认证和授权管理。它是一个强大的,高度自定义的认证和访问控制框架,这句话包括两个关键词:Authentication(认证)和 Authorization(授权,也叫访问控制)。
需要安全认证的服务需要导入如下Jar:

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-security</artifactId>
 </dependency>

yml配置:

server:
  port: 8820
  servlet:
    context-path: /credit-auth
spring:
  application:
    name: uaf-credit-auth
  security:
    user:
      name: wxt
      password: wxt2016
      roles:
        - USER
logging:
  config: classpath:logback.xml

接下来配置认证类:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		/**表示所有的访问都必须进行认证处理后才可以正常进行*/
		http.httpBasic().and().authorizeRequests().anyRequest().fullyAuthenticated();
		/**所有的Rest服务一定要设置为无状态,以提升操作性能*/
		http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
		/**关闭csrf避免POST请求时出现401异常*/
		http.csrf().disable();
		http.authorizeRequests().antMatchers(org.springframework.http.HttpMethod.GET).permitAll();
	}

}

这里有个问题,外面的服务若请求我们的服务需要进行Security认证,但我们的网关应该需要免认证。
我们通过Gateway的GlobalFilter过滤器的方式实现:

@Component
public class OAuthSignatureFilter implements GlobalFilter, Ordered {

	/**授权访问用户名*/
	@Value("${spring.security.user.name}")
	private String securityUserName;
	/**授权访问密码*/
	@Value("${spring.security.user.password}")
	private String securityUserPassword;

	/**
	 * OAuth过滤器
	 * @param exchange
	 * @param chain
	 * @return reactor.core.publisher.Mono<java.lang.Void>
	 * 作者:will
	 * 日期:2019/4/4 13:36
	 */
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		/**oauth授权*/
		String auth= securityUserName.concat(":").concat(securityUserPassword);
		String encodedAuth = new sun.misc.BASE64Encoder().encode(auth.getBytes(Charset.forName("US-ASCII")));
		//注意Basic后面有空格
		String authHeader= "Basic " +encodedAuth;
		//向headers中放授权信息
		ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().header("Authorization",authHeader).build();
		//将现在的request变成change对象
		ServerWebExchange build =exchange.mutate().request(serverHttpRequest).build();
		return chain.filter(build);
	}


	/**
	 * 优先级
	 * @return int 数字越大优先级越低
	 * 作者:will
	 * 日期:2019/4/4 13:36
	 */
	@Override
	public int getOrder() {
		return 2;
	}

5.新增自定义过滤器
gateway里面可以自定义普通filter,也可以创建自定义的GlobalFilter,我们通过继承AbstractGatewayFilterFactory实现自定义过滤器。
yml新增如下配置:

spring:
  application:
    name: uaf-credit-gateway
  security:
    user:
      name: wxt
      password: wxt2016
  cloud:
    gateway:
      routes:
        - id: credit-auth-route
          uri: http://10.168.xx.xx:8820/credit-auth/v1
          predicates:
            - Path=/credit-auth/v1/*        #路径匹配,匹配所有请求路径以/credit-auth开头的用户请求
          filters:
            - CreditFilter                #注意与定义的过滤器类名一致                     

新增CreditFilter.java类,自定义过滤器的优先级低于GlobalFilter

@Configuration
public class CreditFilter extends AbstractGatewayFilterFactory<CreditFilter.Config> {

	public CreditFilter() {
		super(Config.class);
	}

	@Override
	public GatewayFilter apply(Config config) {
		MySlf4j.textInfo("进入自定义Credit过滤器");
		return (exchange, chain) -> {
			String jwtToken = exchange.getRequest().getHeaders().getFirst("Authorization");
			//校验jwtToken的合法性
			if (jwtToken != null) {
				// 合法
				// 将用户id作为参数传递下去
				return chain.filter(exchange);
			}

			//不合法(响应未登录的异常)
			ServerHttpResponse response = exchange.getResponse();
			//设置headers
			HttpHeaders httpHeaders = response.getHeaders();
			httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
			httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
			//设置body
			String warningStr = "未登录或登录超时";
			DataBuffer bodyDataBuffer = response.bufferFactory().wrap(warningStr.getBytes());

			return response.writeWith(Mono.just(bodyDataBuffer));
		};
	}

	public static class Config {

	}

	@Bean
	public CreditFilter creditFileterFactory() {
		return new CreditFilter();
	}
}

至此我们的Gateway及相关的授权认证配置完成。


欢迎关注公众号: 编码是个技术活

在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐