SpringCloud:Gateway之鉴权

2023-05-16

一、JWT 实现微服务鉴权

JWT一般用于实现单点登录。单点登录:如腾讯下的游戏有很多,包括lol,飞车等,在qq游戏对战平台上登录一次,然后这些不同的平台都可以直接登陆进去了,这就是单点登录的使用场景。JWT就是实现单点登录的一种技术,其他的还有oath2等。

1 什么是微服务鉴权

我们之前已经搭建过了网关,使用网关在网关系统中比较适合进行权限校验。

在这里插入图片描述

那么我们可以采用JWT的方式来实现鉴权校验。

2.代码实现

思路分析

在这里插入图片描述

1. 用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录
2. 用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
3. 用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN 
4. 网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误

签发token

(1)创建类: JwtUtil

package com.mye.nacosprovider.jwt;
 
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
 
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;


import java.util.*;

@Component
public class JwtUtil {
 
    //加密 解密时的密钥 用来生成key
    public static final String JWT_KEY = "IT1995";
    
    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

 
    public static String createJWT(String id, String subject, long ttlMillis){
 
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; //指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
        long nowMillis = System.currentTimeMillis();//生成JWT的时间
        Date now = new Date(nowMillis);
        SecretKey key = generalKey();//生成签名的时候使用的秘钥secret,这个方法本地封装了的,一般可以从本地配置文件中读取,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
        JwtBuilder builder = Jwts.builder() //这里其实就是new一个JwtBuilder,设置jwt的body
//                .setClaims(claims)            //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setId(id)                    //设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                .setIssuedAt(now)            //iat: jwt的签发时间
                .setSubject(subject)        //sub(Subject):代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
                .signWith(signatureAlgorithm, key);//设置签名使用的签名算法和签名使用的秘钥
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);        //设置过期时间
        }
        return builder.compact();            //就开始压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
    }
 
    public static Claims parseJWT(String jwt){

        SecretKey key = generalKey();  //签名秘钥,和生成的签名的秘钥一模一样
        Claims claims = Jwts.parser()  //得到DefaultJwtParser
                .setSigningKey(key)         //设置签名的秘钥
                .parseClaimsJws(jwt).getBody();//设置需要解析的jwt
        return claims;
    }
 
    public static void main(String[] args){
 
        Map<String, Object> user = new HashMap<>();
        user.put("username", "it1995");
        user.put("password", "123456");
        String jwt = createJWT(UUID.randomUUID().toString(), JSON.toJSONString(user), 3600 * 24);
 
        System.out.println("加密后:" + jwt);
 
        //解密
        Claims claims = parseJWT(jwt);
        System.out.println("解密后:" + claims.getSubject());
    }
}

(2)修改login方法,用户登录成功 则 签发TOKEN

@PostMapping("/login")
    public String login(@RequestBody User user){
        //在redis中根据用户名查找密码
        String password = redisTemplate.opsForValue().get(user.getUsername());
        System.out.println(password);
        boolean checkResult = BCrypt.checkpw(user.getPassword(), password);
        if (checkResult){
            Map<String, String> info = new HashMap<>();
            info.put("username", user.getUsername());
            String token = JwtUtil.createJWT(UUID.randomUUID().toString(), user.getUsername(), 3600L*1000);
            info.put("token",token);
            return JSONUtil.toJsonStr(info);
        }else {
            return "登录失败";
        }
    }

(3) 测试

在这里插入图片描述

网关过滤器验证token

(1)网关模块添加依赖

<!--鉴权-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

(2)创建JWTUtil类

package com.mye.nacosprovider.jwt;
 
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
 
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;


import java.util.*;

@Component
public class JwtUtil {
 
    //加密 解密时的密钥 用来生成key
    public static final String JWT_KEY = "IT1995";
    
    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

 
    public static String createJWT(String id, String subject, long ttlMillis){
 
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; //指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
        long nowMillis = System.currentTimeMillis();//生成JWT的时间
        Date now = new Date(nowMillis);
        SecretKey key = generalKey();//生成签名的时候使用的秘钥secret,这个方法本地封装了的,一般可以从本地配置文件中读取,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
        JwtBuilder builder = Jwts.builder() //这里其实就是new一个JwtBuilder,设置jwt的body
//                .setClaims(claims)            //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setId(id)                    //设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                .setIssuedAt(now)            //iat: jwt的签发时间
                .setSubject(subject)        //sub(Subject):代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
                .signWith(signatureAlgorithm, key);//设置签名使用的签名算法和签名使用的秘钥
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);        //设置过期时间
        }
        return builder.compact();            //就开始压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
    }
 
    public static Claims parseJWT(String jwt){

        SecretKey key = generalKey();  //签名秘钥,和生成的签名的秘钥一模一样
        Claims claims = Jwts.parser()  //得到DefaultJwtParser
                .setSigningKey(key)         //设置签名的秘钥
                .parseClaimsJws(jwt).getBody();//设置需要解析的jwt
        return claims;
    }
 
    public static void main(String[] args){
 
        Map<String, Object> user = new HashMap<>();
        user.put("username", "it1995");
        user.put("password", "123456");
        String jwt = createJWT(UUID.randomUUID().toString(), JSON.toJSONString(user), 3600 * 24);
 
        System.out.println("加密后:" + jwt);
 
        //解密
        Claims claims = parseJWT(jwt);
        System.out.println("解密后:" + claims.getSubject());
    }
}

(3)创建过滤器,用于token验证

/**
 * 鉴权过滤器 验证token
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
    private static final String AUTHORIZE_TOKEN = "token";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		//1. 获取请求
        ServerHttpRequest request = exchange.getRequest();
        //2. 则获取响应
        ServerHttpResponse response = exchange.getResponse();
        //3. 如果是登录请求则放行
        if (request.getURI().getPath().contains("/admin/login")) {
            return chain.filter(exchange);
        }
        //4. 获取请求头
        HttpHeaders headers = request.getHeaders();
        //5. 请求头中获取令牌
        String token = headers.getFirst(AUTHORIZE_TOKEN);

        //6. 判断请求头中是否有令牌
        if (StringUtils.isEmpty(token)) {
            //7. 响应中放入返回的状态吗, 没有权限访问
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //8. 返回
            return response.setComplete();
        }

        //9. 如果请求头中有令牌则解析令牌
        try {
            JwtUtil.parseJWT(token);
        } catch (Exception e) {
            e.printStackTrace();
            //10. 解析jwt令牌出错, 说明令牌过期或者伪造等不合法情况出现
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //11. 返回
            return response.setComplete();
        }
        //12. 放行
        return chain.filter(exchange);
    }

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

(4)测试:

首先进行登录测试

在这里插入图片描述

在进行鉴权测试

在这里插入图片描述

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

SpringCloud:Gateway之鉴权 的相关文章

随机推荐

  • ubuntu18.04 安装VMware出现gcc无法安装的问题

    在学习linux过程中需要安装VMware 而安装完成后出现报错 提示要求安装gcc 查阅资料后 ubuntu一般在安装后是自带gcc的 于是查看gcc版本 gcc v 提示没有安装gcc 尝试apt安装gcc sudo apt get i
  • vnc服务搭建及配置

    一 xff0c centos6系统 第一步 xff0c 安装VNC packages xff1a root 64 yum install tigervnc server y 第二步 xff0c 开启VNC服务 xff08 第一次开启会要求设
  • vnc之登录失败:Authentication Failure

    vnc设置正常 xff0c 密码通过vncpasswd设置过 xff0c 但是登录后总是限制认证失败 解决方法 xff1a 用户登录服务器的密码和登录vnc的密码是可以不一样的 1 首先 xff0c 通过ssh登陆用户 xff0c 输入容v
  • ftp服务之锁定用户家目录

    很多情况下 我们希望限制ftp用户只能在其主目录下 root dir 下活动 不允许他们跳出主目录之外浏览服务器上的其他目录 这时候我就需要使用到chroot local user chroot list enable chroot lis
  • 惠普暗影精灵2更新bios系统,防止电池鼓包

    https support hp com cn zh drivers selfservice omen by hp 15 ax000 laptop pc series 10862325 model 12732710 bios版本怎么看 查看
  • stm32直流电机控制—PID算法篇

    stm32直流电机控制 PID算法篇 一 常用的控制算法1 控制系统的基本结构 xff1a 2 常用控制算法 xff1a 位式控制 二位式控制算法 具有回差的二位式控制算法 三位式控制算法 小结 二 PID控制算法1 PID控制算法的基本思
  • ADRC例程

    ADRC 优化fhan 自抗扰控制入门 自抗扰死忠粉 ADRC H span class token macro property span class token directive keyword ifndef span ADRC H
  • 简易PID模块(C++)

    简易PID模块 源码为WR学长所写 xff0c 现发布在CSDN xff0c 作为传承 xff0c 留给学弟使用 库文件 span class token macro property span class token directive
  • 字节序的详细讲解

    字节序 1 字节序的特点2 字节序转换函数2 1 htonl函数 发 将主机字节序的IP地址 转换成网络字节序的IP地址2 2 ntohl函数 收 将网络字节序的IP地址3 3 htons函数 发 将主机字节序的端口 转换成 网络字节序的端
  • HBase java API

    1 pom lt dependency gt lt groupId gt org apache hbase lt groupId gt lt artifactId gt hbase client lt artifactId gt lt ve
  • 我用24次离职,换来6条血一样的教训

    作者丨周冲 来源丨周冲的影像声色 xff08 zhouchong2017 xff09 我是2014年毕业的 毕业之后 xff0c 以为年轻 xff0c 有的是任性资本 xff0c 也有的是发展可能 xff0c 结果频繁跳槽 xff0c 稍有
  • 2020年MathorCup高校数学建模挑战赛—大数据竞赛A题移动通信基站流量预测求解全过程文档及程序

    2020年MathorCup高校数学建模挑战赛 大数据竞赛 A题 移动通信基站流量预测 原题再现 xff1a 随着移动通信技术的发展 xff0c 4G 5G 给人们带来了极大便利 移动互联网的飞速发展 xff0c 使得移动流量呈现爆炸式增长
  • rviz导航——2D Pose estimate

    关于rviz中小车初始点的设置问题 一般有两种方法 xff1a 1打开rviz 在其上方工具栏中有2D Pose estimate xff0c 用来设置大概的初始点 2一般在amcl launch文件中也会定义初始点 xff0c 大多设为0
  • 树莓派通过MAVROS与Pixhawk/PX4通信

    1 安装ROS 2 安装MAVROS 3 配置Pixhawk 通常TELEM 2用来和机载计算机连接 xff0c 连接方式为串口转USB 通信协议为MAVLink xff0c 因此需要先使能TELM2上的MAVLink输出 v1 9 0及以
  • PX4使用Optitrack进行室内定位

    1 使用mavros连接上位机和PX4 树莓派通过MAVROS与Pixhawk PX4通信 2 PX4的设置 xff0c 将EKF估计位姿的数据来源改为VISION 使用地面站软件如QGroundControl xff0c 连接Pixhaw
  • 提高MAVROS的IMU发布频率

    提高MAVROS的IMU发布频率 Pixhawk的IMU数据可通过MAVROS获得 xff0c 相关的话题为 xff1a mavros imu data 但是MAVROS默认的IMU输出频率较低 xff0c 可能只有不到10Hz xff0c
  • 日期工具类

    日期工具类 1 span class token keyword package span span class token namespace com span class token punctuation span hl span c
  • easypoi的使用-导入校验

    easypoi导入校验 一 导入之基础校验 现在产品需要对导入的Excel进行校验 xff0c 不合法的Excel不允许入库 xff0c 需要返回具体的错误信息给前端 xff0c 提示给用户 xff0c 错误信息中需要包含行号以及对应的错误
  • springboot-日志配置

    springboot 日志配置 1 默认日志 logback 默认情况下 xff0c springboot会使用logback来记录日志 xff0c 并且使用INFO级别输出到控制台 xff0c 在运行程序的时候 xff0c 你应该看到过很
  • SpringCloud:Gateway之鉴权

    一 JWT 实现微服务鉴权 JWT一般用于实现单点登录 单点登录 xff1a 如腾讯下的游戏有很多 xff0c 包括lol xff0c 飞车等 xff0c 在qq游戏对战平台上登录一次 xff0c 然后这些不同的平台都可以直接登陆进去了 x