【第17章】Spring Cloud之Gateway服务调用

news2024/9/25 5:24:13

文章目录

  • 前言
  • 一、用户服务
  • 二、网关服务
    • 1. 负载均衡
    • 2. 服务调用
    • 3. 登录拦截器
  • 三、单元测试
    • 1. 启动服务
    • 2. 用户不存在
    • 3. 正常登录
  • 总结


前言

在上一章我们使用JWT简单完成了用户认证,【第16章】Spring Cloud之Gateway全局过滤器(安全认证),上一章内容已经太多了,这里单独抽一章出来做个优化,前面的全局过滤器只针对登录接口的用户名密码做了简单校验,这里我们增加网关服务对用户服务的调用,参数检验完成之后我们调用用户服务的用户是否存在接口做判断,
用户存在,则继续执行登录接口,不存在则返回错误信息。


一、用户服务

package org.example.user.controller;

import org.example.common.model.Result;
import org.example.common.util.JwtUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Create by zjg on 2024/7/21
 */
@RestController
@RequestMapping("/user/")
public class UserController {
    List<String> users = List.of("admin");
    @RequestMapping("exist")
    public Boolean exist(@RequestParam("username") String username){
        boolean exist=false;
        if(users.contains(username)){
           exist=true;
        }
        return exist;
    }
    @RequestMapping("login")
    public Result<String> login(@RequestParam("username") String username, @RequestParam("password") String password){
        String message="用户名/密码不正确";
        String admin="admin";
        if(admin.equals(username)&&admin.equals(password)){
            Map<String, Object> claims=new HashMap<>();
            claims.put("username",username);
            return new Result<>(HttpStatus.OK.value(), "请求成功",JwtUtils.create(claims));
        }
        return Result.error(HttpStatus.UNAUTHORIZED.value(), message);
    }
}

二、网关服务

登录之前获取用户是否存在

1. 负载均衡

package org.example.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;

/**
 * Create by zjg on 2024/7/21
 */
@LoadBalancerClients({
        @LoadBalancerClient("user-service")
})
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

2. 服务调用

package org.example.gateway.client;

import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

/**
 * Create by zjg on 2024/7/30
 */
@Service
public class UserService {
    @Resource
    private WebClient.Builder webClientBuilder;

    public Mono<Boolean> exist(String username) {
        return webClientBuilder.build().get()
                .uri("http://user-service/user/exist?username=" + username).retrieve()
                .bodyToMono(Boolean.class);
    }
}

3. 登录拦截器

package org.example.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.common.model.Result;
import org.example.common.util.JwtUtils;
import org.example.gateway.client.UserService;
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.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.concurrent.ExecutionException;

/**
 * Create by zjg on 2024/7/31
 */
@Component
public class LoginGlobalFilter implements GlobalFilter, Ordered {
    @Autowired
    UserService userService;
    ObjectMapper objectMapper = new ObjectMapper();
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String uri = request.getURI().getPath();
        if(uri.equals("/user/login")||uri.equals("/user/login/")){
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            if(queryParams.containsKey("username")&&queryParams.containsKey("password")){
                Mono<Boolean> mono = userService.exist(queryParams.getFirst("username"));
                try {
                    Boolean exist = mono.doOnSuccess((e)->{}).toFuture().get();
                    if (Boolean.FALSE.equals(exist)) {
                        response.setStatusCode(HttpStatus.BAD_REQUEST);
                        Result result = Result.error(HttpStatus.BAD_REQUEST.value(), "登录失败", "用户名不存在!");
                        return write(response, result);
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (ExecutionException e) {
                    throw new RuntimeException(e);
                }
                return chain.filter(exchange);
            }else {
                response.setStatusCode(HttpStatus.BAD_REQUEST);
                Result result = Result.error(HttpStatus.BAD_REQUEST.value(), "登录失败", "用户名和密码不能为空!");
                return write(response,result);
            }
        }
        HttpHeaders headers = request.getHeaders();
        String authorization = headers.getFirst("Authorization");
        if(Boolean.FALSE.equals(StringUtils.hasText(authorization))||Boolean.FALSE.equals(JwtUtils.verify(authorization.startsWith("Bearer")?authorization.substring(authorization.indexOf("Bearer")+7):authorization))){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            Result result = Result.error(HttpStatus.UNAUTHORIZED.value(), "认证失败", "token验证失败,请重新获取token后重试!");
            return write(response,result);
        }
        request = exchange.getRequest().mutate()
        						.headers(httpHeaders -> httpHeaders.add("Source-Mark", "Z2F0ZXdheQ==")).build();
        return chain.filter(exchange.mutate().request(request).build());
    }
    public Mono<Void> write(ServerHttpResponse response, Result result) {
        try {
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return response.writeWith(Flux.just(response.bufferFactory().wrap(objectMapper.writeValueAsBytes(result))));
        } catch (JsonProcessingException e) {
            return response.setComplete();
        }
    }
    @Override
    public int getOrder() {
        return -1;
    }
}

三、单元测试

1. 启动服务

在这里插入图片描述

2. 用户不存在

在这里插入图片描述

3. 正常登录

在这里插入图片描述


总结

回到顶部

到这里我们就完成了在网关服务中对其他服务的调用和处理,不要走开,后面的内容更精彩!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1989244.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Node.js是什么?如何安装

目录 一、前言 1、JavaScript语言-----前端开发 2、JavaScript语言-----后端开发 总结&#xff1a;如果我们写了一段 js 代码&#xff0c;把他放到浏览器中执行&#xff0c;是在做前端开发&#xff1b;如果放在Node.js下运行&#xff0c;是在做后端开发。 二、安装 1、打开…

GHOST重装系统后的分区失踪:数据恢复实战指南

一、引言&#xff1a;GHOST重装引发的数据隐忧 在计算机维护的众多手段中&#xff0c;GHOST重装系统以其高效、便捷的特点深受用户喜爱。然而&#xff0c;这一过程往往伴随着风险&#xff0c;其中之一便是分区丢失的隐患。当GHOST重装操作不当或遭遇意外情况时&#xff0c;原本…

制作喇叭接口拓展

今天发现音响只有两个音频输出口&#xff0c;而喇叭有三个&#xff0c;就想着改装成可以装三个&#xff0c;电脑桌上一个&#xff0c;脚底下两个&#xff0c;从抽屉里翻出来了一个电视上拆下来的三色莲花口&#xff0c;它本来是一个视频输入&#xff0c;两个音频输入&#xff0…

硬币计数器——Arduino

硬币计数器——Arduino 硬币计数盒模型计数传感器硬币计数盒接线计数器程序 硬币计数盒模型 计数传感器 硬币计数盒接线 计数器程序 // 包含TM1637库&#xff0c;这是一个用于驱动TM1637数码管的模块 #include <TM1637.h>// 使用volatile关键字声明布尔变量jishu&#x…

Redis远程字典服务器(1)—— 初识Redis

目录 一&#xff0c;关于Redis 二&#xff0c;Redis特性介绍 2.1 In-memory data structures&#xff08;在内存中存储数据&#xff09; 2.2 Programmablilty&#xff08;编程能力&#xff09; 2.3 Extensibility&#xff08;扩展能力&#xff09; 2.4 Persistence&#…

食家巷小程序:传统面点与平凉特产的美味盛宴

在美食的世界里&#xff0c;总有一些角落等待着我们去探索&#xff0c;而食家巷小程序就是这样一个为您开启美食宝藏的钥匙。 一、传统面点&#xff0c;传承千年的美味 食家巷小程序为您呈现了种类丰富的传统面点&#xff0c;每一款都蕴含着深厚的历史和文化底蕴。 平凉锅盔&…

x-cmd mod | x jina - 为 jina.ai 打造的命令行工具,提供获取网页内容、生成向量数据等等

目录 简介主要特点子命令例子 简介 Jina.ai 是一家专注于大型语言模型和媒体处理公司。基于 jina.ai 公司的接口&#xff0c;jina 模块主要提供了以下功能&#xff1a; 网页内容获取生成文本向量相关信息检索排序检索 主要特点 通过 jina模块的 Reader 功能&#xff0c;我们…

QT(2.0)

1.常用控件的介绍 1.1 TextEdit QTextEdit表示多行输入框&#xff0c;也是一个富文本&markdown编辑器&#xff0c;并且能在内容超出编辑框范围时自动提供滚动条。 核心属性 属性 说明 markdown 输入框内持有的内容&#xff0c;支持markdown格式&#xff0c;能够自动的…

[Leetcode 215][Medium]-数组中的第K个最大元素-快排/小根堆/堆排序

一、题目描述 原题地址 二、整体思路 &#xff08;1&#xff09;快排 对于SELECT K问题&#xff0c;可以通过三路快排解决&#xff0c;快排可以把一个元素放至按升序排序的数组正确的位置&#xff0c;左边为小于该元素的元素集合&#xff0c;右边为大于该元素的元素集合。 三…

朋克养生,现代男人为何对生可乐泡枸杞情有独钟

在当今快节奏、高压力的社会环境中&#xff0c;朋克养生这一独特的养生方式悄然兴起&#xff0c;尤其在年轻男性群体中备受青睐。其中&#xff0c;生可乐泡枸杞这一不乏创意的养生方法&#xff0c;更是成为不少现代男人追求健康与乐趣并存的象征。朋克养生不仅仅是一种外在的行…

lvs的防火墙标记解决轮询调度问题

错误示范 ipvsadm -A -t 192.168.0.200:80 -s rr ipvsadm -a -t 192.168.0.200:80 -r 192.168.0.10:80 -g ipvsadm -a -t 192.168.0.200:80 -r 192.168.0.20:80 -g ipvsadm -A -t 192.168.0.200:443 -s rr ipvsadm -a -t 192.168.0.200:443 -r 192.168.0.10:80 -g ipvsadm -a …

CVE-2024-39877:Apache Airflow 任意代码执行

Apache Airflow 是一个开源平台&#xff0c;用于以编程方式编写、调度和监控工作流。虽然它提供了管理复杂工作流的强大功能&#xff0c;但它也存在安全漏洞。一个值得注意的漏洞 CVE-2024-39877 是 DAG&#xff08;有向无环图&#xff09;代码执行漏洞。这允许经过身份验证的 …

游戏ttf字体瘦身脚本

游戏中通常会用到某种特定字体&#xff0c;而某些字体动则10M&#xff0c;对某些游戏(尤其是小游戏)来讲是无法忍受的&#xff0c;此文章主要讲述上个项目中制作的字体裁剪脚本 工具git地址 配置信息(config.json) { // 文本内容(可能为多语言表导出的内容)"txtFile&qu…

常用API(三)

对于常见API的学习&#xff0c;主要学习了关于时间和日期的传统和新增APi 目录 1.Math 2.System 3.Runtime 4.日期和时间 &#xff08;1&#xff09;JDK8前传统时间API [1] Date [2] SimpledateFormat [3]Calendar &#xff08;2&#xff09;JDK8后新增时间API [1]代替…

JeecgBoot低代码平台简单记录

BasicModal弹窗 Usage 由于弹窗内代码一般作为单文件组件存在&#xff0c;也推荐这样做&#xff0c;所以示例都为单文件组件形式 注意v-bind"$attrs"记得写&#xff0c;用于将弹窗组件的attribute传入BasicModal组件 attribute&#xff1a;是属性的意思&#xff0c;…

嵌入式学习之线程和同步互斥机制

一. 线程的概念 进程的上下文切换&#xff1a; a.上下文&#xff1a;运行一个进程所需要的所有资源。 b.上下文切换&#xff1a;替换原有内容&#xff0c;从访问进程A的所有资源切换到访问进程B的所有资源&#xff0c;是一个耗时操作。 c.为了提高系统性能&#xff0c;引入了一…

HCIP | 实验一

实验内容 配置 1.合理规划IP地址&#xff0c;启用OSPF单区域 R1 R2 R3 R4 R5 R6 2.R1-R2之间启用PPP的pap单向认证 R1 R2 3.R2-R3之间启用PPP的chap双向认证 R2 R3 4.R3-R5-R6之间使用MGRE&#xff0c;R3为hub端&#xff0c;R5&#xff0c;R6为spoke端&#xff0c;要求MGRE…

DS1302实时时钟(51单片机)

一、DS1302时钟 1.DS1302时钟介绍 2.芯片使用 使用芯片时首先要通过数据手册知道芯片功能&#xff0c;根据芯片功能应用。 3.实现DS1302功能 通过对配置寄存器使用DS1302的读写功能 二、实现DS1302读写 1.模块化编程框架 首先对DS1302端口重新定义&#xff08;换端口名字…

汽车电子推拉力测试机测试流程的详细步骤

在现代汽车技术迅猛发展的今天&#xff0c;汽车电子产品的可靠性已成为确保车辆性能和乘客安全的关键因素。标准下的键合线剪切试验&#xff0c;作为评估这些产品中关键连接点强度的一项测试&#xff0c;扮演着至关重要的角色。本文旨在深入探讨这一测试的重要性、实施流程及其…

Java学习Day20

Vue学习 nodejs的安装与环境配置 1.直接去官网下载合适版本的nodejs( https://nodejs.org/zh-cn/download/prebuilt-installer) 2.解压下载的安装包&#xff0c;将文件路径配置到系统变量的path中&#xff0c;然后确认后退出。可以使用终端来查看安装的nodejs版本。使用winR…