SpringCloudGateway网关实战(三)

这篇具有很好参考价值的文章主要介绍了SpringCloudGateway网关实战(三)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

SpringCloudGateway网关实战(三)

上一章节我们讲了gateway的内置过滤器Filter,本章节我们来讲讲全局过滤器。

自带全局过滤器

在实现自定义全局过滤器前, spring-cloud-starter-gateway依赖本身就自带一些全局过滤器,我们举些比较常用的例子:

  1. NettyRoutingFilter:该过滤器使用Netty作为底层的HTTP客户端,负责将请求转发到下游服务。
  2. RouteToRequestUrlFilter:该过滤器将根据路由配置中的URI信息,将请求转发到指定的URL。
  3. WebsocketRoutingFilter:该过滤器用于处理WebSocket协议的请求转发。
  4. GatewayMetricsFilter:该过滤器用于收集网关的基本性能指标数据,例如请求的数量、响应时间等。

自定义全局过滤器

自定义全局过滤器需要实现GlobalFilter接口和Ordered接口。

AuthFilter

token验证全局过滤器

引入依赖:

        <!-- Jwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

具体代码:

@Component
public class AuthFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);

    @Autowired
    private IgnoreWhiteProperties ignoreWhite;
    @Autowired
    public RedisTemplate redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest.Builder mutate = request.mutate();

        // 跳过白名单
        String url = request.getURI().getPath();
        if (com.smallred.gateway.utils.StringUtils.matches(url, ignoreWhite.getWhites())) {
            return chain.filter(exchange);
        }

        // 检查令牌是否存在
        String token = getToken(request);
        if (StringUtils.isEmpty(token)) {
            return unauthorizedResponse(exchange, "令牌不能为空!");
        }

        //
        Claims claims = JwtUtils.parseToken(token);
        if (claims == null) {
            return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
        }

        // 判断登录状态
        String userKey = JwtUtils.getUserKey(claims);
        Boolean islogin = redisTemplate.hasKey(getTokenKey(userKey));
        if (!islogin) {
            return unauthorizedResponse(exchange, "登录状态已过期!");
        }

        // 验证用户信息
        String userId = JwtUtils.getUserId(claims);
        String userName = JwtUtils.getUserName(claims);
        if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(userName)) {
            return unauthorizedResponse(exchange, "令牌验证失败");
        }


        // 设置用户信息到请求
        addHeader(mutate, "user_key", userKey);
        addHeader(mutate, "user_id", userId);
        addHeader(mutate, "username", userName);
        // 内部请求来源参数清除
        removeHeader(mutate, "from-source");
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }

    /**
     *  添加头
     */
    private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {
        if (value == null) {
            return;
        }

        String valueStr = value.toString();
        String valueEncode = urlEncode(valueStr);
        mutate.header(name, valueEncode);
    }

    /**
     *  删除头
     */
    private void removeHeader(ServerHttpRequest.Builder mutate, String name) {

        mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
    }

    /**
     *  获取缓存key
     */
    private String getTokenKey(String token) {
        return "login_tokens:" + token;
    }

    /**
     *  获取请求token
     */
    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst("Authorization");
        // 如果前端设置了令牌前缀,则裁剪掉前缀
        if (StringUtils.isNotEmpty(token) && token.startsWith("Bearer")) {
            token = token.replaceFirst("Bearer", StringUtils.EMPTY);
        }
        return token;
    }

    /**
     * 内容编码
     *
     * @param str 内容
     * @return 编码后的内容
     */
    public static String urlEncode(String str)
    {
        try
        {
            return URLEncoder.encode(str, "UTF-8");
        }
        catch (UnsupportedEncodingException e)
        {
            return StringUtils.EMPTY;
        }
    }

    /**
     *  验证失败返回
     */
    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
        return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, 401);
    }

    @Override
    public int getOrder() {
        return -200;
    }
}
依赖类

白名单:

@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.ignore")
public class IgnoreWhiteProperties {

    private List<String> whites = new ArrayList<>();

    public List<String> getWhites() {
        return whites;
    }

    public void setWhites(List<String> whites) {
        this.whites = whites;
    }
}

白名单配置:

# 安全配置
security:
  ignore:
    whites:
      - /auth/logout
      - /auth/login
      - /auth/register
      - /*/v2/api-docs
      - /csrf

StringUtils类:

public class StringUtils {

    /** 空字符串 */
    private static final String NULLSTR = "";

    public static boolean matches(String str, List<String> strs)
    {
        if (isEmpty(str) || isEmpty(strs))
        {
            return false;
        }
        for (String pattern : strs)
        {
            if (isMatch(pattern, str))
            {
                return true;
            }
        }
        return false;
    }

    public static boolean isEmpty(String str)
    {
        return isNull(str) || NULLSTR.equals(str.trim());
    }

    public static boolean isEmpty(Collection<?> coll)
    {
        return isNull(coll) || coll.isEmpty();
    }

    public static boolean isNull(Object object)
    {
        return object == null;
    }

    public static boolean isMatch(String pattern, String url)
    {
        AntPathMatcher matcher = new AntPathMatcher();
        return matcher.match(pattern, url);
    }

}

ServletUtils类:

public class ServletUtils {

    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code)
    {
        return webFluxResponseWriter(response, HttpStatus.OK, value, code);
    }

    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code)
    {
        return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
    }

    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code)
    {
        response.setStatusCode(status);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
        R<?> result = R.fail(code, value.toString());
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
        return response.writeWith(Mono.just(dataBuffer));
    }

}

JwtUtils类:

public class JwtUtils {

    public static String secret = "abcdefghijklmnopqrstuvwxyz";

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    public static String createToken(Map<String, Object> claims)
    {
        String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    public static Claims parseToken(String token)
    {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

    /**
     * 根据令牌获取用户标识
     *
     * @param token 令牌
     * @return 用户ID
     */
    public static String getUserKey(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, "user_key");
    }

    /**
     * 根据令牌获取用户标识
     *
     * @param claims 身份信息
     * @return 用户ID
     */
    public static String getUserKey(Claims claims)
    {
        return getValue(claims, "user_key");
    }

    /**
     * 根据令牌获取用户ID
     *
     * @param token 令牌
     * @return 用户ID
     */
    public static String getUserId(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, "user_id");
    }

    /**
     * 根据身份信息获取用户ID
     *
     * @param claims 身份信息
     * @return 用户ID
     */
    public static String getUserId(Claims claims)
    {
        return getValue(claims, "user_id");
    }

    /**
     * 根据令牌获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public static String getUserName(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, "username");
    }

    /**
     * 根据身份信息获取用户名
     *
     * @param claims 身份信息
     * @return 用户名
     */
    public static String getUserName(Claims claims)
    {
        return getValue(claims, "username");
    }

    /**
     * 根据身份信息获取键值
     *
     * @param claims 身份信息
     * @param key 键
     * @return 值
     */
    public static String getValue(Claims claims, String key)
    {
        return toStr(claims.get(key), "");
    }

    public static String toStr(Object value, String defaultValue)
    {
        if (null == value)
        {
            return defaultValue;
        }
        if (value instanceof String)
        {
            return (String) value;
        }
        return value.toString();
    }

}

IpAddressFilter

根据请求记录ip地址日志:文章来源地址https://www.toymoban.com/news/detail-733167.html

@Component
public class IpAddressFilter implements GlobalFilter, Ordered {

    public static final Map<String, AtomicInteger> CACHE = new ConcurrentHashMap<>();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    InetSocketAddress host = exchange.getRequest().getHeaders().getHost();
    if (host == null || host.getHostName() == null) {
      exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
      return exchange.getResponse().setComplete();
     }
    String hostName = host.getHostName();
    AtomicInteger count = CACHE.getOrDefault(hostName, new AtomicInteger(0));
    count.incrementAndGet();
    CACHE.put(hostName, count);
    System.out.println("IP地址:" + hostName + ",访问次数:" + count.intValue());
    return chain.filter(exchange);

    }

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

到了这里,关于SpringCloudGateway网关实战(三)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringCloudGateway 入门

    我的Java版本是17 父模块 子模块 假设:如果没有网关,客户端如何调用微服务,数十个微服务。 如果真有数十个微服务,那客户端要记录数十个微服务的地址,然后分别去使用,这样会存在很多问题,比如: 重复造轮子 每个微服务单独为战,各自实现自己的鉴权、限流、跨

    2024年02月06日
    浏览(27)
  • SpringCloudGateway集成SpringDoc

    最近在搞Spring版本升级,按客户要求升级Spring版本,原来用着SpringBoot 2.2.X版本,只需要升级SpringBoot 2.X最新版本也就可以满足客户Spring版本安全要求,可是好像最新的SpringBoot 2.X貌似也不支持Swagger2了,综合考虑,把项目的环境升级到SpringBoot3 SpringBoot 3.1.2 SpringCloud 2022.0.3 (Sp

    2024年02月10日
    浏览(27)
  • SpringCloudGateway学习(2)-过滤器

     Gateway 作用:过滤器就是在请求的传递过程中,对请求和响应做一些手脚。  Gateway 生命周期:Pre Post。 PRE :这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。 POST :这种过滤器在路由到微服务以后执

    2024年02月12日
    浏览(38)
  • SpringCloudGateway整合swagger3文档

             SpringCloud项目中,微服务模块和网关模块必不可少。按照以前SpringBoot的模式,单个服务拥有自己的Api文档(Swagger文档),引入微服务后,多文档管理成了一个问题。我们需要一个统一的入口方便前端同学查看。本篇文章就是把各个微服务的swagger-api文档,集成到网

    2024年02月09日
    浏览(42)
  • SpringCloudGateway集成SpringDoc CORS问题

    集成SpringDoc后,在gateway在线文档界面,请求具体的服务接口,报CORS问题 Failed to fetch. Possible Reasons: CORS Network Failure URL scheme must be “http” or “https” for CORS request. 其实是网关直接请求具体服务/v3/api-docs接口(默认),获取文档数据,里面包含该服务注册上来的地址,gateway swa

    2024年02月10日
    浏览(34)
  • SpringCloudGateway--过滤器(内置filter)

    目录 一、概览 二、内置过滤器 1、StripPrefix 2、AddRequestHeader 3、AddResponseHeader 4、DedupeResponseHeader 5、AddRequestParameter 6、CircuitBreaker 7、FallbackHeaders 8、RequestRateLimiter 9、RedirectTo 10、RemoveRequestHeader 11、RemoveResponseHeader 12、RemoveRequestParameter 13、RewritePath  14、RewriteResponseHeader  15、S

    2024年02月01日
    浏览(56)
  • SpringCloudGateway--过滤器(自定义filter)

    目录 一、概览  二、全局过滤器GlobalFilter 三、通过GatewayFilter实现 四、继承AbstractGatewayFilterFactory        当使用Spring Cloud Gateway构建API网关时,可以利用Spring Cloud Gateway提供的内置过滤器(filter)来实现对请求的处理和响应的处理。过滤器可以在请求被路由之前或之后被执

    2024年02月06日
    浏览(38)
  • SpringCloudGateway使用Skywalking时日志打印traceId

    Skywalking oap 与 agent部署 https://blog.csdn.net/kismet2399/article/details/131560171 spring-cloud-starter-gateway:3.1.4 Skywalking Agent:8.14.0 SpringCloudGateway集成Skywalking后无法打印traceId 目前没有找到logback的解决方式,所以日志打印改用log4j2 mvn添加配置 让日志获取到traceId 插件添加 将skywalking-agent下opt

    2024年02月13日
    浏览(40)
  • 记录SpringCloudGateway无法完成转发Websocket的问题

    使用SpringCloudGateway作为网关转发Websocket链接到微服务。 SpringCloudGateway无法完成Websocket的转发,表现为无法链接。 我遇到的问题具体有两个原因导致。 跨域问题 我其实已经配置了,但是少加了一个s,allowedOrigins写成了allowedOrigin 花了我八个小时看源码 自闭 因为SpringGateway有一

    2023年04月27日
    浏览(23)
  • springCloudGateway+Nacos注册与转发Netty+WebSocket

    Netty+WebSocket是一开始单体应用中与前端交互使用的,最近开始搞Cloud想着移植过来使用 具体官方描述本文就不体现了 直接开始实现 以及解决过程中遇到的问题 1.首先编写netty端代码,由于是微服务模式就直接新建一个项目          1.1 由于Netty 需要额外启动所以配置一个

    2024年03月16日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包