SpringCloud网关Gateway认证鉴权【SpringCloud系列7】

这篇具有很好参考价值的文章主要介绍了SpringCloud网关Gateway认证鉴权【SpringCloud系列7】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见。
程序员每天的CV 与 板砖,也要知其所以然,本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发

本文章是系列文章中的一篇

  • 1、SpringCloud 项目基础工程搭建 【SpringCloud系列1】
  • 2、SpringCloud 集成Nacos注册中心 【SpringCloud系列2】
  • 3、SpringCloud Feign远程调用 【SpringCloud系列3】
  • 4、SpringCloud Feign远程调用公共类抽取 【SpringCloud系列4】
  • 5、SpringCloud 整合Gateway服务网关 【SpringCloud系列5】
  • 6、SpringCloud 整合 Spring Security 认证鉴权【SpringCloud系列6】

SpringCloud网关Gateway认证鉴权【SpringCloud系列7】
本文章实现的是 Gateway 网关中的令牌校验功能 ,上图中所示用户所有的访问全部走网关,然后在网关每次都调用 auth-api 鉴权,当访问量足够大的时候,还是会有访问性能问题,所以优化如下:
SpringCloud网关Gateway认证鉴权【SpringCloud系列7】

1 网关Gateway 添加 security 与 oauth2 相关配置

这里添加的 security 与 oauth2 相关配置 ,是为了解密 auth-api 中生成的 access_token 令牌信息

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

然后添加安全拦截配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

/**
 * @description 安全配置类
 * @author 早起的年轻人
 */
 @EnableWebFluxSecurity
 @Configuration
 public class SecurityConfig {
  //安全拦截配置
  @Bean
  public SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) {

   return http.authorizeExchange()
           .pathMatchers("/**").permitAll()
           .anyExchange().authenticated()
           .and().csrf().disable().build();
  }
 }

配置 JwtAccessTokenConverter 所使用的密钥信息

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * @author 早起的年轻人
 * @version 1.0
 **/
@Configuration
public class TokenConfig {

    String SIGNING_KEY = "test_key";

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }

}

然后 网关配置文件中添加 auth-api 相关的路由

server:
  port: 10001
spring:
  application:
    name: '@project.name@'
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
    gateway:
      routes: # 网关路由配置
        - id: rewritepath_route
          uri: https://www.baidu.com/
          predicates:
            - Path=/search/**
          filters:
            - RewritePath=/search/(?<segment>.*), /$\{segment}

        - id: user-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://user-service # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
        - id: order-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://order-service
          predicates:
            - Path=/order/**

        - id: auth-api # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://auth-api
          predicates:
            - Path=/oauth/**
2 网关认证过虑器
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author 早起的年轻轻人
 * @version 1.0
 * @description 网关认证过虑器
 */
@Component
@Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered {


    //白名单
    private static List<String> whitelist = null;

    static {
        //加载白名单
        try (
                InputStream resourceAsStream = GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties");
        ) {
            Properties properties = new Properties();
            properties.load(resourceAsStream);
            Set<String> strings = properties.stringPropertyNames();
            whitelist= new ArrayList<>(strings);

        } catch (Exception e) {
            whitelist = new ArrayList<>();
            log.error("加载/security-whitelist.properties出错:{}",e.getMessage());
            e.printStackTrace();
        }


    }

    @Autowired
    private TokenStore tokenStore;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //请求的url
        String requestUrl = exchange.getRequest().getPath().value();
        AntPathMatcher pathMatcher = new AntPathMatcher();
        //白名单放行
        for (String url : whitelist) {
            if (pathMatcher.match(url, requestUrl)) {
                return chain.filter(exchange);
            }
        }
        //检查token是否存在
        String token = getToken(exchange);
        if (StringUtils.isBlank(token)) {
            return buildReturnMono("没有认证",exchange);
        }
        //判断是否是有效的token
        OAuth2AccessToken oAuth2AccessToken;
        try {
            oAuth2AccessToken = tokenStore.readAccessToken(token);

            boolean expired = oAuth2AccessToken.isExpired();
            if (expired) {
                return buildReturnMono("认证令牌已过期",exchange);
            }
            return chain.filter(exchange);
        } catch (InvalidTokenException e) {
            log.info("认证令牌无效: {}", token);
            return buildReturnMono("认证令牌无效",exchange);
        }

    }

    /**
     * 获取token
     */
    private String getToken(ServerWebExchange exchange) {
        String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (StringUtils.isBlank(tokenStr)) {
            return null;
        }
        return tokenStr;
    }




    private Mono<Void> buildReturnMono(String error, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        Map<String,Object> map = new HashMap<>();
        map.put("code",403);
        map.put("message",error);

        String jsonString = JSON.toJSONString(map);
        byte[] bits = jsonString.getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }


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

Spring Cloud Gateway 根据作用范围划分为 GatewayFilter 和 GlobalFilter

  • GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上。
  • GlobalFilter : 不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器
3 启动服务 测试

SpringCloud网关Gateway认证鉴权【SpringCloud系列7】
首先通过网关访问订单详情

http://localhost:10001/order/109

使用很久之前的一个token
SpringCloud网关Gateway认证鉴权【SpringCloud系列7】
然后再通过网关获取令牌
SpringCloud网关Gateway认证鉴权【SpringCloud系列7】
然后使用新获取到的令牌来访问订单详情
SpringCloud网关Gateway认证鉴权【SpringCloud系列7】

4 获取token中的用户信息

在网关中将token解析,获取登录 token 中对应的用户 userId , 在网关中的令牌校验过滤器 GatewayAuthFilter 中添加内容:

public class GatewayAuthFilter implements GlobalFilter, Ordered {


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        
        ... ... 
            //----------获取token中的用户令牌--------------------------------------------------------------------
            OAuth2Authentication authentication = tokenStore.readAuthentication(token);
            User authUser = (User) authentication.getPrincipal();
            //获取保存的用户令牌 我这里是一个JSON 
            String username = authUser.getUsername();
            //使用Fastjson 将json字符串转为map
            Map<String, Object> parse = JSON.parseObject(username, Map.class);
            //获取其中的 userId
            String userId = parse.get("userId").toString();

            ServerHttpRequest req = exchange.getRequest();
            HttpHeaders httpHeaders = req.getHeaders();
            ServerHttpRequest.Builder requestBuilder = req.mutate();
            // 先删除,后新增
            //requestBuilder.headers(k -> k.remove("要修改的header的key"));
            // requestBuilder.header("要修改的header的key", 处理完之后的header的值);

            // 或者直接修改,要求修改的变量为final
            requestBuilder.headers(k -> k.set("userId", userId));
            log.info("令牌解析成功:userId is {}",userId);

            ServerHttpRequest request = requestBuilder.build();
            exchange.mutate().request(request).build();

            return chain.filter(exchange);
        } catch (InvalidTokenException e) {
            log.info("认证令牌无效: {}", token);
            return buildReturnMono("认证令牌无效", exchange);
        }
    }
}

这里是获取了用户的 userId ,然后将userId添加到请求头中,比如在后续的 admin 管理后台的服务中,可以直接通过 @RequestHeader 获取
SpringCloud网关Gateway认证鉴权【SpringCloud系列7】

到此 网关中的鉴权功能开发完成。

本项目源码 https://gitee.com/android.long/spring-cloud-biglead/tree/master/biglead-api-07-auth
如果有兴趣可以关注一下公众号 biglead ,每周都会有 java、Flutter、小程序、js 、英语相关的内容分享文章来源地址https://www.toymoban.com/news/detail-483242.html

到了这里,关于SpringCloud网关Gateway认证鉴权【SpringCloud系列7】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • satoken+ gateway网关统一鉴权 初版

    本博客内容 参考了satoken官网实现,satoken官网地址: https://sa-token.cc/doc.html#/micro/gateway-auth jinyi-gateway 网关服务 jinyi-user-service 用户服务 2.1 jinyi-user-api 2.2 jinyi-user-client 2.3 jinyi-user-provider jinyi-common 通用服务,定义了一些统一返回类,全局常量(R等) 项目层级关系截图: 3.1jinyi-

    2023年04月20日
    浏览(37)
  • springcloud-gateway-2-鉴权

    目录 一、跨域安全设置 二、GlobalFilter实现全局的过滤与拦截。 三、GatewayFilter单个服务过滤器 1、原理-官方内置过滤器 2、自定义过滤器-TokenAuthGatewayFilterFactory 3、完善TokenAuthGatewayFilterFactory的功能 4、每一个服务编写一个或多个过滤器,实现鉴权的需要 四、总结 续前篇,介

    2024年02月03日
    浏览(43)
  • 【实现微服务集成satoken在网关gateway处统一鉴权】

    本文旨在使用开源轻量级 Java 权限认证框架sa-token+springcloud-gateway实现微服务在网关处统一鉴权。sa-token参考地址:https://sa-token.cc/doc.html#/ 项目按照业务分为三个板块,如图: api(也就是微服务中各种api接口,不涉及任何权限相关代码,只提供服务) auth(认证中心,实现登陆逻辑

    2024年02月10日
    浏览(41)
  • Spring Gateway + Oauth2 + Jwt网关统一鉴权

    之前文章里说过,分布式系统的鉴权有两种方式,一是在网关进行统一的鉴权操作,二是在各个微服务里单独鉴权。 第二种方式比较常见,代码网上也是很多。今天主要是说第一种方式。 重要前提:需要收集各个接口的uri路径和所需权限列表的对应关系,并存入缓存。 服务

    2024年02月03日
    浏览(45)
  • GateWay网关自定义过滤器实现token校验完成统一鉴权

    gateWay---API网关,也可以称为业务网关,主要服务于微服务的; (1)  三大组件 路由(Route)         构建网关的基本模块,由id(唯一标示)、目标URI、一组断言、一组过滤器组成,如果断言为true,则匹配该路由   断言(Predicate)          可以使用它匹配来自HTTP请求的任何

    2024年02月08日
    浏览(47)
  • SpringCloud搭建微服务之Gateway+Jwt实现统一鉴权

    在微服务项目中,需要对整个微服务系统进行权限校验,通常有两种方案,其一是每个微服务各自鉴权,其二是在网关统一鉴权,第二种方案只需要一次鉴权就行,避免了每个微服务重复鉴权的麻烦,本文以网关统一鉴权为例介绍如何搭建微服务鉴权项目。 本文案例中共有四

    2024年02月13日
    浏览(48)
  • SpringCloud-网关 Gateway

      官方地址:SpringCloud Gateway   网关统一了服务的入口,可以方便实现对众多服务接口进行管控,对访问服务的身份认证,防报文重放与防数据篡改,功能调用的业务鉴权,响应数据的脱敏,流量与并发控制,甚至基于API调用的计量或者计费等等。更通俗理解,网关可以

    2024年02月04日
    浏览(29)
  • 【SpringCloud-5】gateway网关

    网关是干啥用的就不用再说了。 sringcloud中的网关,第一代是zuul,但是性能比较差(1.x是阻塞式的,2.x是基于Netty的),然后有了第二代GateWay,基于Reactor模型 异步非阻塞。  springcloud网关就是一系列的filter,在请求到达真实服务的前后,进行拦截处理。   GateWay 核⼼逻辑:路

    2024年02月09日
    浏览(36)
  • SpringCloud.03.网关Gateway

    目录  网关Gateway的概念: 准备 使用 方式一 因为配置了网关所以可以直接通过gateway发送请求 方式二 修改配置前:http://localhost:8082/provider/run 方式三(动态路由) 导入配置类 Spring Cloud Gateway 是 Spring 官方基于 Spring5.0 、 SpringBoot2.0 和 Project Reactor 等技术开发的网 关 旨在为微服务

    2024年02月01日
    浏览(36)
  • SpringCloud网关——GateWay

    本专栏学习内容来自尚硅谷周阳老师的视频 有兴趣的小伙伴可以点击视频地址观看 SpringCloud Gateway 是 Spring Cloud 的一个全新项目,基于 Spring 5.0+Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 SpringCloud Gatewa

    2023年04月23日
    浏览(37)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包