java jwt生成token并在网关设置全局过滤器进行token的校验并在给请求头设置参数及在微服务中解析参数

news2024/11/17 10:06:23

1、首先引入jjwt的依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2、编写生成token的工具类

package com.jjw.result.util;

import com.jjw.result.constants.SystemConstants;
import io.jsonwebtoken.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

public class AppJwtUtil {

    // TOKEN的有效期一天(S)
    private static final int TOKEN_TIME_OUT = 3600;
    // 加密KEY
    private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";
    // 最小刷新间隔(S)
    private static final int REFRESH_TIME = 300;

    // 生产ID
    public static String createToken(Long id) {
        Map<String, Object> claimMaps = new HashMap<>();
        claimMaps.put("id", id);
        long currentTime = System.currentTimeMillis();
        return Jwts.builder()
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date(currentTime))  //签发时间
                .setSubject("system")  //说明
                .setIssuer("jjw") //签发者信息
                .setAudience("app")  //接收用户
                .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
                .signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
                //过期一个小时
                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))  //过期时间戳
                .addClaims(claimMaps) //cla信息
                .compact();
    }

    /**
     * 获取token中的claims信息
     *
     * @param token
     * @return
     */
    private static Jws<Claims> getJws(String token) {
        return Jwts.parser()
                .setSigningKey(generalKey())
                .parseClaimsJws(token);
    }

    /**
     * 获取payload body信息
     *
     * @param token
     * @return
     */
    public static Claims getClaimsBody(String token) {
        try {
            return getJws(token).getBody();
        } catch (ExpiredJwtException e) {
            return null;
        }
    }

    /**
     * 获取hearder body信息
     *
     * @param token
     * @return
     */
    public static JwsHeader getHeaderBody(String token) {
        return getJws(token).getHeader();
    }

    /**
     * 是否过期
     *
     * @param token
     * @return 1 有效  0 无效  2 已过期
     */
    public static Integer verifyToken(String token) {

        try {
            Claims claims = AppJwtUtil.getClaimsBody(token);
            if (claims == null) {
                return SystemConstants.JWT_FAIL;
            }
            return SystemConstants.JWT_OK;
        } catch (ExpiredJwtException ex) {
            return SystemConstants.JWT_EXPIRE;
        } catch (Exception e) {
            return SystemConstants.JWT_FAIL;
        }
    }

    /**
     * 由字符串生成加密key
     *
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    public static void main(String[] args) {
       /* Map map = new HashMap();
        map.put("id","11");*/
        String token = AppJwtUtil.createToken(1102L);
        System.out.println(token);	

        Claims claims = AppJwtUtil.getClaimsBody(token);
        //Integer integer = AppJwtUtil.verifyToken("dsafafsa");
        Integer integer = AppJwtUtil.verifyToken("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAC2LywrDIBAA_2XP8aCrZs3frI-CQkFYQ1tK_z0b6G2GYb4wVocDMNuMXMhQfKDxJQXDuEcTk68tuFooBNig84LDRiJHO2HaQM6st3xktefdRVTHeCnzWZV5TuX2nv8vWX9_XZv7Xf0W2Rt-AAAA.srh9GIXr4ZqpXNfFb6vGIUxT12ve6Tu6xQ2KmdySsziP6AeCgP1h2GBhhx1g4rNbdsLozNI_WA-2FKsoR6-yPg");
        System.out.println(integer);
        System.out.println(claims);

    }
}

3、使用到的常量类:

package com.jjw.result.constants;

public class SystemConstants {
    //JWT TOKEN已过期
    public static final Integer JWT_EXPIRE = 2;
    //JWT TOKEN有效
    public static final Integer JWT_OK = 1;
    //JWT TOKEN无效
    public static final Integer JWT_FAIL = 0;

    public static final String USER_HEADER_NAME = "userId";

    public static final Integer TYPE_USER = 1; // 用户
    public static final Integer TYPE_E = 0; // 设备

}

4、在service的实现类中调用,生成token返回给前端

//4.生成令牌返回给前端
String token = AppJwtUtil.createToken(Long.valueOf(adUserFromDb.getId()));
Map<String, Object> info = new HashMap<>();
info.put("token",token);

5、在网关上进行校验,因此需要搭建网关服务。首先引人依赖

<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.jjw</groupId>
    <artifactId>result-content</artifactId>
    <version>1.0-SNAPSHOT</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </exclusion>
        <!--要排除spring-boot-starter-web这个依赖,但是result-content中包含的swagger-content这个
        依赖又依赖于spring-boot-starter-web,而我只需要result-content中的内容,所以将这两个依赖都排除了-->
        <exclusion>
            <groupId>com.jjw</groupId>
            <artifactId>swagger-content</artifactId>
        </exclusion>
    </exclusions>
</dependency>

6、启动类代码如下:

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayAdminApplication.class,args);
    }
}

7、配置application.yml文件

spring:
  profiles:
    active: dev
---
server:
  port: 6001
spring:
  application:
    name: leadnews-admin-gateway
  profiles: dev
  cloud:
    nacos:
      server-addr: 192.168.211.136:8848
      discovery:
        server-addr: ${spring.cloud.nacos.server-addr}
    gateway:
      globalcors:
        add-to-simple-url-handler-mapping: true  #增加
        cors-configurations:
          '[/**]': # 匹配所有请求
            allowedOrigins: "*" #跨域处理 允许所有的域
            allowedHeaders: "*"
            allowedMethods: # 支持的方法
              - GET
              - POST
              - PUT
              - DELETE
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期
      routes:
        # 平台管理
        - id: admin
          uri: lb://leadnews-admin
          predicates:
            - Path=/admin/**
          filters:
            - StripPrefix= 1

8、添加网关模块、结构如下:

在这里插入图片描述

9、编写校验token的AuthorizeFilter类(全局过滤器),其内容如下:

package com.jjw.gatewayadmin.filter;

import com.jjw.result.constants.SystemConstants;
import com.jjw.result.util.AppJwtUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
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.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //+ 1.先获取请求和响应对象
        ServerHttpRequest request = exchange.getRequest();

        ServerHttpResponse response = exchange.getResponse();
        // /get /a  post /a

        //+ 2.判断当前的请求 是否 是登录的请求 如果是 ,则放行
        String path = request.getURI().getPath();
        if(path.equals("/admin/admin/login")){
            return chain.filter(exchange);
        }

        //+ 3.获取页面传递过来的请求头中的令牌数据 如果获取不到 返回错误(401)
        String token = request.getHeaders().getFirst("token");
        if(StringUtils.isEmpty(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return  response.setComplete();//完成响应 返回 后面就不执行了
        }

        //+ 4.校验令牌 校验失败 返回错误401
        //通过jwt 来校验
        if(SystemConstants.JWT_OK!= AppJwtUtil.verifyToken(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return  response.setComplete();//完成响应 返回 后面就不执行了
        }
        //+ 5.校验成功 放行
        return chain.filter(exchange);
    }


    //过滤器的执行的优先级的设置 值越低 优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

可在请求头中添加用户信息:

import com.alibaba.fastjson.JSON;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
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.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;

/**

 * 全局过滤器:所有的请求都需要经过此过滤器。可以自定义业务逻辑进行处理,在转发请求之前先处理
 * 步骤如下:
 * 1、创建一个类实现GlobalFilter, Ordered
 * 2、将此类交给spring容器进行管理  @Component
 * 3、重写方法,实现自定义的业务逻辑
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    //获取用户携带的token令牌  解析校验 校验通过放行 不通过 返回错误
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取请求对象 和 响应对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();


        //1.5 如果请求的路径是 自媒体登录【白名单】 放行即可
        String path = request.getURI().getPath();
        if(path.startsWith("/user/app/login") ||path.startsWith("/user/app/refreshToken") || path.endsWith("v2/api-docs")){
            return chain.filter(exchange);
        }


        //2.从请求头中获取访问令牌数据
        String token = request.getHeaders().getFirst("token");
        //3.判断 是否为空 如果为空 返回错误 401
        if(StringUtils.isEmpty(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        //4.校验令牌是否正确了 如果不是  返回错误 401
        try {
            // 4.1 解析成功令牌有效
            UserTokenInfoExp userTokenInfoExp = JwtUtil.parseJwtUserToken(token);
            // 4.2 判断角色是否正确(网关是App网关, 来的令牌必须是APP或者是匿名用户的令牌)
            if(JwtUtil.isValidRole(userTokenInfoExp, TokenRole.ROLE_MEDIA)||JwtUtil.isValidRole(userTokenInfoExp, TokenRole.ROLE_ADMIN)){
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
            //直接返回 表示需要续约  判断是否过期
            if(JwtUtil.isExpire(userTokenInfoExp)){
                //403
                response.setStatusCode(HttpStatus.FORBIDDEN);
                return response.setComplete();
            }

            // 成功了将用户的令牌解析之后的所有的数据 放到请求头中 传递给下游 放行
            //URL编码 否则有乱码产生
            String encode = URLEncoder.encode(JSON.toJSONString(userTokenInfoExp), "UTF-8");
            //将信息传递给下游微服务
            request.mutate().header(SystemConstants.USER_HEADER_NAME, encode);

        } catch (Exception e) {
            e.printStackTrace();
            //直接错误
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        return chain.filter(exchange);
    }

    //值越低 优先级越高 优先被执行
    @Override
    public int getOrder() {
        return -10;
    }


     校验当前用户发送过来的请求是否有权限去访问,用户需要在请求头中携带之前生成颁发的令牌过来
    //@Override
    //public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    //    //+ 1.先获取请求和响应对象
    //    ServerHttpRequest request = exchange.getRequest();
    //
    //    ServerHttpResponse response = exchange.getResponse();
    //    // /get /a  post /a
    //
    //    //+ 2.判断当前的请求 是否 是登录的请求 如果是 ,则放行
    //    String path = request.getURI().getPath();
    //    if(path.equals("/media/wmUser/login")){
    //        return chain.filter(exchange);
    //    }
    //
    //    //+ 3.获取页面传递过来的请求头中的令牌数据 如果获取不到 返回错误(401)
    //    String token = request.getHeaders().getFirst("token");
    //    if(StringUtils.isEmpty(token)){
    //        response.setStatusCode(HttpStatus.UNAUTHORIZED);
    //        return  response.setComplete();//完成响应 返回 后面就不执行了
    //    }
    //
    //    //+ 4.校验令牌 校验失败 返回错误401
    //    //通过jwt 来校验
    //    if(SystemConstants.JWT_OK!= AppJwtUtil.verifyToken(token)){
    //        response.setStatusCode(HttpStatus.UNAUTHORIZED);
    //        return  response.setComplete();//完成响应 返回 后面就不执行了
    //    }
    //
    //    //在路由请求之前,将当前令牌中的用户id解析出来并添加到请求头中,传递给下游
    //    Claims claimsBody = AppJwtUtil.getClaimsBody(token);
    //    Object id = claimsBody.get("id"); // 这个id是claimMaps.put("id", id);的id
    //    //String id1 = claimsBody.getId(); // 这俩id是不一样的,这个id是Jwts.builder().setId
    //
    //    request.mutate().header(USER_HEADER_NAME, id.toString());
    //
    //    //+ 5.校验成功 放行
    //    return chain.filter(exchange);
    //}
    //
    //
    过滤器的执行的优先级的设置 值越低 优先级越高
    //@Override
    //public int getOrder() {
    //    return 0;
    //}




}

在服务端如何获取拦截器放在请求头中的信息:先定义一个获取的工具类

public class RequestContextUtil {
    /**
     * 获取登录的用户的ID 可以是自媒体账号 也可以是 平台账号 也可以是app账号
     * @return
     */
    public static String getUserInfo(){
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取路由转发的头信息
        String headerValue = request.getHeader(SystemConstants.USER_HEADER_NAME);
        return headerValue;
    }
}

在微服务中使用到的地方直接导包调用即可:

String userId = RequestContextUtil.getUserInfo();

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

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

相关文章

软考高级系统架构设计师(九) 作文模板-论设计模式及其应用(未完待续)

目录 掌握的知识点 创建型 结构型 行为型 掌握的知识点 设计模式分为哪3类 每一类包含哪些具体的设计模式 创建型 创建型模式是对对象实例化过程的抽象&#xff0c;他通过抽象类所定义的接口&#xff0c;封装了系统中对象如何创建、组合等信息。 创建型模式主要用于创建对…

【物联网】微信小程序接入阿里云物联网平台

微信小程序接入阿里云物联网平台 一 阿里云平台端 1.登录阿里云 阿里云物联网平台 点击进入公共实例&#xff0c;之前没有的点进去申请 2.点击产品&#xff0c;创建产品 3.产品名称自定义&#xff0c;按项目选择类型&#xff0c;节点类型选择之恋设备&#xff0c;联网方式W…

Linux下安装Redis的详细安装步骤

一.Redis安装 1.下载linux压缩包 【redis-5.0.5.tar.gz】 2.通过FlashFXP把压缩包传送到服务器 3.解压缩 tar -zxvf redis-5.0.5.tar.gz4.进入redis-5.0.5可以看到redis的配置文件redis.conf 5.基本的环境安装 使用gcc -v 命令查看gcc版本已经是4.8.5了&#xff0c;于是就…

ubuntu系统突然失去网络问题

修复ubuntu系统网络问题 1. 服务不存在&#xff1f;2. 修改配置&#xff0c;自动启动网络 每天都在用的ubuntu系统突然ssh连接不上&#xff0c;进系统ifconfig也不显示ip。当然也ping不通任何网页。 1. 服务不存在&#xff1f; 初步怀疑网络服务被关闭了&#xff0c;需要修改配…

【C6】数据类型/移植/对齐,内核中断,通过IO内存访问外设,PCI

文章目录 1.内核基础数据类型/移植性/数据对齐&#xff1a;页大小为PAGE_SIZE&#xff0c;不要假设4K&#xff0c;保证可移植性1.1 kdatasize.c&#xff1a;不同的架构&#xff08;x86_64,arm&#xff09;&#xff0c;基础类型大小可能不同&#xff0c;主要区别在long和指针1.2…

chatgpt赋能python:用Python访问数据库的SEO文章

用Python访问数据库的SEO文章 在当今互联网飞速发展的时代&#xff0c;数据处理和数据库技术的重要性不言而喻。在这些应用中&#xff0c;Python是使用最广泛和最受欢迎的编程语言之一。Python的简单和易学性使其成为理想的选项&#xff0c;可以通过Python来访问各种类型的数据…

荣耀90推出最新MagicOS7.1更新,增加控制中心功能

荣耀 90 系列机型推出了最新的 Magic OS 7.1更新&#xff0c;版本号为7.1.0.137 (C00E130R2P2)。该更新主要增加了控制中心功能&#xff0c;并对部分场景拍摄效果进行了优化。此外&#xff0c;该更新还提升了系统与部分三方应用的兼容性&#xff0c;以提高系统性能和稳定性。 …

选择最适合您自动化系统的控制方式

自动化系统可采用多种不同的控制方式&#xff0c;其中硬件控制和PLC&#xff08;可编程逻辑控制器&#xff09;是常见的选择。 刚好&#xff0c;我这里有上位机入门&#xff0c;学习线路图&#xff0c;各种项目&#xff0c;需要留个6。 硬件控制通常指使用专用硬件电路实现控…

C++3(sizeof和逗号运算符,类型转换)

1.sizeof的用法 逗号运算符 口诀&#xff1a;从左到右算&#xff0c;返回最右边的值 类型转换 如何实现的隐式类型转换&#xff1f; 先算右边的&#xff0c;右边的3&#xff08;int&#xff09;先提升为double &#xff0c;然后算得&#xff08;7.541&#xff08;double&#…

CMU 15-445 -- 关系型数据库重点概念回顾 - 01

CMU 15-445 -- 关系型数据库重点概念回顾 - 01 引言Relational Data ModelDBMS数据模型Relational ModelRelation & TuplePrimary KeysForeign Keys Data Manipulation Languages (DML)Relational Algebra Advanced SQLSQL 的历史SQLAggregatesGroup ByHavingOutput Redire…

内存屏障类型表

load store 啥意思 内存屏障类型表 StoreLoad Barriers是一个“全能型”的屏障&#xff0c;它同时具有其他3个屏障的效果。现代的多处理器大多支持该屏障&#xff08;其他类型的屏障不一定被所有处理器支持&#xff09;。执行该屏障开销会很昂贵&#xff0c;因为当前处理器通常…

在文件每行开头或结尾插入指定字符

1、在文件每行插入指定字符 sed -i "s/^/curl /g" missing.txt效果 2、在每行末尾插入指定字符 sed -i "s/$/结束 /g" missing.txt

leetcode1856. 子数组最小乘积的最大值(单调栈-java)

子数组最小乘积的最大值 leetcode1856.子数组最小乘积的最大值题目描述解题思路代码演示&#xff1a; 经典算法集锦 leetcode1856.子数组最小乘积的最大值 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/maximum-subarr…

【网络安全】初探SQL注入漏洞

如何利用SQL注入漏洞获取用户密码 前言1. 设计思路2. 设计目的 一、网站快速搭建1. 登录页2. 注册页3. 数据库连接页4. 首页&#xff08;登录后跳转到此处&#xff09;5. session页6. 注销页7. 查询页8. 数据库 二、SQL注入实例&#xff08;小试牛刀&#xff09;1. 猜测漏洞逻辑…

tomcat部署以及优化

目录 1.三个核心组件 2.tomcat服务部署 3.虚拟主机配置 4.tomcat优化 5.部署两台tomcat服务器 6.总结 1.三个核心组件 web容器 完成web服务 servlet容器 名为catalina 用于处理servlet JSP容器 将JSP动态网页翻译成…

网络通信之旅:揭秘交换机、路由器与网关的神奇世界!

文章目录 一 交换机2.1 交换机初识2.2 MAC地址表2.3 数据包2.4 交换机与数据包2.5 泛洪2.6 结论&#xff1a;交换机—二层设备 三 路由器3.1 WAN口&LAN口3.2 路由器-WAN交换机 四 网关4.1 子网划分4.2 网关4.3 路由 五 实践&#xff1a;路由器桥接-搭建主副路由器5.1 知识探…

动态规划:

这类问题非常简单&#xff0c;甚至看起来有点笨&#xff0c;说白了就是利用计算机的计算能力一步步算过去&#xff0c;也就是大多数人没有意识到的递推问题 比如求1~n的前缀和&#xff1a; #include<iostream> using namespace std; long long sum[100]; int main(){in…

20kV高精度可调高压稳压测试电源的学习与使用

一&#xff1a;应用范围 A: 二极管反向耐压测试 B: 二极管反向漏电流测试 C: 高压电容耐压测试 D: 玻璃釉电阻非线性性能测试 E:氙灯击穿电压测试 F: 材料耐压测试 二、特点 高精度恒流恒压高压输出源 它拥有0~20kV的电压输出能力, 0.005%的电压分辨率精度, 0.1uA的电 …

Docker安装Prometheus和Grafana监控Redis

Docker安装Prometheus和Grafana监控Redis 使用 Docker 安装 Grafana 和 Prometheus 无疑是最简单的&#xff0c;我们接下来将采用此种方式。 1、安装Prometheus 查看Prometheus镜像 $ docker search prometheus拉取镜像 $ docker search bitnami/prometheus在/home/zhangs…

css 小程序 按钮控件点击区域放大 热点区域

背景&#xff1a; 小程序在手机上屏幕过小&#xff0c;但是又想放很多元素&#xff0c;这时候点击区域&#xff0c;命中元素概率很&#xff0c;希望能在不布局不变形情况下&#xff0c;把点击区域放大。 先看效果&#xff1a; 解决方法&#xff1a; 通过&#xff1a;transfo…