手动搭建gateway,项目集成gateway实现Token效果

news2024/11/16 7:40:43

目录

  • 背景
  • 步骤
    • 1、首先创建springboot项目
    • 2、引入依赖
    • 3、配置文件!!!!!(超级重要!!!根据自己的需要进行配置)
    • 4、相关类
    • 我们在服务中进行的白名单中接口的操作如下
  • 测试
    • 存:
    • 拿:
  • 总结

背景

现在想要进行token校验,故引入gateway服务。
首先阅读官网,知道概念:Gateway官网详解

步骤

1、首先创建springboot项目

看整体的目录结构
在这里插入图片描述

2、引入依赖

 <dependencies>
        <!--gateway的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--nacos注册与发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--nacos配置中心来做配置管理-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <dependency>
            <groupId>com.tfjybj</groupId>
            <artifactId>fewCode-common-redis</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>


    </dependencies>

3、配置文件!!!!!(超级重要!!!根据自己的需要进行配置)

server:
  port: 8088
  servlet:
    context-path: /api

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: login_route
          uri: lb://fewCode-provider-login
          #uri: http://localhost:8001/
          predicates:
            - Path=/login/**,/Curriculum/**,/classes/**
          filters:
            - RewritePath=/(?<segment>.*),/$\{segment}
leyou:
  filter:  #需要进行过滤的白名单
    allowPaths:
      - /login/checkLogin
      - /login/user
      - /login/userLogin
      - /upload/file
    tokenExpire: 1440

这份配置是用于Spring Cloud Gateway的配置文件,用于构建API网关。让我来解释一下每个部分的含义:

服务器配置:

服务器监听端口为8088。
Servlet的上下文路径设置为"/api",意味着所有API的端点都将具有这个前缀。
Spring Cloud Gateway配置:

服务发现定位器:启用,这意味着网关将使用服务发现来查找后端服务的路由。在动态的微服务环境中非常有用。
路由:路由配置指定了网关如何将传入的请求路由到后端服务。在这里,定义了一个路由:
id: login_route - 这是该路由的唯一标识符。
uri: lb://fewCode-provider-login - 后端服务的URI,用于处理具有负载均衡器(lb)的请求。服务名为"fewCode-provider-login",网关将使用服务发现来定位该服务。
predicates: 定义了应用此路由的条件。在这里,该路由将用于以"/login/“、”/Curriculum/“或”/classes/"开头的路径的请求。
filters: 对请求应用的过滤器,在将其转发到后端服务之前。在这里,使用了一个RewritePath过滤器,用于删除路径末尾的斜杠。
自定义配置:

leyou:这似乎是一些与"leyou"相关的自定义配置,用于特定的过滤逻辑。
filter:这是一个允许白名单路径的配置。
allowPaths:列出的路径将无需任何额外过滤而被允许。这些路径的请求不会受到任何额外的检查。
tokenExpire:设置令牌过期的时间限制(以分钟为单位),这里设置为1440分钟(24小时)。
总体而言,这份配置将Spring Cloud Gateway配置成将传入的请求路由到名为"fewCode-provider-login"的后端服务,前提是请求路径以"/login/“、”/Curriculum/“或”/classes/"开头。同时,它提供了一个白名单,允许无需任何额外过滤的路径。

4、相关类

package com.tfjybj.gateway.config;


import com.tfjybj.gateway.filter.AuthorizeFilter;
import com.tfjybj.gateway.filter.RenewFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class FilterConfig {

    @Bean
    public AuthorizeFilter authGatewayFilter() {
        //配置拦截器参数
        //order:序号,设置拦截器执行顺序,AuthGatewayFilter为1
        return new AuthorizeFilter(0);
    }

    @Bean
    public RenewFilter renewFilter(){
        // 续约Token
        return new RenewFilter(5);
    }



}

package com.tfjybj.gateway.config;

import com.tfjybj.gateway.util.FilterProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 请求白名单
 *

 */
@Component
public class IgnorePath {

    @Autowired
    private FilterProperties filterProperties;

    public boolean isAllowPath(String path) {
        //遍历白名单
        for (String allowPath : filterProperties.getAllowPaths()) {
            //判断是否允许
            if(path.startsWith(allowPath)){
                return true;
            }
        }
        return  false;
    }

}

package com.tfjybj.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class LapCorsConfiguration {
    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();

        //1、配置跨域
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}

package com.tfjybj.gateway.filter;

import com.alibaba.fastjson.JSON;

import com.tfjybj.gateway.config.IgnorePath;
import com.tfjybj.gateway.result.ResultMsgEnum;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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.data.redis.core.StringRedisTemplate;
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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;


public class AuthorizeFilter implements GlobalFilter, Ordered {

    private static final Logger log = LogManager.getLogger();

    @Autowired
    private StringRedisTemplate redisTemplate;


    @Autowired
    private IgnorePath ignorePath;

    private final int order;

    public AuthorizeFilter(int order) {
        this.order = order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();

        //获取请求的url路径
        String path = request.getURI().getPath();
        boolean flag=ignorePath.isAllowPath(path);
        if (flag) {
            log.info("请求在白名单中,metaverse.filter: {}",path);
            return chain.filter(exchange);
        } else {

            //获取token值
            String authorization = headers.getFirst("Authorization");
            log.info("Authorization值{}", authorization);
            authorization = authorization.split("Bearer ")[1];
            //判断redis中是否有token
            Boolean aBoolean = redisTemplate.hasKey("fewCode:userinfo:" + authorization);
            if (aBoolean){
                return chain.filter(exchange);
            }else {
                //声明变量
                ServerHttpResponse response = exchange.getResponse();
                HashMap map = new HashMap();
                String resp;
                DataBuffer bodyDataBuffer;

                //设置响应头
                response.setStatusCode(HttpStatus.FORBIDDEN);
                map.put("code", "403");
                map.put("message", ResultMsgEnum.AUTH_FAILED.getMsg());
                resp = JSON.toJSONString(map);
                bodyDataBuffer = response.bufferFactory().wrap(resp.getBytes());
                response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
                return response.writeWith(Mono.just(bodyDataBuffer));

            }


        }
    }


    @Override
    public int getOrder() {
        return this.order;
    }

}

package com.tfjybj.gateway.filter;

import com.tfjybj.gateway.config.IgnorePath;
import com.tfjybj.gateway.util.FilterProperties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.concurrent.TimeUnit;

/**
 * token续约的逻辑
 *

 */
public class RenewFilter implements GlobalFilter, Ordered {


    private static final Logger log = LogManager.getLogger();

    private final int order;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public RenewFilter(int order) {
        this.order = order;
    }

    @Autowired
    private IgnorePath ignorePath;

    @Autowired
    private FilterProperties filterProperties;

    @Override
    public int getOrder() {
        return this.order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();

        //获取请求的url路径
        String path = request.getURI().getPath();
        boolean flag=ignorePath.isAllowPath(path);
        if (flag) {
            log.info("请求在白名单中,metaverse.filter: {}", path);
            return chain.filter(exchange);
        }
        //token值
        String authorization = headers.getFirst("Authorization");
        authorization = authorization.split("Bearer ")[1];
        log.info("Authorization值{}", authorization);


        //TOKEN续活


        //解析TOKEN


        //根据uuid,延长用户信息
        String uuid = authorization;
        String key = "fewCode:userinfo:" + uuid;
        stringRedisTemplate.expire(key, filterProperties.getTokenExpire(), TimeUnit.MINUTES);


        return chain.filter(exchange);

    }



}

package com.tfjybj.gateway.result;

public enum ResultMsgEnum {

    FIND_SUCCESS("查询成功!"),
    FIND_FAIL("查询失败!"),

    UPDATE_SUCCESS("更新成功"),
    UPDATE_FAIL("更新失败"),

    DELETE_SUCCESS("删除成功"),
    DELETE_FAIL("删除失败"),

    SEND_SUCCESS("发送成功"),
    SEND_FAIL("发送失败"),

    EXECUTE_SUCCESS("执行成功!"),
    EXECUTE_FAIL("执行失败!"),

    AUTH_FAILED("权限认证失败"),
    AUTH_SUCCESS("权限认证成功");

    private final String msg;

    ResultMsgEnum(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}

package com.tfjybj.gateway.util;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.util.List;


@Component
@RefreshScope
@ConfigurationProperties(prefix = "leyou.filter")
public class FilterProperties {
    public void setAllowPaths(List<String> allowPaths) {
        this.allowPaths = allowPaths;
    }

    public List<String> getAllowPaths() {
        return allowPaths;
    }

    private List<String> allowPaths;

    /**
     * token过期时间
     */
    private Integer tokenExpire;

    public Integer getTokenExpire() {
        return tokenExpire;
    }

    public void setTokenExpire(Integer tokenExpire) {
        this.tokenExpire = tokenExpire;
    }
}

package com.tfjybj.gateway;

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


@SpringBootApplication
//@EnableDiscoveryClient

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

}

我们在服务中进行的白名单中接口的操作如下

(主要是根据传过来的信息生成token,然后以键值对的形式存入redis中,方便后续通过redis根据token拿人的信息):

RestController
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private Actor actorInfo;

    @ApiOperation("学生登录验证")
    @RequestMapping(value="checkLogin",method= RequestMethod.POST)
    @Transactional(rollbackFor = Exception.class)
    public Actor checkLoginInfo(@RequestBody Actor actor){
        return actorInfo.notifyStudentCheckLoginInfo(actor);
    }
}
public Actor notifyStudentCheckLoginInfo(Actor student){
        Actor actor;
        for (Actor studentInfo:allStudent){
            actor=studentInfo.checkLoginInfo(student);
            if (!ObjectUtils.isEmpty(actor)){
                //生成UUID
                String uuid = CreateUUID.createUUID();
                //存入redis
                saveRedis(uuid, actor);
                //生成token,封装到请求头
                putHeader(uuid);
                return actor;
            }
        }
        return null;
    }
public class CreateUUID {
    public CreateUUID() {
    }

    public static String createUUID() {
        String preUuid = UUID.randomUUID().toString();
        String newUUID = preUuid.replace("-", "");
        return newUUID;
    }
}
   private void saveRedis(String uuid, Object userInfo) {
        //拼接key,user信息序列化,存入redis,过期时间在nacos中设置
        String key = "fewCode:userinfo:" + uuid;
        String userJson = JSONObject.toJSONString(userInfo);
        redisTemplate.opsForValue().set(key, userJson);
        redisTemplate.expire(key, 1440, TimeUnit.MINUTES);
    }
   private void putHeader(String token) {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = sra.getResponse();
        response.setHeader("Authorization", token);
    }

测试

存:

在这里插入图片描述

在这里插入图片描述
将断点打到这里,可以观察到我们要请求的服务IP+端口号还有url地址,如下
相当手动访问

在这里插入图片描述

调通后存入redis中如下

在这里插入图片描述

拿:

然后拿着根据token获取信息

在这里插入图片描述

在这里插入图片描述

package com.tfjybj.login.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Service
public class AnalysisTokenService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 获取当前登陆人id
     *
     * @return
     */
    public String getUserId() {
        JSONObject userData = getUserData();
        return userData.get("id").toString();
    }

    /**
     * 获取用户code
     *
     * @return
     */
    public String getUserAccount() {
        JSONObject userData = getUserData();
        return userData.get("account").toString();
    }

    /**
     * 获取当前登陆人name
     *
     * @return
     */
    public String getUserName() {
        JSONObject userData = getUserData();
        return userData.get("name").toString();

    }

    public JSONObject getUserData() {
        //从请求头获取token
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String token = sra.getRequest().getHeader("Authorization");
        token = token.split("Bearer ")[1];
        //解析token
        JSONObject userJson = this.analysisToken(token);
        return userJson;
    }


    public JSONObject analysisToken  (String token){

        //解析token
        String key= "fewCode:userinfo:"+token;
        String userInfoStr = redisTemplate.opsForValue().get(key);
        JSONObject userJson = JSONObject.parseObject(userInfoStr);
//        String data = jsonObject.get("data").toString();
//        JSONObject userJson = JSONObject.parseObject(data);
        return userJson;

    }

}

总结

1、搞懂gateway是干嘛的
2、知道配置文件中各个参数是什么

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

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

相关文章

Java8 LocalDate、Date、LocalDateTime、时间戳的转换

文章目录 LocalDateplusminus比较日期 LocalDate、Date、LocalDateTime、时间戳的转换 LocalDate plus LocalDate localDate2 localDate1.plus(15, ChronoUnit.DAYS);LocalDate localDate2 localDate1.plus(Period.ofDays(15));minus LocalDate localDate2 localDate1.minu…

电缆振荡波局部放电检测定位技术

电缆振荡波检测技术主要用于交联聚乙烯电力电缆检测&#xff0c;是属于离线检测的一种有效形式 。该技术基于LCR阻尼振荡原理&#xff0c;在完成电缆直流充电的基础上&#xff0c;通过内置的高压电抗器、高压实时固态开关与试品电缆形成阻尼振荡电压波&#xff0c;在试品电缆上…

新品发布| PPS Tester同步精度测试仪

产品简介 Product introduction 产品概述 PPS 同步精度测试仪&#xff0c;即PPS Tester&#xff0c;是怿星科技开发的一款基于1PPS测量方法的系统时钟同步精度测试设备。它由硬件模块ETS2110、上位机软件ePPSTester以及相关附件组成。PPS Tester支持24路*24小时PPS信号的持续…

NodeJs后端项目使用docker打包部署

docker安装看之前的文章 默认已经安装好docker并且配置没有问题 拉取项目 https://gitee.com/coder-msc/docker-node 本地跑一个看看 pnpm install pnpm start 本地访问 http://localhost:1301/getname?name%E5%93%88%E5%88%A9%E6%B3%A2%E7%89%B9项目整个上传服务器 查看…

8.10 PowerBI系列之DAX函数专题-TopN中实现动态指标

需求 实现 建立一个辅助表供切片器选择 2 建立条件判断度量值top_measure swich(true(),selectedvalue(table[tope_type])"按数量top",sum(order_2[产品数量]),selectedvalue(table[tope_type])"按金额top",sum(order_2[订单金额]),selectedvalue(table…

字符指针和常量字符指针

用来存放字符的地址的指针 还有一种比较特殊的字符指针 有这样一道题&#xff1a;

最小时间差(力扣)排序 + 思维 JAVA

给定一个 24 小时制&#xff08;小时:分钟 “HH:MM”&#xff09;的时间列表&#xff0c;找出列表中任意两个时间的最小时间差并以分钟数表示。 示例 1&#xff1a; 输入&#xff1a;timePoints [“23:59”,“00:00”] 输出&#xff1a;1 示例 2&#xff1a; 输入&#xff1a;…

css实现有缺口的border

css实现有缺口的border 1.问题回溯2.css实现有缺口的border 1.问题回溯 通常会有那种两个div都有border重叠在一起就会有种加粗的效果。 div1,div2,div3都有个1px的border&#xff0c;箭头标记的地方是没有处理解决的&#xff0c;很明显看着是有加粗效果的。其实这种感觉把di…

python爬虫-加速乐cookie混淆解析实例小记

注意&#xff01;&#xff01;&#xff01;&#xff01;某XX网站逆向实例仅作为学习案例&#xff0c;禁止其他个人以及团体做谋利用途&#xff01;&#xff01;&#xff01; 第一步&#xff1a;抓包工具第一次请求页面&#xff0c;得到响应。本次我使用的fiddle进行抓包&#…

如何利用Requestly提升前端开发与测试的效率

痛点 B站最牛的Python接口自动化测试进阶教程合集&#xff08;真实企业项目实战&#xff09; 前端测试 在进行前端页面开发或者测试的时候&#xff0c;我们会遇到这一类场景&#xff1a; 在开发阶段&#xff0c;前端想通过调用真实的接口返回响应在开发或者生产阶段需要验证前…

Jmeter post请求传参问题

同线程组引用参数 新增数据bizId&#xff0c;然后将此次新增数据删除 添加新增数据接口&#xff0c;然后查询数据列表&#xff0c;正则表达式提取bizId 在删除接口引用此值${bizId} 添加断言&#xff0c;执行查看结果 json格式的post请求 摘要&#xff1a;正在执行的活动内容…

基于 FFmpeg 的跨平台视频播放器简明教程(七):使用多线程解码视频和音频

系列文章目录 基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;一&#xff09;&#xff1a;FFMPEG Conan 环境集成基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;二&#xff09;&#xff1a;基础知识和解封装&#xff08;demux&#xff09;基于 FFmpeg 的跨平台视频…

【AI之路】使用huggingface_hub优雅解决huggingface大模型下载问题

文章目录 前言一、Hugging face是什么&#xff1f;二、准备工作三、下载整个仓库或单个大模型文件1. 下载整个仓库2. 下载单个大模型文件 总结附录 前言 Hugging face 资源很不错&#xff0c;可是国内下载速度很慢&#xff0c;动则GB的大模型&#xff0c;下载很容易超时&#…

c++实现计时功能

#include<iostream> #include<string> #include<iomanip>//setw对应的头文件&#xff0c;用于控制输出流的格式、精度、对齐方式等 #include <thread>//实现延迟输出对应的提供了创建、管理和控制线程的功能 using namespace std;int main() {for (int…

使用镜像搭建nacos集群

安装并配置 docker 1 先安装docker //1.查看操作系统的发行版号 uname -r//2.安装依赖软件包 yum install -y yum-utils device-mapper-persistent-data lvm2//3.设置yum镜像源 //官方源&#xff08;慢&#xff09; yum-config-manager --add-repo http://download.docker.co…

sql server导入.back文件

使用SQL server官方的连接工具 SQL server Management studio 有两种方式 第一种&#xff1a; 前提是&#xff0c;提前知道数据库名称&#xff0c;建好数据库 以数据库 TEST为例子 右键数据库选型&#xff0c;选择新建数据库 输入数据库名字&#xff0c;点击确定 创建完成之…

在线讨论相亲app开发过程功能文档

用户注册与登录&#xff1a; 提供用户注册功能&#xff0c;要求用户填写基本信息&#xff08;如姓名、性别、年龄、身高、职业等&#xff09;。 支持使用手机号码或其他第三方账号&#xff08;如微信、QQ&#xff09;进行快速登录。 实现用户隐私保护机制&#xff0c;确保用…

Springboot之把外部依赖包纳入Spring容器管理的两种方式

前言 在Spring boot项目中&#xff0c;凡是标记有Component、Controller、Service、Configuration、Bean等注解的类&#xff0c;Spring boot都会在容器启动的时候&#xff0c;自动创建bean并纳入到Spring容器中进行管理&#xff0c;这样就可以使用Autowired等注解&#xff0c;…

基础篇:多线程所需知识:

前言&#xff1a; 这里的多线程主要指算法部署时所涉及的多线程内容&#xff0c;对于其他多线程知识需要自行补充常用组件有thread、mutex、promise、future、condition_variable启动线程&#xff0c;thread&#xff0c;以及join、joinable、detach、类函数启动为线程生产者消…

自动化测试 selenium(测试系列7)

目录 前言&#xff1a; 1.什么是自动化测试 2.Selenium是什么 3.Selenium原理 4.SeleniumJava环境搭建 5.Selenium常用的API使用 5.1定位元素findElement 5.1.1css选择器 5.1.2id选择器 5.1.3类选择器 5.1.4xpath选择器 5.2操作测试对象 5.2.1click点击对象 5.2.…