【shiro】shiro整合JWT——1.需要创建的类

news2024/12/23 20:26:44

前言

shiro整合JWT系列,主要记录核心思路–如何在shiro+redis整合JWTToken。
该篇主要讲述整合JWT需要创建那些类,如下:

  1. JwtToken (JWT实体类)
  2. JwtUtil (JWT工具类)
  3. JwtFilter (JWT拦截器)

所使用的依赖是:

        <!--shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        
        <!--JWT-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.2.0</version>
        </dependency>  

		<!-- Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>    

1、JwtToken (JWT实体类)

该类实现了AuthenticationToken中的2个接口,getPrincipal()getCredentials(),主要用来获取token。
这两个方法之前是由UsernamePasswordToken实现,用于获取用户名和密码。

  • JwtToken代码如下:
public class JwtToken implements AuthenticationToken {
	/**
     * 用于确保在对象序列化过程中,版本号一致。
     * 当一个对象被序列化后,它的字节流可以被存储在文件系统中或通过网络传输到另一个计算机。
     * 当接收方收到字节流并反序列化它时,它需要确保序列化和反序列化的版本号一致,否则就会抛出版本不一致的异常。
     * 因此,在序列化类中,需要为每个类提供一个唯一的 serialVersionUID,确保在版本升级时,反序列化仍然可以正确地工作。
     */
    private static final long serialVersionUID = 1L;
    private String token;

    public JwtToken(String token) {
        this.token = token;
    }
    
    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}

疑问:这个类具体是怎么使用到的?
回答:当获取到前端传来的token字符串后,将该字符串token存入JwtToken 对象中(如:new JwtToken(token) ),当使用的时候,只需要调用上面任意方法就可以取出前端传来的token。

2、JwtUtil (JWT工具类)

JWT工具类最主要的应该就是以下3个方法:

  1. verify:验证
    目的:验证token是否正确,判断前端传入的token后端生成的token是否一样
    账号和密钥按指定Algorithm 创建出来的JWT对象,可以理解就是我们的token。

  2. getUsername:获取token中的账号(用户名)
    目的:用于查询数据库中该用户的信息(得到存于数据库中的加密密码)
    Jwt中主要包含header和payload两个部分。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. sign:生成签名
    目的:创建该用户的token
    密钥按指定Algorithm生成的实例 + payload信息 + 过期时间,生成token(还可以加更多内容,有兴趣再去研究)

  • JwtUtil代码如下:
public class JwtUtil {

    // 过期时间30分钟
    public static final long EXPIRE_TIME = 30 * 60 * 1000;

    /**
     * 1、校验token是否正确
     *
     * @param token  密钥
     * @param secret 用户的密码
     * @return 是否正确
     */
    public static boolean verify(String token, String username, String secret) {
        try {
            // 根据密钥(这里是密码)生成一个算法实例
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 生成JWT效验器
            JWTVerifier verifier = JWT.require(algorithm) // 设置一个以该算法为基础的校验器
                                      .withClaim("username", username) // payload 存储非敏感的信息 例如用户账号,不能存密码,防止被人解
                                      .build(); // 创建校验器
            // 效验TOKEN,如果出现不匹配,就会出现异常
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    /**
     * 2、获得token中的信息无需secret解密也能获得
     *
     * JWT.decode解码:可以查看token中的head,payload信息
     * 如果想要校验token,则需要知道sign的算法和附带的payload信息
     * sign的算法:Algorithm.HMAC256(secret)
     * 附带的payload信息:JWT.withClaim
     * @return token中包含的用户名
     */
    public static String getUsername(String token) {
        try {
            // 对token进行解码【可以查看token中的head,payload信息】
            DecodedJWT jwt = JWT.decode(token);
            // 获取解码信息中的username
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 3、生成签名,5min后过期
     * 生成token签名EXPIRE_TIME 分钟后过期
     * @param username 用户名
     * @param secret   用户的密码【secret应该是定义的密钥,这里是用当前用户密码当作密钥】
     * @return 加密的token
     */
    public static String sign(String username, String secret) {
        try{
            //现在系统的时间 + 过期时间
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            // 根据密钥(这里是密码)生成一个算法实例
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 附带username信息
            return JWT.create()
                    .withClaim("username", username) // payload 存储非敏感的信息 例如用户账号,不能存密码,防止被人解析
                    .withExpiresAt(date) // 指定令牌的过期时间
                    .sign(algorithm); // 签名  保密复杂
        }catch (UnsupportedEncodingException e){
            return null;
        }

    }

3、JwtFilter (JWT拦截器)

注意:引入JWT后,核心登录subject.login()将在JwtFilter类的executeLogin方法里。

public class JwtFilter extends BasicHttpAuthenticationFilter {

    /**
     * 执行登录认证
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        try {
            executeLogin(request, response);
            return true;
        } catch (Exception e) {
            throw new AuthenticationException("Token失效请重新登录");
        }
    }

    /**
     * 执行登录
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("ACCESS_TOKEN");

        JwtToken jwtToken = new JwtToken(token);
        // 提交给realm进行登入,如果错误他会抛出异常并被捕获 
        // 这里真正实现shiro的登录,getSubject(request, response)直接SecurityUtils.getSubject()也一样
        getSubject(request, response).login(jwtToken);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
    }

	/**
     * 如果没有登录,直接返回401未授权提示
     *
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 这里是个坑,如果不设置的接受的访问源,那么前端都会报跨域错误,因为这里还没到corsConfig里面
        httpResponse.setHeader("Access-Control-Allow-Origin", ((HttpServletRequest) request).getHeader("Origin"));
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json; charset=utf-8");
		httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
        try {
            // 返回到前端信息
            httpResponse.getWriter().write("未登录或登录失效,请重新登录!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }
}

疑惑:我知道了token的值,在模拟请求中,这个token应该放在header里的哪个字段?
回答:这个是我们代码中定义的,String token = httpServletRequest.getHeader("ACCESS_TOKEN");这段就是获取请求header中名为ACCESS_TOKEN的值;也就是我们说token存放的地方。(注意:ACCESS_TOKEN这个名称可以自己定义)
ps:辣鸡的我就这个地方找了好久😢

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

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

相关文章

IIS日志分析

一、下载IIS日志分析软件 地址如下&#xff1a; 开放网盘: 寄存一些分享出来的文件之类的东西 其中就是LogParser和LPS两个压缩文件 二、安装软件 1、需要先安装Log Parser 运行安装上面的文件。 2. 运行Log Parser Studio 在解压的LPSV2.D1文件夹中运行LPS.exe 出现下面…

BR 4P3040.00-490 标准PLC采用梯形逻辑编程

B&R 4P3040.00-490 奥地利贝加莱 电源面板 可编程逻辑控制器(Programmable Logic Controller)技术通常与梯形逻辑编程隔离通信——这是B&R迈出的一大步。B&R平台是基于PC的&#xff0c;这意味着您可以使用PLC系统中不常见的编程语言和功能。例如&#xff0c;可以用…

《架构设计》-09-分布式服务架构(注册中心、服务发布、服务调用、服务治理)

文章目录 1. 概述2. 集群容错策略3. 服务路由3.1 直接路由3.2 间接路由和注册中心3.3 路由规则3.4 服务路由/负载均衡/集群容错的关系 4. 服务发布4.1 发布启动器4.2 动态代理4.3 发布管理器4.4 协议服务器 5. 服务调用6. 服务治理 1. 概述 RPC架构的意义 解决了分布式环境下两…

chatgpt赋能python:Python写UDF对于SEO的影响

Python写UDF对于SEO的影响 作为一名有10年python编程经验的工程师&#xff0c;我对Python写UDF的优势深有体会。UDF&#xff08;User-Defined Functions&#xff09;是用户自定义函数的缩写&#xff0c;在数据处理和数据分析的过程中经常用到。下面我将介绍Python写UDF对于SEO…

渲染学生信息表

代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, initi…

MFC(六)框架理论

关键类 ,MFC中关键类有&#xff1a; CMFCAPP:最底层的类&#xff0c;也是最重要的类&#xff0c;统筹全局&#xff0c;管理DOCUMENT TEMPLATE CFRAMEWND:框架窗口&#xff0c;包括菜单栏、工具栏、状态栏等等&#xff0c;主要是负责窗口的布局 CVIEW:负责展示具体的数据 C…

chatgpt赋能python:Python内置变量介绍

Python内置变量介绍 Python是一种高级编程语言&#xff0c;具有简单易学、可读性强、可扩展性强等特点。在Python中&#xff0c;有许多内置变量&#xff08;built-in variables&#xff09;&#xff0c;以方便用户在编写程序时进行使用。本文将会对Python中的内置变量进行介绍…

基于SpringBoot+Vue的逍遥大药房管理系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

干货,一文弄懂RF检波器那些事

WiFi、4G、蓝牙等各种无线连接技术的普及带动各种终端设备井喷式增长&#xff0c;包括物联网、可穿戴等各种基于无线连接技术的新兴产业迅速成长起来&#xff0c;各种无线信号链解决方案涌现推动这种热潮的持续发展。在无线信号链中&#xff0c;很久没有听到有人提起一个关键的…

快速开发和使用Android串口

一、什么是串口 串口叫做串行接口&#xff0c;也称串行通信接口&#xff0c;也可以叫做COM口&#xff0c;按电气标准及协议来分包括RS-232-C、RS-422、RS485、USB等。串行接口是指数据一位一位地顺序传送&#xff0c;其特点是通信线路简单&#xff0c;只要一对传输线就可以实现…

计算机视觉—YOLO V4

计算机视觉—YOLO V4 1、YOLO V41.1、网络结构1.1.1、BackBone&#xff1a;CSPDarknet531.1.2、Neck&#xff1a;SPP结构1.1.3、Neck&#xff1a;PAN结构1.1.4、YOLO v4整体结构 1.2、优化策略 1、YOLO V4 原论文下载地址&#xff1a;https://arxiv.org/abs/2004.10934 1.1、…

Windows中安装GCC教程

GCC的安装教程 GCC简介 GCC编译器通常在Linux系统下使用&#xff0c;一般来说大部分发行的系统会默认安装&#xff0c;GCC编译器使用gcc指令在终端进行shell操作。 对于新接触Linux的朋友来说&#xff0c;简单的在Windows中练习过渡一下应该就足够了。&#xff08;我就是因为…

Apache IoTDB 荣获国家网信办 2022 年中国开源创新大赛决赛一等奖,三位核心研发荣获表彰!...

项目获得权威认可&#xff01; 2023 年 5 月 15 日&#xff0c;2022 年中国开源创新大赛组委会对外公布“2022 中国互联网发展创新与投资大赛公益项目暨2022年中国开源创新大赛”决赛获奖名单&#xff0c;并于 2023 年 5 月 31 日在北京举办“2022年中国开源创新大赛总结发布活…

chatgpt赋能python:用Python编写FizzBuzz——解析最简单的编程题

用Python编写FizzBuzz——解析最简单的编程题 作为每个程序员的入门题目&#xff0c;FizzBuzz是一个简单但常见的问题。FizzBuzz要求我们用数字1到100来打印输出&#xff0c;但是当数字是3的倍数时&#xff0c;需要输出Fizz&#xff1b;当数字是5的倍数时&#xff0c;需要输出…

力扣高频SQL50题(基础版)——第三天

力扣高频SQL50题(基础版)——第三天 1 产品销售分析Ⅰ 1.1 题目内容 1.1.1 基本题目信息1 1.1.2 基本题目信息2 1.1.3 示例输入输出 1.2 示例sql语句 # Write your MySQL query statement below SELECT p.product_name,s.year,s.price FROM Sales s INNER JOIN Product p …

chatgpt赋能python:Python几次方函数介绍

Python几次方函数介绍 Python作为一门高级编程语言&#xff0c;具有丰富的数学函数库。其中&#xff0c;几次方函数在许多数值计算、数据分析和科学计算中都得到广泛应用。Python中的几次方函数有多种实现方式&#xff0c;包括内置函数pow()、运算符**、NumPy库的numpy.power(…

(3)NUC 980 kenerl编译

解压 用到的配置文件位置&#xff1a; /NUC980-linux-4.4.y-master/arch/arm/configs/nuc980_defconfig 执行&#xff1a; 编译linux内核源码。了解其 配置文件在 arch/arm/configs/nuc980_defconfig (1) make nuc980_defconfig 载入配置文件 (2) make menuconfig --->Devi…

机器龙的制作

1. 功能说明 本文示例将实现R326样机机器龙边张合嘴巴、边煽动翅膀、边摆动尾巴运动的功能。 2. 结构说明 本项目使用的机器龙样机是用可以用探索者零件或者探索者兼容零件制作。样机主要由头部模块、翅膀模块、尾巴模块、四足行走模块四部分组成。其中头部模块由2自由度并联关…

从元宇宙到生成式AI:炒作、现实和未来前景

不久前&#xff0c;科技界充斥着一种被称为元宇宙的未来主义概念。这个相互关联的虚拟现实空间宇宙&#xff0c;个人可以在模拟环境中进行交互&#xff0c;被誉为技术的未来。如今围绕元宇宙的炒作已经彻底失败了。技术重点现在已经转向生成AI&#xff0c;重点是像GPT-4和谷歌的…

一文搞懂Flutter的手势事件——事件分发与冲突处理详解

本文字数&#xff1a;43617字 预计阅读时间&#xff1a;110分钟 前言 之前有两篇文章都围绕着runApp()进行展开&#xff0c;讲解了布局绘制的详细过程。 https://www.jianshu.com/p/2ef749ff4d40/https://www.jianshu.com/p/f37f8da235ec 那么接下来我们想详细的说一说Flutter是…