微服务:gateway的使用,和解决跨域问题,用户认证与网关整合

2023-11-11

1、网关介绍

API网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:

(1)客户端会多次请求不同的微服务,增加了客户端的复杂性。

(2)存在跨域请求,在一定场景下处理相对复杂。

(3)认证复杂,每个服务都需要独立认证。

(4)难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。

(5)某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难。

以上这些问题可以借助 API 网关解决。API 网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过API 网关这一层。也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由 API 网关来做,这样既提高业务灵活性又不缺安全性

2、Spring Cloud Gateway介绍

Spring cloud gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式,Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且还基于Filer链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等

 3、搭建server-gateway模块

服务网关

3.1 搭建server-gateway

搭建过程如common模块

3.2 修改配置pom.xml

修改pom.xml

<dependencies>
    <dependency>
        <groupId>com.atguigu.yygh</groupId>
        <artifactId>common-util</artifactId>
        <version>1.0</version>
    </dependency>

    <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>
</dependencies>

3.3 在resources下添加配置文件

1、application.properties

# 服务端口
server.port=80
# 服务名
spring.application.name=service-gateway

# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

#使用服务发现路由
spring.cloud.gateway.discovery.locator.enabled=true

#设置路由id
spring.cloud.gateway.routes[0].id=service-hosp
#设置路由的uri
spring.cloud.gateway.routes[0].uri=lb://service-hosp
#设置路由断言,代理servicerId为auth-service的/auth/路径
spring.cloud.gateway.routes[0].predicates= Path=/*/hosp/**

#设置路由id
spring.cloud.gateway.routes[1].id=service-cmn
#设置路由的uri
spring.cloud.gateway.routes[1].uri=lb://service-cmn
#设置路由断言,代理servicerId为auth-service的/auth/路径
spring.cloud.gateway.routes[1].predicates= Path=/*/cmn/**

3.4添加启动类

package com.atguigu.yygh;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ServerGatewayApplication {

public static void main(String[] args) {
      SpringApplication.run(ServerGatewayApplication.class, args);
   }
}

3.5 跨域处理

跨域:浏览器对于javascript的同源策略的限制 。

以下情况都属于跨域:

跨域原因说明

示例

域名不同

www.jd.com 与 www.taobao.com

域名相同,端口不同

www.jd.com:8080 与 www.jd.com:8081

二级域名不同

item.jd.com 与 miaosha.jd.com

如果域名和端口都相同,但是请求路径不同,不属于跨域,如:

www.jd.com/item

www.jd.com/goods

http和https也属于跨域

而我们刚才是从localhost:1000去访问localhost:8888,这属于端口不同,跨域了。

3.5.1 为什么有跨域问题?

跨域不一定都会有跨域问题。

因为跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。

因此:跨域问题 是针对ajax的一种限制。

但是这却给我们的开发带来了不便,而且在实际生产环境中,肯定会有很多台服务器之间交互,地址和端口都可能不同,怎么办?

3.5.2解决跨域问题

全局配置类实现

CorsConfig

@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}

重点:3.6服务调整

目前我们已经在网关做了跨域处理,那么service服务就不需要再做跨域处理了,将之前在controller类上添加过@CrossOrigin标签的去掉,防止程序异常

  • 用户认证与网关整合

思路:

  1. 所有请求都会经过服务网关,服务网关对外暴露服务,在网关进行统一用户认证;
  2. 既然要在网关进行用户认证,网关得知道对哪些url进行认证,所以我们得对ur制定规则
  3. Api接口异步请求的,我们采取url规则匹配,如:/api/**/auth/**,如凡是满足该规则的都必须用户认证

1、调整server-gateway模块

1.1在服务网关添加fillter

package com.atguigu.yygh.gateway.filter;

import com.alibaba.excel.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.hosp.help.JwtHelper;
import com.atguigu.yygh.hosp.result.Result;
import com.atguigu.yygh.hosp.result.ResultCodeEnum;
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.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * <p>
 * 全局Filter,统一处理会员登录与外部不允许访问的服务,验证是否携带token
 * </p>
 *
 * @author qy
 * @since 2019-11-21
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    //
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        System.out.println("==="+path);

        //直接访问内部服务接口,不允许外部访问,match()判断路径是否匹配
        if(antPathMatcher.match("/**/inner/**", path)) {
            ServerHttpResponse response = exchange.getResponse();
            return out(response, ResultCodeEnum.PERMISSION);
        }

        Long userId = this.getUserId(request);
        //api接口,异步请求,校验用户必须登录,如果userid为空就没事登录否则放行
        if(antPathMatcher.match("/api/**/auth/**", path)) {
            if(StringUtils.isEmpty(userId)) {
                ServerHttpResponse response = exchange.getResponse();
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }
        return chain.filter(exchange);
    }

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

    /**
     * api接口鉴权失败返回数据
     * @param response
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        Result result = Result.build(null, resultCodeEnum);
        byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        //指定编码,否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * 获取当前登录用户id
     * @param request
     * @return
     */
    //通过token获取userid
    private Long getUserId(ServerHttpRequest request) {
        String token = "";
        List<String> tokenList = request.getHeaders().get("token");
        if(null  != tokenList) {
            token = tokenList.get(0);
        }
        if(!StringUtils.isEmpty(token)) {
            return JwtHelper.getUserId(token);
        }
        return null;
    }
}

请求服务器端接口时我们默认带上token,需要登录的接口如果token没有或者token过期,服务器端会返回208状态,然后发送登录事件打开登录弹出层登录

修改前端utils/request.js文件

import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import cookie from 'js-cookie'

// 创建axios实例
const service = axios.create({
    baseURL: 'http://localhost',
    timeout: 15000 // 请求超时时间
})
// http request 拦截器
service.interceptors.request.use(
    config => {
    // token 先不处理,后续使用时在完善
    //判断cookie是否有token值
    if(cookie.get('token')) {
        //token值放到cookie里面
        config.headers['token']=cookie.get('token')
    }
    return config
},
  err => {
    return Promise.reject(err)
})
// http response 拦截器
service.interceptors.response.use(
    response => {
        //状态码是208
        if(response.data.code === 208) {
            //弹出登录输入框
            loginEvent.$emit('loginDialogEvent')
            return
        } else {
            if (response.data.code !== 200) {
                Message({
                    message: response.data.message,
                    type: 'error',
                    duration: 5 * 1000
                })
                return Promise.reject(response.data)
            } else {
                return response.data
            }
        }
    },
    error => {
        return Promise.reject(error.response)
})
export default service

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

微服务:gateway的使用,和解决跨域问题,用户认证与网关整合 的相关文章

随机推荐

  • 检测鼠标位置是否有UI

    示例 using System using System Collections Generic using UnityEngine using UnityEngine EventSystems using UnityEngine UI p
  • 【编译原理】课程一:编译原理入门

    目录 1 为什么要学习编译原理 2 什么是编译原理 3 编译与计算机程序设计语言的关系 3 1 程序设计语言的转换方式 3 2 编译的转换过程 3 3 编译器在语言处理系统中的位置 3 4 编译系统的结构 3 4 1 词法分析 扫描 3 4
  • unity3D僵尸模型和场景带动作,unity游戏素材

    unity3D僵尸模型和场景带动作 详情如下动态图所示 资源链接在文末 点我下载资源 https download csdn net download weixin 43474701 55001671
  • IMX6ULL驱动学习--Linux自带LED驱动

    Linux自带LED驱动 Linux内核LED驱动 内核使能LED驱动 LED驱动源码分析 设备树节点编写 设备树LED节点 设备操作 Linux内核LED驱动 Linux 内核集成了LED驱动 采用platform框架 我们只需要在编译内
  • Centos7-安装keepalived

    1 到keepalived官网下载 Keepalived for Linux 2 使用xftp上传到 usr local packages目录下 3 解压上传的tar gz压缩文件 cd usr local packages tar zxv
  • 西安电子科技大学计算智能导论公茂果老师课程所有PPT

    西安电子科技大学计算智能导论公茂果老师课程所有PPT http pan baidu com s 1hqst8pe 随着信息技术的发展 计算的观念越来越显示其在各个领域的威力 从计算的角度审视世界 认为从物理世界 生命过程直到人类智能都是可计
  • sqlserver ssms 安装失败拒绝访问(0x80070005)

    今天安装了SQL Server Management Studio 安装的时候提示 安装失败 拒绝访问 0x80070005 果断将电脑重启 重启之后重新安装还是不行 解决方法 我们将杀毒软件关闭 我的是360 然后我们再安装一下SQL S
  • linux 基础(一)

    1 Linux常见发行版 Linux发行版有三大主流分支 Debian Slackware Redhat 包含的主要发行版分别如下 Debian Ubuntu Linux mint Knopix Slackware S u S E SLES
  • 机器学习之单变量线性回归

    1 线性回归基础概念 回归模型 regression model 数据集 包含feature 输入变量 和与之对应的target 输出变量 训练集 training set 输入数据 x feature or input feature 输
  • esp8266一键安装arduino_ESP8266如何获取B站粉丝数、关注数和播放量?

    硬件 ESP8266 12板 0 96 IIC驱动OLED一个 杜邦线4根 1 获取API接口 打开B站个人中心 按下F12 刷新页面 搜索这个词 https api bilibili com x relation stat vmid 40
  • 进程、线程、协程

    一 名词概念 1 什么是进程 what is process 进程是操作系统提供的抽象概念 是系统进行资源分配和调度的基本单位 是操作系统结构的基础 程序是指令 数据及其组织形式的描述 进程是程序的实体 程序本身是没有生命周期的 它只是存在
  • 系统稳定性方法论 - 降发生

    上一篇 lt 系统稳定性方法论 序篇 gt 中提到了系统稳定性的4大抓手 今天就先说说其中的第一个 降发生 何为 降发生 降发生指的是 从设计阶段开始 开发阶段 测试阶段 上线前的准备阶段 上线阶段到最后上线后的回归测试阶段 在这整个过程中
  • 微信云托管-填坑之旅

    微信云开发开始收费了 一个月20块钱 贼贵 用不起 而按用量收费 资源还能复用的兄弟产品微信云托管还像还行 所以就有了一场填坑旅行 windows10下安装docker之坑 在本地开发 需要使用docker 但是下载 安装docker后 大
  • less查看日志常用命令

    在Linux系统中 使用less命令可以查看日志文件 下面是一些常用的less命令 less filename 查看指定文件的内容 pattern 搜索包含指定模式的文本 可以使用向上或向下箭头浏览搜索结果 n 在搜索结果中跳转到下一个匹配
  • 组合数--深度优先算法(DFS)的简单实现

    找出从自然数 1 2 n 0
  • Github上堪称最全的面试题库(Java岗)到底有多香

    前言 最近金三银四想必大家或多或少都有去面试 面试前也都会有刷刷题的习惯 不过网上有太多的Java面试题库了 大部分都潦潦草草 没有知识储备的小伙伴一般很难看下去 看完后也不会留下印象 最近也是有粉丝私信我说求一份 图文并茂 易于理解 代码
  • linux终端基本操作命令

    Linux系统一切皆为文件 所有命令都是小写 使用命令的格式 命令名 操作内容 1 man 其他命令名 命令介绍 主要看name部分 2 ls ls 文件夹名或者说路径 列出指定路径下的内容 ls a 列出当前目录下所有内容 包括点开头的和
  • QModbusServer

    目录 数据 1 setData 重载一 重载二 2 writeData 3 Data 重载一 重载二 4 readData 数据 1 setData 重载一 将数据写入Modbus服务器 Modbus服务器有四个表 A表 每个表有一个唯一的
  • 5G/NR 学习笔记: 波束赋形 / beam 管理

    What beam 是对于整列天线电磁波传播的一种样式 一个天线的时候 电磁波的辐射方向是360度传播的 但是一个天线阵列可以实现电磁波单方向传播 天线个数越多 电磁波传播方向越集中 https www rcrwireless com 20
  • 微服务:gateway的使用,和解决跨域问题,用户认证与网关整合

    1 网关介绍 API网关出现的原因是微服务架构的出现 不同的微服务一般会有不同的网络地址 而外部客户端可能需要调用多个服务的接口才能完成一个业务需求 如果让客户端直接与各个微服务通信 会有以下的问题 1 客户端会多次请求不同的微服务 增加了