本文共 8172 字,大约阅读时间需要 27 分钟。
网关作为流量的入口,常用功能包括路由转发、权限校验、限流控制等。而springcloud gateway作为SpringCloud官方推出的第二代网关框架,取代了Zuul网关。
org.springframework.cloud spring-cloud-starter-gateway
springcloud gateway使用RouteLocator的Bean进行路由转发,对请求进行处理,并转发给下游服务处理。
@Beanpublic RouteLocator myRoutes(RouteLocatorBuilder builder) { return builder.routes() .route(p -> p .path("/get") .filters(f -> f.addRequestHeader("Hello", "World")) .uri("http://httpbin.org:80")) .build();}
sprincloud gateway中主要有两种类型的过滤器:GlobalFilter
和 GatewayFilter
指定的路由
起作用,单路由过滤器
自定义GatewayFilter有两种实现方式,一种是直接实现GatewayFilter接口,另一种是继承AbstractGatewayFilterFactory类 ,任意选一种即可
1、指定路由,应用GatewayFilter接口
/** * token校验过滤器 */public class AuthorizeGatewayFilter implements GatewayFilter, Ordered { private static final String AUTHORIZE_TOKEN = "token"; private static final String AUTHORIZE_UID = "uid"; @Autowired private StringRedisTemplate stringRedisTemplate; @Override public Monofilter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); String token = headers.getFirst(AUTHORIZE_TOKEN); String uid = headers.getFirst(AUTHORIZE_UID); if (token == null) { token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN); } if (uid == null) { uid = request.getQueryParams().getFirst(AUTHORIZE_UID); } ServerHttpResponse response = exchange.getResponse(); if (StringUtils.isEmpty(token) || StringUtils.isEmpty(uid)) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } String authToken = stringRedisTemplate.opsForValue().get(uid); if (authToken == null || !authToken.equals(token)) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return 0; }}// 对指定路由的应用@Beanpublic RouteLocator routeLocator(RouteLocatorBuilder builder) { return builder.routes().route(r -> r.path("/user/list") .uri("http://localhost:8080/api/user/list") .filters(new AuthorizeGatewayFilter()) .id("user-service")) .build();}
2、指定路由,继承AbstractGatewayFilterFactory
/** * 自定义basic认证 */@Slf4j@Componentpublic class HttpBasicGatewayFilter extends AbstractGatewayFilterFactory { @Override public GatewayFilter apply(Object config) { return (exchange, chain) -> { if (hasAuth(exchange)) { return chain.filter(exchange); } else { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.UNAUTHORIZED); response.getHeaders().add(HttpHeaders.WWW_AUTHENTICATE, "Basic Realm=\"xaax\""); return response.setComplete(); } }; } /** * 简单的basic认证 * * @param exchange 上下文 * @return 是否有权限 */ private boolean hasAuth(ServerWebExchange exchange) { ServerHttpRequest request = exchange.getRequest(); String auth = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION); log.info("Basic认证信息为:{}", auth); return true; }}
再举一例
/** * @date 2018/7/4 * 验证码处理 */@Slf4j@Component@AllArgsConstructorpublic class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory { private final ObjectMapper objectMapper; private final RedisTemplate redisTemplate; private final FilterIgnorePropertiesConfig filterIgnorePropertiesConfig; @Override public GatewayFilter apply(Object config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); // 不是登录请求,直接向下执行 if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath() , SecurityConstants.OAUTH_TOKEN_URL, SecurityConstants.SMS_TOKEN_URL , SecurityConstants.SOCIAL_TOKEN_URL)) { return chain.filter(exchange); } // 刷新token,直接向下执行 String grantType = request.getQueryParams().getFirst("grant_type"); if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) { return chain.filter(exchange); } // 终端设置不校验, 直接向下执行 try { String[] clientInfos = WebUtils.getClientId(request); if (filterIgnorePropertiesConfig.getClients().contains(clientInfos[0])) { return chain.filter(exchange); } // 如果是社交登录,判断是否包含SMS if (StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.SOCIAL_TOKEN_URL)) { String mobile = request.getQueryParams().getFirst("mobile"); if (StrUtil.containsAny(mobile, LoginTypeEnum.SMS.getType())) { throw new ValidateCodeException("验证码不合法"); } else { return chain.filter(exchange); } } //校验验证码 checkCode(request); } catch (Exception e) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED); try { return response.writeWith(Mono.just(response.bufferFactory() .wrap(objectMapper.writeValueAsBytes( R.builder().msg(e.getMessage()) .code(CommonConstants.FAIL).build())))); } catch (JsonProcessingException e1) { log.error("对象输出异常", e1); } } return chain.filter(exchange); }; } /** * 检查code * * @param request */ @SneakyThrows private void checkCode(ServerHttpRequest request) { String code = request.getQueryParams().getFirst("code"); if (StrUtil.isBlank(code)) { throw new ValidateCodeException("验证码不能为空"); } String randomStr = request.getQueryParams().getFirst("randomStr"); if (StrUtil.isBlank(randomStr)) { randomStr = request.getQueryParams().getFirst("mobile"); } String key = CommonConstants.DEFAULT_CODE_KEY + randomStr; redisTemplate.setKeySerializer(new StringRedisSerializer()); if (!redisTemplate.hasKey(key)) { throw new ValidateCodeException("验证码不合法"); } Object codeObj = redisTemplate.opsForValue().get(key); if (codeObj == null) { throw new ValidateCodeException("验证码不合法"); } String saveCode = codeObj.toString(); if (StrUtil.isBlank(saveCode)) { redisTemplate.delete(key); throw new ValidateCodeException("验证码不合法"); } if (!StrUtil.equals(saveCode, code)) { redisTemplate.delete(key); throw new ValidateCodeException("验证码不合法"); } redisTemplate.delete(key); }}
只需要添加 @Component 注解,不需要进行任何额外的配置,实现GlobalFilter接口,自动会对所有的路由起作用
/** * * token校验全局过滤器 */@Componentpublic class AuthorizeFilter implements GlobalFilter, Ordered { private static final String AUTHORIZE_TOKEN = "token"; private static final String AUTHORIZE_UID = "uid"; @Autowired private StringRedisTemplate stringRedisTemplate; @Override public Monofilter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); String token = headers.getFirst(AUTHORIZE_TOKEN); String uid = headers.getFirst(AUTHORIZE_UID); if (token == null) { token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN); } if (uid == null) { uid = request.getQueryParams().getFirst(AUTHORIZE_UID); } ServerHttpResponse response = exchange.getResponse(); if (StringUtils.isEmpty(token) || StringUtils.isEmpty(uid)) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } String authToken = stringRedisTemplate.opsForValue().get(uid); if (authToken == null || !authToken.equals(token)) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return 0; }}
springcloud gateway需要排除spring-boot-starter-web引入spring-boot-starter-webflux
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-webflux
参考
转载地址:http://sbaxb.baihongyu.com/