token令牌,过滤器,JWT,拦截器

news2024/9/29 22:14:07

令牌(token)技术

不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了

1.基本流程

用户使用用户名密码来请求服务器

服务器进行验证用户的信息

服务器通过验证发送给用户一个token

客户端存储token,并在每次请求时附送上这个token值

服务端验证token值,并返回数据

JWT (全称JSON WEB TOKEN)

1.导入jar包

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.6</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.6</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
    <version>0.12.6</version>
    <scope>runtime</scope>
</dependency>

 这个jar包包含了上面三个

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

2.官网(https://jwt.io/) 

 3.格式

  • 以json格式传输共享数据(分为三部分,最后三个部分组成的完整json会使用Base64进行编码且以.分割

    • Header(头)

      • 用于记录令牌类型以及签名算法等信息

    • PayLoad(载荷)

      • 存储Http共享数据

    • Signature(签名)

      • 使用加密算法保证Header与PayLoad的安全性

 4.如何使用jwt(生成,校验)

package com.lu.tlias84;


import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.junit.jupiter.api.Test;

import javax.xml.crypto.Data;
import java.util.Date;


public class TestJwt {
    @Test
    public void testGenerateJwtToken(){

        //1. 构建jwt对象
        JwtBuilder builder = Jwts.builder();
        //2. 指定header(一般不指定)
        //省略使用默认的jwt头
        //3. 往payload放置http需要共享的信息,并且指定过期时间
        builder.claim("username","count-l");
        builder.claim("password","count-l");
        Date date = new Date();
        builder.issuedAt(date);//token生效时间
        builder.expiration(new Date(date.getTime()+60*1000));//指定过期时间
        //4. 进行数字签名
        String key = "febaa5b6-b818-440a-ad8a-9606ffaafd0c";
        //第一个参数是数字签名(需要自己生成)
        //第二个参数 加密的签名算法,新版需要保证一定强度(HS256至少要32字节,HS384至少需要84字节,HS512至少要64字节)
        builder.signWith(Keys.hmacShaKeyFor(key.getBytes()), Jwts.SIG.HS256);
        //生成token
        String compact = builder.compact();
        System.out.println(compact);
    }
    @Test
    public void testVerifyJwtToken(){
        String key = "febaa5b6-b818-440a-ad8a-9606ffaafd0c";
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImNvdW50LWwiLCJwYXNzd29yZCI6ImNvdW50LWwiLCJpYXQiOjE3MjQzMTUyODUsImV4cCI6MTcyNDMxNTM0NX0.olJzzamV5Pi5qdKCU3p0F19SX_vaD2rSkiLXfrxenVo";
        //1.构建jwt解析对象
        JwtParserBuilder parser = Jwts.parser();
        //2.指定数字签名
        JwtParser build = parser.verifyWith(Keys.hmacShaKeyFor(key.getBytes())).build();
        //3.指定token
        Claims payload = build.parseSignedClaims(token).getPayload();
        System.out.println(payload.get("username"));
        System.out.println(payload.get("password"));
    }
}

 解析

public void testVerifyJwtToken(){
    String key = "febaa5b6-b818-440a-ad8a-9606ffaafd0c";
    String token = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImNvdW50LWwiLCJwYXNzd29yZCI6ImNvdW50LWwifQ.0HgzrDeIXEOixSV9-08lCLx0sX6m--3ShxYYUpJKhz4";
    //1.构建jwt解析对象
    JwtParserBuilder parser = Jwts.parser();
    //2.指定数字签名
    JwtParser build = parser.verifyWith(Keys.hmacShaKeyFor(key.getBytes())).build();
    //3.指定token
    Claims payload = build.parseSignedClaims(token).getPayload();
    System.out.println(payload.get("username"));
    System.out.println(payload.get("password"));
}

JWT工具类

package com.lu.tlias84.utils;

import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;

import java.util.Date;

/**
 * jwt token 生成与验证(解析)
 */
public class JwtUtil {
    private JwtUtil(){}
    private static final String SIG = "febaa5b6-b818-440a-ad8a-9606ffaafd0c";

    /**
     * 生成token
     * @param username payload-用户名
     * @param password payload-密码
     * @param minutes token过期时间,以分钟为单位
     * @return token
     */
    public static String generateToken(String username,String password,long minutes){
        //1.构建jwt对象
        //2.指定header(一般省略)
        //3.放置payload,以及过期时间和生效时间
        //4.进行数字签名
        //第一个参数是数字签名(需要自己生成)
        //第二个参数 加密的签名算法,新版需要保证一定强度(HS256至少要32字节,HS384至少需要84字节,HS512至少要64字节)
        //5.生成token
        Date now = new Date();
        String token = Jwts.builder()
                .claim("username", username)
                .claim("password", password)
                .issuedAt(now)
                .expiration(new Date(now.getTime() + 60 * 1000 * minutes))
                .signWith(Keys.hmacShaKeyFor(SIG.getBytes()), Jwts.SIG.HS256)
                .compact();
        return token;
    }

    /**
     * 验证token
     * @param token 生成的令牌
     * @return payload中的用户信息
     */
    public static JSONObject verifyToken(String token){
        //1.构建解析对象
        //2.指定数字签名
        //3.指定被签名后的token
        JwtParser parser = Jwts.parser()
                .verifyWith(Keys.hmacShaKeyFor(SIG.getBytes())).build();
        Claims payload = parser.parseSignedClaims(token).getPayload();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", payload.get("username"));
        jsonObject.put("password", payload.get("password"));
        //TODO: 还没有完成token失效的处理以及 token字符串不和法的处理
        return jsonObject;
    }
}

使用

package com.lu.tlias84.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.lu.tlias84.entity.Emp;
import com.lu.tlias84.mapper.EmpMapper;
import com.lu.tlias84.po.LoginParam;
import com.lu.tlias84.service.LoginService;
import com.lu.tlias84.utils.JwtUtil;
import com.lu.tlias84.utils.ResultUtil;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service
public class LoginServiceImpl implements LoginService {
    @Resource
    private EmpMapper empMapper;

    @Override
    public ResultUtil login(LoginParam loginParam) {
        Emp emp = empMapper.selectEmpByUsername(loginParam.getUsername());
        if (Objects.isNull(emp) || !emp.getPassword().equals(loginParam.getPassword())) {
            return ResultUtil.fail("用户名或者密码不正确");
        }
        String token = JwtUtil.generateToken(loginParam.getUsername(), loginParam.getPassword(), 30);
        JSONObject json = new JSONObject();
        json.put("id", emp.getId());
        json.put("username", emp.getUsername());
        json.put("password", emp.getPassword());
        json.put("token", token);
        return ResultUtil.success(json);
    }
}

过滤器 

1.filter

package com.lu.tlias84.filter;

import com.alibaba.fastjson.JSONObject;
import com.lu.tlias84.entity.Emp;
import com.lu.tlias84.mapper.EmpMapper;
import com.lu.tlias84.utils.JwtUtil;
import com.lu.tlias84.utils.ResultUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;

/**
 * 登录过滤器,进行登录token拦截
 */
@Slf4j
//@Component
public class LoginFilter implements Filter {
    @Resource
    private EmpMapper empMapper;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        log.info("初始化过滤器");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("开始执行http请求过滤");
        //参数向下转型成HttpServletRequest以及HttpServletResponse
        HttpServletRequest sRequest = (HttpServletRequest) request;
        HttpServletResponse sResponse = (HttpServletResponse) response;
        //放行登录接口
        String uri = sRequest.getRequestURI();
        if (uri.contains("login")) {
            chain.doFilter(request, response);
            return;
        }
        //获取请求头中的token
        String token = sRequest.getHeader("token");
        //判断是否有token->是否绕过登录
        if (Objects.isNull(token)) {
            writeErrorResponse(sResponse,"请先登录");
            return;
        }
        //校验token是否过期或者错误
        JSONObject jsonObject =null;
        try {
            jsonObject = JwtUtil.verifyToken(token);
        } catch (Exception e) {
            writeErrorResponse(sResponse,"token不合法");
            return;
        }
        //TODO: 使用jwt工具类验证token中的数据是否正确
        Emp emp = empMapper.selectEmpByUsername(jsonObject.getString("username"));
        if (Objects.isNull(emp) || !emp.getPassword().equals(jsonObject.getString("password"))) {
            writeErrorResponse(sResponse,"校验不正确");
            return;
        }

        //过滤链对象执行完过滤逻辑之后放行http请求,把request,response重新交给controller(servlet)
        chain.doFilter(request, response);
        log.info("过滤完成");
    }

    private static void writeErrorResponse(HttpServletResponse sResponse,String msg) throws IOException {
        //设置UTF-8编码
        sResponse.setContentType("application/json;charset=utf-8");
        sResponse.setCharacterEncoding("UTF-8");
        //设置响应状态码
        sResponse.setStatus(HttpStatus.SC_UNAUTHORIZED);
        PrintWriter writer = sResponse.getWriter();
        ResultUtil fail = ResultUtil.fail(msg);
        //使用fastjson把对象转成json字符串
        writer.write(JSONObject.toJSONString(fail));
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

2.流程图 

拦截器 

1.SpringBoot2.x

编写拦截器

实现HandlerInterceptorAdapter接口

配置拦截器

继承WebMvcConfigurer类器

2.SpringBoot3.x

编写拦截器

实现Handlerlnterceptor接口

package com.lu.tlias84.Interceptor;

import com.alibaba.fastjson.JSONObject;
import com.lu.tlias84.entity.Emp;
import com.lu.tlias84.mapper.EmpMapper;
import com.lu.tlias84.utils.JwtUtil;
import com.lu.tlias84.utils.ResultUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;

/**
 * springboot提供的登录拦截器
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Resource
    private EmpMapper empMapper;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        if (Objects.isNull(token)) {
            writeErrorResponse(response,"请先登录");
            return false;
        }
        JSONObject jsonObject =null;
        try {
            jsonObject = JwtUtil.verifyToken(token);
        } catch (Exception e) {
            writeErrorResponse(response,"token不合法");
            return false;
        }
        Emp emp = empMapper.selectEmpByUsername(jsonObject.getString("username"));
        if (Objects.isNull(emp) || !emp.getPassword().equals(jsonObject.getString("password"))) {
            writeErrorResponse(response,"校验不正确");
            return false;
        }
        return true;
    }
    private static void writeErrorResponse(HttpServletResponse sResponse,String msg) throws IOException {
        //设置UTF-8编码
        sResponse.setContentType("application/json;charset=utf-8");
        sResponse.setCharacterEncoding("UTF-8");
        //设置响应状态码
        sResponse.setStatus(HttpStatus.SC_UNAUTHORIZED);
        PrintWriter writer = sResponse.getWriter();
        ResultUtil fail = ResultUtil.fail(msg);
        //使用fastjson把对象转成json字符串
        writer.write(JSONObject.toJSONString(fail));
    }
}

配置拦截器

继承WebMvcConfigurationSupport类

package com.lu.tlias84.config;

import com.lu.tlias84.Interceptor.LoginInterceptor;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * springboot配置类
 * 一般用来配置拦截器或者跨域配置。。
 */
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Resource
    LoginInterceptor loginInterceptor;

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        //添加拦截器
        registry.addInterceptor(loginInterceptor)
                //添加拦截路径
                .addPathPatterns("/**")
                //排除路劲
                .excludePathPatterns("/login");
    }
}

面试题

1.过滤器和拦截器有什么区别

过滤器(filter) 与拦截器(Interceptor)的区别:
相同点:执行都在controller(servlet)之前
不同点:
     1.技术提供方:过滤器->servlet提供 拦截器 ->spring
     2.执行顺序:先执行过滤器 后执行拦截器
     3.拦截力度:过滤器拦截力度粗 ->编码转换,时间格式转化
                          拦截器拦截力度细 ->登录认证,接口日志记录

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

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

相关文章

大模型学习笔记 - LLM 之 LLaMA系列(待更新)

LLaMA 系列 LLaMA 概述 LLaMA-1LLaMA-2LLaMA-3 LLaMA 概述 LLaMA: Open and Efficient Foundation Language Models Llama 2: Open Foundation and Fine-Tuned Chat Models (LLama2 & LLama2-Chat) LLama 3 | LLama 3.1 LLaMA-1 涉及到的基础知识点: pre-normalizat…

Leetcode面试经典150题-25.K个一组反转链表

解法都在代码里&#xff0c;不懂就留言或者私信 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val …

基于java图书销售管理系统设计与实现

1引言 随着Internet国际互联网的发展&#xff0c;越来越多的企业开始建造自己的网站。基于Internet的信息服务&#xff0c;商务服务已经成为现代企业一项不可缺少的内容。很多企业都已不满足于建立一个简单的仅仅能够发布信息的静态网站。现代企业需要的是一个功能强大的&…

利用API返回值实现商品信息的自动化更新

利用API返回值实现商品信息的自动化更新是一个涉及到数据交互、数据处理和自动化脚本编写的任务。以下是一个基本的步骤指南&#xff0c;帮助你实现这一过程&#xff1a; 1. 确定API接口 首先&#xff0c;你需要确定能够提供商品信息的API接口。这通常是由商品数据提供商&…

2024年培训服务行业CRM研究报告

国家实行“双减”政策后&#xff0c;很多教培企业慢慢淡出了公众的视野&#xff0c;很多人觉得&#xff0c;教培行业怕是要日薄西山了。 但如果你了解了培训服务行业到底有多庞大&#xff0c;你就会立即改变自己的想法。 从2017年起&#xff0c;中国职业培训机构的年注册量超…

X86架构基础

X86目前的架构有32位和64位两种&#xff0c;不同的架构支持的运行模式也是不一样的&#xff0c;64位的基本能兼容32位。64位是X86架构的主流&#xff0c;本文内容默认以64位位基础。X86平台目前的支持的运行模式有以下几种&#xff1a; 1、实模式&#xff1a;这是最早的X86运行…

ue5远程渲染和本地渲染的区别,及云渲染的联系

UE5这款引擎以其令人惊叹的渲染能力&#xff0c;为游戏开发者们打开了一扇通往视觉盛宴的大门。但是在UE5的世界里&#xff0c;渲染技术其实还有着本地渲染和远程渲染之分&#xff0c;而且它们与时下大热的云渲染技术也有着千丝万缕的联系。本文主要说明UE5中的远程渲染和本地渲…

鲜为人知的 9 种人工智能工具

这套 AI 工具确实与众不同。您可能在其他地方读到过&#xff0c;图像生成需要大量工作。使用 AI 工具制作网站也需要大量工作。此列表涵盖了我在过去几个月发现的一些前沿项目&#xff0c;它们在很多方面都很有用。 如果您没有时间阅读最新的营销书籍&#xff0c;SoBrief 是个不…

Semantic Kernel进阶:多模型的支持

大家可能已经知道&#xff0c;Semantic Kernel默认主要支持两款模型&#xff1a;OpenAI和AzureOpenAI。对于开发者来说&#xff0c;这显然是不够的&#xff0c;尤其是当我们希望对接国内的一些强大模型&#xff0c;比如百度的文心一言、阿里的通义千问、搜狗的百川、智谱ChatGL…

kickstart自动安装脚本制作详解

一、kickstart自动安装脚本制作 此实验中&#xff0c;使用Rhel7.9&#xff0c;并开启图形化系统 1.设置实验环境 1.使用Rhel7.9 2.需要打开图形化系统 [rootpxe ~]# hostnamectl # 查看当前系统是否为图形化Static hostname: pxeIcon name: com…

数字赋能下的艺术蝶变:沃可趣如何重塑乐园演艺人才培训?

全球知名主题乐园在2023年共接待游客超过1300万人次。 这意味着&#xff0c;在童话世界里创造快乐的演职人员们&#xff0c;平均每天要与三、四万人见面&#xff0c;以精湛的演技服务好每一个人&#xff0c;其工作难度和强度不言而喻。 减轻员工负担&#xff0c;帮助员工成长…

江协科技STM32学习- P4 新建工程

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

大语言模型超参数调整指南:入门调参的实用手册

在人工智能的广阔天地中&#xff0c;大语言模型&#xff08;LLM&#xff09;正以其强大的能力&#xff0c;不断刷新我们对机器理解语言的认知。然而&#xff0c;要使这些模型在特定应用场景下发挥最大效能&#xff0c;关键在于如何巧妙地调整其超参数。本文将带你深入探索 LLM …

4. 数组与集合

数据结构是管理和组织数据的基础&#xff0c;它直接影响到程序的性能和效率。在本章中&#xff0c;我们将深入探讨与数组和集合相关的知识。这些数据结构在Java编程中至关重要&#xff0c;无论是处理简单的线性数据还是复杂的多维数据&#xff0c;合理使用这些结构都能大大提高…

100个智能体实战技巧 | 如何让工作流也能处理图片

相信不少朋友都遇到过想要在工作流中处理图片但是却无从下手的情况 举个例子&#xff0c;扣子中有个插件叫OCR&#xff0c;是可以用来识别图中的文字的 然而作为一个插件&#xff0c;它只能在工作流中被调用&#xff0c;如下图 工作流 vs. 图像流 这就意味着&#xff0c;要使用…

Modal中的跳转用<Link>组件会报错?

在做链接跳转时&#xff0c;一般是用a标签或者link标签。但是当团队规范使用标签时&#xff0c;在modal&#xff08;antd的版本4&#xff09;中使用可能就有问题了。 报错内容是&#xff0c;发现link在使用时找不到路由上下文。因此报错。 原因&#xff1a;Link 组件在 return …

Vue3-响应式原理解析

vue3 与 vue2 主要差异之一无疑是响应式实现上的改变。本文主要阐述响应式原理的实现方式解析以及核心源码阅读的注释理解。 本文主要对响应式实现原理进行逻辑梳理,舍弃枯燥无味的代码,只用图解/文字进行功能描述,具体实现请自行阅读。保重!!! 如果问题,虚心求教,还请…

xxl_job任务调度简单使用

一、概念 任务调度是为了自动完成特定任务&#xff0c;在约定的特定时刻去执行任务的过程 如以下应用场景&#xff1a; 某电商平台需要每天上午10点&#xff0c;下午3点&#xff0c;晚上8点发放一批优惠券 某银行系统需要在信用卡到期还款日的前三天进行短信提醒 某财务系统…

UEditor百度富文本后端上传文件接口

UEditor百度富文本后端上传文件接口 直接上代码 接口&#xff1a; RequestMapping("/UEditorConfig")public String list(HttpServletRequest request, HttpServletResponse response) throws IOException {String config environment.getProperty("ueditor.c…

60%公司推行精益管理失败都源于同一原因,这个原因是...

精益管理在许多公司中已经成为提高运营效率、减少浪费、和提升客户满意度的重要方法。或者你觉得惊讶&#xff0c;根据我们的经验&#xff0c;超过60%的公司在实施精益管理失败&#xff0c;我们发现他们都有一个共同的原因&#xff0c;这个原因就是公司没有跟踪正确的指标&…