java脚手架系列9-统一权限认证gateway

之所以想写这一系列,是因为之前工作过程中有几次项目是从零开始搭建的,而且项目涉及的内容还不少。在这过程中,遇到了很多棘手的非业务问题,在不断实践过程中慢慢积累出一些基本的实践经验,认为这些与业务无关的基本的实践经验其实可以复刻到其它项目上,在行业内可能称为脚手架,因此决定将此java基础脚手架的搭建总结下来,分享给大家使用。

注意由于框架不同版本改造会有些使用的不同,因此本次系列中主要使用基本框架是 spring-boo-2.3.12.RELEASE和spring-cloud.-Hoxton.SR12,所有代码都在commonFramework项目上:https://github.com/forever1986/commonFramework/tree/master

目录

  • [1 gateway](#1 gateway)
  • [2 动态加载路由表](#2 动态加载路由表)
  • [3 鉴权功能](#3 鉴权功能)

1 gateway

之所以将gateway放在这里讲,是因为承接上一篇的OAuth2,其中gateway有一个重要的功能就是鉴权,可以结合OAuth2进行鉴权。

在我们使用微服务架构的时候,免不了需要一个网关,而对于java项目来说,gateway是一个很好的选择。一般实践中,gateway有以下功能:

  • 路由转发:根据请求的特定条件(如 URL路径、请求参数、请求头等)来将请求转发到后端的多个服务,并支持动态路由配置
  • 过滤功能:提供了一套过滤器机制,允许开发人员对请求进行修改和验证,以及应用各种策略,如认证、安全、监控/指标、限流、日志、请求转发/重试等
  • 容错处理:集成断路器(Hystrix),为微服务网关提供容错处理的功能
  • 服务发现:与服务注册中心集成,动态从服务注册中心获取服务信息并进行路由
  • 请求转发:进行请求的协议转换,比如将 HTTP 请求转换成 WebSocket 请求
  • 请求限流:支持通过配置限流规则,对请求进行限流,防止恶意请求或异常情况下的流量冲击

下面以动态读取路由配置+过滤功能,实现gateway的路由转发可以动态加载,以及实现鉴权功能

2 动态加载路由表

参考gateway子模块

在实际应用中,我们不可能将路由配置放入项目的yaml配置文件中,这样修改路由时,都需要重新启动或者发布。这时候,配置中心就起到动态配置路由转发的存储功能,同时gateway也提供RouteDefinitionRepository可进行路由表动态加载。

原理:将路由配置文件放入nacos中,gateway子模块通过实现RouteDefinitionRepository,监听nacos的路由配置文件,实现动态更新路由配置

1)创建gateway子模块,引入以下依赖:

yaml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--监听配置文件的更新 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--获取配置文件 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2)配置bootstrap.yml,要配置服务发现和配置中心的信息

yaml 复制代码
server:
  port: 9991
spring:
  application:
    name: cloud-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos
      config:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos
        file-extension: yaml
        group: DEFAULT_GROUP
        prefix: ${spring.application.name}

3)实现NacosRouteDefinitionRepository(实现RouteDefinitionRepository),用于自动监听配置文件并刷新路由表

java 复制代码
package com.demo.route.config;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

/**
 * nacos路由数据源
 */
@Component
public class NacosRouteDefinitionRepository implements RouteDefinitionRepository {

    private static final Logger logger = LoggerFactory.getLogger(NacosRouteDefinitionRepository.class);

    private static final String SCG_DATA_ID = "cloud-gateway-service";
    private static final String SCG_GROUP_ID = "DEFAULT_GROUP";

    private final ApplicationEventPublisher publisher;

    private final NacosConfigManager nacosConfigManager;

    public NacosRouteDefinitionRepository(ApplicationEventPublisher publisher, NacosConfigManager nacosConfigManager) {
        this.publisher = publisher;
        this.nacosConfigManager = nacosConfigManager;
        addListener();
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        List<RouteDefinition> routeDefinitionList = new ArrayList<>(0);
        try {
            String configContent = nacosConfigManager.getConfigService().getConfig(SCG_DATA_ID, SCG_GROUP_ID, 5000);

            if (StringUtils.isNotEmpty(configContent)) {
                routeDefinitionList = JSON.parseArray(configContent, RouteDefinition.class);
            }
        } catch (NacosException e) {
            logger.error("从Nacos加载配置的动态路由信息异常", e);
        }
        return Flux.fromIterable(routeDefinitionList);
    }

    /**
     * 添加Nacos监听
     */
    private void addListener() {
        try {
            nacosConfigManager.getConfigService()
                    .addListener(SCG_DATA_ID, SCG_GROUP_ID, new Listener() {

                        @Override
                        public Executor getExecutor() {
                            return null;
                        }

                        @Override
                        public void receiveConfigInfo(String configInfo) {
                            publisher.publishEvent(new RefreshRoutesEvent(this));
                        }
                    });
        } catch (NacosException e) {
            logger.error("添加Nacos监听异常", e);
        }
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return null;
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return null;
    }
}

4)设置GatewayApplication设置@EnableDiscoveryClient注册到nacos

5)在nacos的public命名空间和DEFAULT_GROUP分组中,配置nacos的路由配置名为:cloud-gateway-service的json文件

json 复制代码
[
  {
    "id": "baidu",
    "order": 0,
    "predicates": [{
      "args": {
        "pattern": "/baidu/**"
      },
      "name": "Path"
    }],
    "uri": "http://www.baidu.com",
    "filters": [{
      "args": {
        "_genkey_0": "2"
      },
      "name": "StripPrefix"
    }]
  },
  {
    "id": "manager",
    "predicates": [{
      "args": {
        "pattern": "/manager/**"
      },
      "name": "Path"
    }],
    "uri": "http://localhost:9981",
    "filters": [{
      "args": {
        "_genkey_0": "1"
      },
      "name": "StripPrefix"
    }]
  }
]

6)通过请求以下访问,可以得到转发;同时你可以在nacos上面修改cloud-gateway-service的json文件路由,可以测试动态刷新

http://localhost:8891/baidu/aa

http://localhost:8891/business/business

3 鉴权功能

参考gateway子模块

网关的过滤功能:提供了一套过滤器机制,允许开发人员对请求进行修改和验证,以及应用各种策略,如认证、安全、监控/指标、限流、日志、请求转发/重试等。我们这里的鉴权就是使用过滤功能。在配置鉴权功能之前,我们有必要了解一下内容:

  • 鉴权原理:在系列8中我们讲过security、JWT、oauth的内容,这里利用auth-authentication子模块生成token,我们知道验证token的有效性2种方法,一种是使用访问auth-authentication子模块的access_token接口验证,一种是通过RSA非对称加密验证。这里的token采用RSA非对称加密验证(这是因为本身路由访问非常频繁的服务,不适合频繁访问access_token接口)。
  • gateway过滤:在gateway中有3个比较重要的过滤:ReactiveAuthenticationManager->ReactiveAuthorizationManager->Gateway Filters。

1)其中ReactiveAuthenticationManager用于封装JWT为OAuth2Authentication并判断Token的有效性;

2)ReactiveAuthorizationManager用于基于URL的鉴权;

3)Gateway Filters是网关的过滤流程。

下面我们通过配置JWT+RSA的解密对token进行认证,另外通过实现ReactiveAuthorizationManager对其进行鉴权。

1)引入以下依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

3)编写ResourceServerManager(实现ReactiveAuthorizationManager),实现其check接口,获得用户信息,对权限进行判断(这里只是演示一下,没有实际权限判断代码,实际业务中可以加入一些权限判断,比如路由权限、RABC等业务判断功能)

4)编写ResourceServerConfig,通过注入securityWebFilterChain类,将JWT认证和ResourceServerManager都设置到过滤链中

5)在ResourceServerConfig中,通过注入jwtAuthenticationConverter,实现JWT+RSA的公钥,对token进行认证

6)编写SecurityGlobalFilter(实现GlobalFilter,和Ordered),做最后处理(当然如果没有此需求,也可以不做处理)

7)通过请求以下访问,可以得到转发(前提是你需要在header中增加token)

http://localhost:8891/baidu/aa

http://localhost:8891/business/business

相关推荐
坐吃山猪4 小时前
SpringBoot01-配置文件
java·开发语言
我叫汪枫4 小时前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao5 小时前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区6 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT7 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy7 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss8 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续9 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0449 小时前
ReAct模式解读
java·ai