SpringCloud确保服务只能通过gateway转发访问,禁止直接调用接口访问

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

SpringCloud确保服务只能通过gateway转发访问,禁止直接调用接口访问

作者Hpsyche

blog.csdn.net/Hpsyche/article/details/102926010

前言

在微服务体系架构中,网关承担着重要的角色,在网关中可以添加各种过滤器,过滤请求,保证请求参数安全,限流等等。如果请求绕过了网关,那就等于绕过了重重关卡,直捣黄龙,所以,在分布式架构中,我们需要有一定的防范,来确保各个服务相互之间安全调用。

正文

思路

1、在网关中给所有请求加上一个请求密钥

2、在服务添加过滤器,验证密钥

首先在网关服务(gateway)中添加过滤器,给放行的请求头上加上判断

package com.hpsyche.hpsychegatewayserver.filter;

import com.hpsyche.hpsychegatewayserver.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @author Hpsyche
 */

public class TokenFilter implements GlobalFilter {
    @Autowired
    private RedisUtil redisUtil;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
     //token(用户身份)判断
     ................
     
        ServerHttpRequest req = exchange.getRequest().mutate()
                .header("from""gateway").build();
        return chain.filter(exchange.mutate().request(req.mutate().build()).build());
    }
}

如果考虑到安全性的话,这里header的值也可以使用UUID,并保存至redis中,在其他服务中通过redis读取判断身份。

在主服务,如auth授权服务上,添加全局拦截器,判断header上是否有对应的value,是则否则,反之拦截。

package com.hpsyche.hpsycheauthserver.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * @author Hpsyche
 */

@Slf4j
public class GlobalInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {
        String secretKey = request.getHeader("from");
        if(!StringUtils.isNotBlank(secretKey)||secretKey.equals("gateway"){
            response.setContentType("application/json; charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write("error");
            return false;
        }
        return true;
    }
}

此时,绕过网关,直接访问auth服务的接口会返回error,即必须通过网关,我们的任务看似完成了。

问题

在项目中,发现了feign远程调用会失败,因为在fegin中调用不通过gateway,缺少from的请求头,会被拦截器拦截,导致调用失败。

解决方案

在auth-client(服务暴露方)添加请求头,在供其他服务调用时添加header。

package com.hpsyche.hpsycheauthclient.config;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Configuration;

/**
 * Feign调用的时候添加请求头from
 */

@Configuration
public class FeignConfiguration implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {;
        requestTemplate.header("from""gateway");
    }
}

同时,需要在service接口中添加configuration配置:

package com.hpsyche.hpsycheauthclient.api;

import com.hpsyche.hpsychecommonscommon.vo.BaseResponse;
import com.hpsyche.hpsycheauthclient.config.FeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @author Hpsyche
 */

@FeignClient(value = "hpsyche-auth-server",configuration = FeignConfiguration.class)
public interface AuthService 
{
    @PostMapping("/auth/verifyUser")
    BaseResponse verifyUser(@RequestParam("username")String username,@RequestParam("password") String password);
}

由此,实现了feign调用时请求头的追加。

总结

遇到问题多思考,多学习;在搭建服务时自己也遇到了不少问题,通过搜索、借鉴其他大佬们的博客经验才得以解决。由于博主水平有限,如有不足之处望指出!

END

推荐好文

强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!

能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!

SpringCloud确保服务只能通过gateway转发访问,禁止直接调用接口访问

原文始发于微信公众号(Java知音):SpringCloud确保服务只能通过gateway转发访问,禁止直接调用接口访问