SpringCloud——路由网关Zuul(包括过滤器功能)+Gateway网关(解决跨域)

本文介绍了如何使用SpringCloud的Zuul和Gateway组件搭建服务网关,包括路由转发、负载均衡、过滤器应用(如验证请求和验证码检查)以及如何处理跨域问题。同时,文章提到了整合用户认证的策略,通过自定义过滤器实现API接口的权限控制。
摘要由CSDN通过智能技术生成

网关作用:

  • 将请求路由到真实的服务器上 ,进而保护真实服务器的IP地址, 避免直接地攻击真实服务器
  • 作为负载均衡的手段,使得请求按照特定的算法平摊到多个节点上,减缓单点压力
  • 作为过滤器的使用可以判定请求是否为有效请求一旦判定失败,就可以将请求阻止,避免发送到真实的服务器,这样就能降低真实服务器的压力

一、新建工程,导入依赖

spring-cloud-starter-netflix-euraka-client
spring-cloud-tarter-netflix-zuul

二、主类添加注解,启用Zuul网关

@SpringBootApplication(scanBasePackage="com.springboot.chapter.zuul")
@EnableZuulProxy //开启Zuul注解代理,内部已经引入了断路机制
public class ChapterZuulApplication{
	//...略
}

三、配置properties

server.port=80
spring.application.name=zuul

# 指定ANT风格的URL
zuul.routes.user-service.path=/u/**
#指定映射的服务用户地址,这样Zuul就会将请求转发到用户微服务上了
zuul.routes.user-servcie.url=http://localhost:8001/
# 产品微服务映射规则
zuul.routes.product-service.path=/p/**
# 映射产品服务中心ID,zuul会自动使用服务端负载均衡,分摊请求
zuul.routes.product-service.serviceId=product


# 注册给服务治理中心集群
euraka.client.serviceUrl.defaultZone=http://localhost:7001/euraka,http://localhost:7002/euraka

使用过滤器
监测用户登录、黑名单用户、购物验证码、恶意刷请求攻击等场景

一,引入Redis依赖

<dependency> 
	<groupid>org.sprigframework.boot</g oupid>
	<artifactid>spring-boot-starter-data-redis</artifactid> 
	<exclusions> 
		<!--不依赖redis的异步客户端-->
		<exclusion> 
			<groupid>io.lettuce</group i d> 
			<artifactid>lettuce-core</artifactid> 
		</exclusion> 
	</exclusions> 
</dependency> 
<!--Redis客户端驱动jedis-->
<dependency> 
	<groupId> red ients</groupId>
	<artifactid>jedis</artifactid> 
</dependency>

二、配置文件

spring.redis.jedis.pool.min-idle=5
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.max-wait=2000
spring.redis.port=6379
spring.redis.host=192.168.11.131
spring.redis.password=123456
spring.redis.timeout=1000

三、使用过滤器判定验证码

@Component  //该注解表示Spring会扫描,并装配到IOC容器中
public class MyZuulFilter extends ZuulFilter{
	@Autowired
	private StringRedisTemplate redisTemplate = null;

	@Override
	public boolean shoudFilter(){ //返回true,则主席那个这个过滤器的run方法
		//请求上下文
		RequestContext ctx = RequestContext.getCurrentContext();
		//获取HttpServletRequest对象
		HttpServletRequest req = ctx.getRequest();
		//取出表单序列号
		String serialNumber = req.getParameter("serialNumber");
		//如果存在验证码,返回true,启用过滤器
		return !StringUtils.isEmpty(serialNumber);
	}

	@Override
	public Object run(){
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest req = ctx.getRequest();
		//去除表单序列号和请求验证码
		String serialNumber = req.getParameter("serialNumber");
		String reqCode = req.getParameter("verificationCode");

		//从Redis中取出验证码
		String verifCode = redisTemplate.opsForValue().get(serialNumber);
		//若redis验证码为空或者与请求不一致,拦截请求爆出错误
		if(verifCode == null || !verifCode.equals(reqCode)){
			ctx.setSendZuulResponse(false);//不再转发请求
			ctx.setResponseStateusCode(401);//设置HTTP响应码为401(未授权)
			ctx.getResponse().setContentType(MediaType.APPLICATION_JSON_UTF8.getType());

			//设置响应体
			ctx.setResponseBody("{'success':false," + "'message':Verification Code Error");
		}
		//一致放过
		return null;
	}

	//过滤器类型为请求前
	@Override
	public String filterType(){
		return "pre";
	}
	//过滤器排序,数字越小优先级越高
	@Override
	public int filterOrder(){
		return 0;
	}
}

四、通过浏览器请求进行测试

@SpringCloudApplication注解

会启用SpringBoot的应用,以及开发服务发现和断路器的功能
但缺乏扫描包配置项,需要配合@ComponentScan定义扫描包
在这里插入图片描述

@EnableFeignClients(basePackages="com.springboot.chapter.product")
@ComponentScan(basePackages="com.springboot.chapter.product")
@SpringCloudApplication //并不提供配置包
public class ChapterProductApplication{
}

=============================================================================

Gateway网关

解决跨域问题,不需要在每个Controller上添加@CrossOrigin注解
跨域问题是浏览器对于ajax请求的一种安全限制:一个月面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。
替代NetflixZuul
提供了统一的路由方式,并且基于Filer链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等
在这里插入图片描述

一、创建一个maven模块,添加依赖

spring-cloud-starter-gateway
spring-cloud-starter-alibaba-nacos-discovery

二、配置文件application.properties

server.port=80
spring.application.name=service-gateway
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

#开启服务发现路由
spring.cloud.gateway.discovery.locator.enabled=true

#设置路由id
spring.coud.gateway.routes[0].id=service-hosp
#设置路由的uri
spring.cloud.gateway.routes[0].uri=lb://service-hosp
#设置路由断言,代理serviceId为auth-service的/auth/路径
spring.cloud.gateway.routes[0].predicates=Path=/*/hosp/**

#设置路由id
spring.coud.gateway.routes[1].id=service-cmn
#设置路由的uri
spring.cloud.gateway.routes[1].uri=lb://service-cmn
#设置路由断言,代理serviceId为auth-service的/auth/路径(一个*表示一层目录)
spring.cloud.gateway.routes[1].predicates=Path=/*/cmn/**

三、添加启动类

@SpringBootApplication
public class ServerGatewayApplication{
	public static void main(String[] args){
		SpringApplication.run(ServerGatewayApplication.class,args);
	}
}

四、配合前端测试

config/dev.env.js中的配置内容(指定网关路径)
在这里插入图片描述
在这里插入图片描述

Gateway解决跨域的配置类实现(Controller中的@CrossOrigin注解必须去掉)

@Configuration
public class CorsConfig{
	
	@Bean
	public CorsWebFilter corsFilter(){
		CorsConfiguration config = new CorsConfiguration();
		config.addAllowedMethod("*");
		config.addAllowedOrigin("*");
		config.addAllowedHeader("*");
		
		UrlBaseCorsConfigurationSource source = new UrlBaseCorsConfigurationSource(new PathPatternParser());
		source.registerCorsConfiguration("/**",config);
		return new CorsWebFilter(source);
	}
}

网关与用户认证的整合

  • 所有请求都会经过服务网关,服务网关对外暴露服务,在网关进行统一用户认证
  • 既然在网关进行用户认证,网关需要得知哪些url进行认证,所以需要对url制定规则
  • Api接口异步请求的,采取url规则匹配。如:/api//auth/,凡是满足该规则都必须进行用户认证

一、创建一个Filter包,在里面创建制定规则的过滤类

@Component
public class AuthGlobalFilter implements GlobalFilter,Ordered{
	
	private AntPathMatcher antPathMatcher = new AntPathMatcher();

	@Override
	public Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain){
		ServerHttpRequest request = exchange.getRequest();
		String path = request.getURI().getPath();
		System.out.println("===" + path);

		//内部服务接口,不允许外部访问
		if(antPathMatcher.match("/**/inner/**",path)){
			ServerHttpResponse response = exchange.getResponse();
			return out(response,ResultCodeEnum.PERMISSION);
		}

		Long userId = this.getUserId(request);
		//api接口,异步请求,校验用户必须登录
		if(antPathMatcher.match("/api/**/auth/**",path)){
			if(StringUtils.isEmpty(userId)){
				ServerHttpResponse response = exchange.getResponse();
				return out(response,ResultCodeEnum.LOGIN_AUTH);
			}
		}
		return chain.fileter(exchange)
	}

	@Override
	public int getOrder(){
		return 0;
	}

	//api接口鉴权失败返回数据
	private Mono<Void> out(ServerHttpResponse response,ResultCodeEnum resultCodeEnum){
		Result result = Result.build(null,resultCodeEnum);
		byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
		DataBuffer buffer = response.bufferFactory().wrap(bits);
		//指定编码,否则在浏览器中会中文乱码
		response.getHeaders().add("Content-type","application/json;charset=UTF-8");
		return response.writeWith(Mono.just(buffer));
	}

	//获取当前登录用户id
	private Long getUserId(ServerHttpRequest request){
		String token = "";
		List<String> tokenList = request.getHeaders().get("token");
		if(null != tokenList){
			return JwtHelper.getUserId(token);
		}
		return null;
	}
}

二、前端request.js

import axios from 'axios'
import {MessageBox,Message} from 'element-ui'
import cookie from 'js-cookie'

//创建axios实例
const service = axios.create({
	baseURL:'http://localhost'//网关路径
	timeout:15000 //请求超时时间
})

//http request 拦截器
service.interceptors.request.use(
	config => {
		//判断cookie是否有token里面
		if(cookie.get('token')){
			//将token值放到cookie里面
			config.headers['token'] = cookie.get('token')
		}
		return config
	},
	err => {
		return Promise.reject(err)
	}
)

//http response拦截器
service.interceptors.response.use(
	response => {
		if(response.data.code===208){
			//后端返回的状态码208,就弹出登录弹框
			loginEvent.$emit('loginDialogEvent')
			return 
		}else{
			if(response.data.code !== 200){
			Message({
				message:response.data.message,
				type:'error',
				duration:5*1000
			})
			return Promise.reject(response.data)
		}else{
			return response.data
		}
		
	}
)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值