【第16章】Spring Cloud之Gateway全局过滤器(安全认证)

news2024/11/24 8:46:25

文章目录

  • 前言
  • 一、公共模块
    • 1. 引入依赖
    • 2. 工具类
  • 二、用户服务
    • 1. 新建模块
    • 2. 引入依赖
    • 3. 启动类
    • 4. 基本配置
    • 5. 登录接口
  • 三、网关服务
    • 1. 引入依赖
    • 2. 应用配置
    • 3. 自定义过滤器
  • 四、单元测试
    • 1.介绍
    • 2. 登录接口
    • 3. 提供者接口
      • 3.1 无token
      • 3.2 有token
  • 总结


前言

我们已经接入了网关,所有的外部访问需要通过网关才能访问到我们的微服务,这一章我们在网关层进行统一的安全认证,保障服务安全和数据安全。

在前面的的【第3章】SpringBoot实战篇之登录接口(含JWT和拦截器) 已经实现了对用户身份的认证,思路是差不多的,我们这里通过网关层的过滤器来简单实现下。


一、公共模块

我们需要在在公共模块实现JWT以工具类的形式出现,方便其他模块的使用

1. 引入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.4.0</version>
</dependency>

2. 工具类

package org.example.common.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Date;
import java.util.Map;

public class JwtUtils {

    private static final String SECRET="167589447321";

    public JwtUtils() {
    }

    /**
     * 生成token
     * @param claims 用户信息
     * @return String
     */
    public static String create(Map<String, Object> claims) {
        return JWT.create()
                .withClaim("claims", claims)
                .withIssuer("auth0")
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24))
                .sign(Algorithm.HMAC256(SECRET));
    }

    /**
     * 验证token
     * @param token token
     * @return boolean
     */
    public static boolean verify(String token) {
        DecodedJWT decodedJWT;
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    // specify any specific claim validations
                    .withIssuer("auth0")
                    // reusable verifier instance
                    .build();

            decodedJWT = verifier.verify(token);
        } catch (JWTVerificationException exception){
            // Invalid signature/claims
            return false;
        }
        return true;
    }

    /**
     * 解析token数据
     * @param token token
     * @return Map
     */
    public static Map<String, Object> getClaims(String token) {
        return JWT.require(Algorithm.HMAC256(SECRET))
                .withIssuer("auth0")
                .build()
                .verify(token)
                .getClaim("claims")
                .asMap();
    }

}

二、用户服务

这里我们新增用户服务,用于管理用户信息和生成token信息

1. 新建模块

在这里插入图片描述

2. 引入依赖

<dependencies>
   <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
	<dependency>
       <groupId>org.example</groupId>
       <artifactId>base-common</artifactId>
       <version>1.0.0-SNAPSHOT</version>
   </dependency>
</dependencies>

3. 启动类

package org.example.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * Create by zjg on 2024/7/21
 */
@EnableDiscoveryClient
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

4. 基本配置

server:
  port: 9007
spring:
  application:
    name: user-service
  cloud:
    nacos:
      config:
        file-extension: yaml
        server-addr: ${NACOS_SERVER_ADDR}
        namespace: ${NACOS_NAMESPACE}
        username: ${NACOS_USERNAME}
        password: ${NACOS_PASSWORD}
        shared-configs[0]:
            data-id: base-discovery.yaml
            group: DEFAULT_GROUP
            refresh: true

5. 登录接口

package org.example.user.controller;

import org.example.common.util.JwtUtils;
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.Map;

/**
 * Create by zjg on 2024/7/21
 */
@RestController
@RequestMapping("/user/")
public class UserController {
    @RequestMapping("login")
    public 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 JwtUtils.create(claims);
        }
        return message;
    }
}

三、网关服务

1. 引入依赖

<dependency>
    <groupId>org.example</groupId>
    <artifactId>base-common</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

2. 应用配置

spring:
  cloud:
    gateway:
      routes:
      - id: provider-service
        uri: lb://provider-service
        predicates:
        - Path=/provider/**
        - BlackRemoteAddr=192.168.1.1/24,127.0.0.1,192.168.0.102
      - id: consumer-service
        uri: lb://consumer-service
        predicates:
        - Path=/consumer/**
        filters:
        - BlackList=/consumer/hello1,/consumer/hello2
      - id: user-service
        uri: lb://user-service
        predicates:
        - Path=/user/**

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.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.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;

/**
 * Create by zjg on 2024/7/21
 */
@Component
public class LoginGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String uri = request.getURI().getPath();
        ObjectMapper objectMapper = new ObjectMapper();
        if(uri.equals("/user/login")||uri.equals("/user/login/")){
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            if(queryParams.containsKey("username")&&queryParams.containsKey("password")){
                return chain.filter(exchange);
            }else {
                response.setStatusCode(HttpStatus.BAD_REQUEST);
                Result result = Result.error(HttpStatus.BAD_REQUEST.value(), "登录失败", "用户名和密码不能为空!");
                try {
                    return response.writeWith(Flux.just(response.bufferFactory().wrap(objectMapper.writeValueAsBytes(result))));
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        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后重试!");
            try {
                return response.writeWith(Flux.just(response.bufferFactory().wrap(objectMapper.writeValueAsBytes(result))));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
        request = exchange.getRequest().mutate()
        						.headers(httpHeaders -> httpHeaders.add("Source-Mark", "Z2F0ZXdheQ==")).build();
        return chain.filter(exchange.mutate().request(request).build());
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

四、单元测试

1.介绍

在这里插入图片描述

需要访问一个登录接口,获取token之后访问提供者的接口

大体思路:

  • 用户请求登录接口>网关>用户>公共模块
  • 用户请求提供者接口>网关>公共模块>提供者

2. 登录接口

在这里插入图片描述

3. 提供者接口

3.1 无token

在这里插入图片描述

3.2 有token

在这里插入图片描述
在这里插入图片描述


总结

回到顶部

网关层面已经实现了服务安全认证,进一步增加了我们微服务程序的安全性和稳定性。

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

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

相关文章

构造散列表以及处理冲突问题

目录 一. 散列表的基本概念 二. 散列表的冲突问题 三. 构造散列函数 四. 处理冲突 一. 散列表的基本概念 散列表简而言之就是一个根据一个对应关系&#xff08;这个对应关系也叫散列函数&#xff09;来存储关键码&#xff08;关键字&#xff09;的一个表。 例如&#xff0c;…

SSM电子商务系统-计算机毕业设计源码68470

基于SSM框架的电子商务系统的设计与实现 摘 要 随着电子商务的迅猛发展和计算机信息技术的全面跃升&#xff0c;网上购物系统由于其迎合了人们诉求和期望而渗入社会生活各个层面和角落。本文设计并实现了一个基于SSM框架的电子商务系统。该系统旨在为用户提供一个舒适且快捷的…

创建第一个Spring MVC项目

上篇文章&#xff0c;我们围绕什么是Spring MVC进行讲述了&#xff0c;这篇&#xff0c;我们将在IDEA创建我们的第一个Spring MVC项目。 创建我们的第一个Spring MVC项目 创建项目模块 如果你出现上面小图这样的&#xff0c;代表你就成功创建成功了一个Web项目&#xff0c;当…

商品购物网页的设计

系统名称&#xff1a; 基于TCP网络通信及数据库的网页查询系统 文档作者&#xff1a;清馨 创作时间&#xff1a;2024-8-3 最新修改时间&#xff1a;2024-8-6 最新版本号&#xff1a; 1.0 1.背景描述&#xff1a; 该系统为创建网络并发服务器&#xff0c;通过HTTP超文本网络…

【ML】 如何训练transform model, 模型训练实现细节

【ML】 如何训练transform model, 模型训练实现细节 1. transform 训练 原理2. transform 训练TIPS2.1 copy mechanism![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/69fb84a73d0240cc9042e17ae10bbef7.jpeg) 2. bean search束搜索的工作原理&#xff1a;束搜索的特…

部署DR模式集群

一、配置实验环境 每台主机的防火墙和SELinux都要关掉 systemctl stop firewalld setenforce 0 1、client(eth0为nat模式) 配置好网卡IP和网关IP&#xff0c;然后重启网卡 nmcli connection reload nmcli connection up eth0 [rootclient ~]# cat /etc/NetworkManager/syst…

500Kg载重履带式无人车技术详解

本款500Kg载重履带式无人车&#xff0c;专为复杂环境与多样化任务设计&#xff0c;具备卓越的机动性和承载能力。其基础参数如下&#xff1a; 最大载重量&#xff1a;500Kg&#xff0c;适用于运输装备、物资或执行特定作业任务。 整车重量&#xff1a;根据具体配置有所不同&am…

解决RDP远程计算机收到非预期的证书的问题

打开证书管理 查看远程桌面证书删除 然后搜索别的同名证书&#xff0c;如果在受信任的根证书里面&#xff0c;也删除。

差旅平台如何为企业降本30%?事前管控是关键

在多员工、业务跨地域的众多企业中&#xff0c;差旅成本常常成为一项巨大负担。为了有效解决这一问题&#xff0c;许多企业开始寻求可以显著降低企业差旅成本的差旅平台。本次&#xff0c;我们将集中拆解上市公司中智药业集团是如何通过差旅平台分贝通实现差旅成本减少30%效果的…

微信小程序教程011-3:京西购物商城实战之Home页实现

文章目录 3、首页3.0 创建home分支3.1 配置网络请求3.2 轮播图区域3.2.1 请求轮播图的数据3.2.2 渲染轮播图的UI结构3.2.3 配置小程序分包3.2.4 点击轮播图跳转到商品详情页3.2.5 封装 uni.$showMsg() 方法3.3 分类导航区域3.3.1 获取分类导航的数据3.3.2 渲染分类导航的UI结构…

通过Sui Gas Pool扩展赞助交易gas费

Mysten Labs正在向Sui开发者社区开源Sui Gas Pool功能。这项创新服务旨在大规模赞助Sui上交易的gas费&#xff0c;解决高并发需求应用的用户入驻问题。 Sui的原生功能赞助交易&#xff0c;允许交易使用与发送者地址不同的gas coin支付gas费。这一功能允许服务补贴用户的交易成…

如何评估并选择最佳的国内项目管理软件?

国内外主流的10款国内项目管理软件对比&#xff1a;PingCode、Worktile、Jira 、Basecamp、Trello、Asana 、Wrike、Tower 、禅道、Teambition 。 在选择适合自己企业的项目管理软件时&#xff0c;很多人会感到无从下手&#xff0c;担心无法找到既符合预算又能满足团队需求的解…

【优秀python案例】基于百度贴吧的数据采集与文本分析设计与实现

数据采集实现&#xff1a; 对百度贴吧帖子数据的采集。首先&#xff0c;使用requests库发送HTTP请求&#xff0c;通过设置请求头模拟浏览器访问&#xff0c;获取网页的HTML内容。然后&#xff0c;利用BeautifulSoup库对HTML内容进行解析&#xff0c;以便提取所需的信息。 在循…

AI回答:C#项目编译后生成部分文件的主要职责

【引入】以ConsoleApp1为例&#xff0c;请问C#编译之后以下文件有啥用 1.bin\runtimes 文件夹存放什么&#xff0c;有什么用&#xff1f; bin\runtimes 文件夹存放了项目的运行时相关文件&#xff0c;这些文件包括了各种目标平台的运行时库。 2.bin\生成的exe文件可以在别的电脑…

(这是让文心一言生成的文心一言指令博客)3分钟学会写文心一言指令:解锁AI创作新境界

3分钟学会写文心一言指令&#xff1a;解锁AI创作新境界 在这个AI技术日新月异的时代&#xff0c;文心一言作为领先的智能语言模型&#xff0c;正逐步改变着我们的创作与交流方式。无论是撰写文章、创作诗歌&#xff0c;还是进行日常对话&#xff0c;文心一言都能凭借其强大的语…

记忆化搜索——1

目录 1.斐波那契数 2.不同路径 3.最长递增子序列 4.猜数字大小2 5.矩阵中的最长递增路径 1.斐波那契数 该题规律很明显&#xff0c;就直接放记忆化搜索的版本了 class Solution { public:int dfs(int n){if(n0||n1)//递归出口{return n;}if(f[n-1]-1)//检查是否已经记忆过…

计算机网络中拥塞控制的门限值怎么设置

拥塞避免的门限值设置主要涉及到加权随机早期检测&#xff08;‌WRED&#xff09;‌技术&#xff0c;‌这是一种拥塞避免机制&#xff0c;‌通过为每个队列设定一对低门限和高门限值来实现。‌具体来说&#xff0c;‌当队列长度小于低门限时&#xff0c;‌不丢弃报文&#xff0…

64 lambda 表达式

lambda 表达式常用来声明匿名函数&#xff0c;即没有函数名字的临时使用的小函数&#xff0c;常用在临时需要一个类似于函数的功能但又不想定义函数的场合。 lambda 表达式只可以包含一个表达式&#xff0c;不允许包含其他复杂的语句&#xff0c;但在表达式中可以调用其他函数…

Flink实战(10)-checkpoint容错保证

0 前言 程序在 Flink 集群运行&#xff0c;某个算子因为某些原因出现故障&#xff0c;如何处理 在故障恢复后&#xff0c;如何保证数据状态&#xff0c;和故障发生之前的数据状态一致? 1 什么是 checkpoint(检查点)? Checkpoint 能生成快照(Snapshot)。 若 Flink 程序崩…

ResNet 网络中的残差单元

今晚看《深度学习推荐系统实战》这本书&#xff0c;读到这样一句话&#xff0c;残差单元中的两层 ReLU 网络其实拟合的是输出和输入之间的“残差” x o − x i x^o-x^i xo−xi &#xff0c;想看看微信读书的 AI 问书新功能对这句话怎么理解&#xff0c;原以为会像其他大模型一…