https://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调用时请求头的追加。

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

Logo

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

更多推荐