SpringBoot整合JWT实现token验证_springboot jwt token_藤井大叔的博客-程序员宅基地

技术标签: jwt  SpringBoot  

介绍

在这里插入图片描述
jwt官网:https://jwt.io/

什么是JWT

在这里插入图片描述

如上是官网的介绍,这里我说一下大概的意思

JWT,拆分来理解就是JSON Web Token的意思,是一种开放标准。
它定义了一种紧凑并、包含的方式。
以JSON格式的形式在各方面安全的传输信息
因为它由数字签名,所以可以对其进行验证与信任
jwt可以使用秘密(使用HMAC算法)签名,也可以使用RSA或ECDSA公钥/私钥对签名

JWT的应用场景

在这里插入图片描述

  1. 授权
    这是使用JWT最常见的场景。一旦用户登录,每个后续请求将包括JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是目前JWT广泛使用的一个特性,因为它的开销很小,而且可以轻松地跨不同的领域使用。

  2. 信息交换
    JSON Web令牌是在各方之间安全地传输信息的一种好方法。因为jwt可以签名(例如,使用公钥/私钥对),所以可以确定发送方就是他们所说的那个人。此外,由于使用头和有效负载计算签名,您还可以验证内容没有被篡改。

JWT结构

在紧凑的形式下,JSON Web令牌由三个由点(.)分隔的部分组成。
主要组成由

头
有效载荷
签名

报头通常由两部分组成:令牌的类型,即JWT,以及使用的签名算法,如HMAC SHA256或RSA。

{
    
  "alg": "HS256",
  "typ": "JWT"
}

然后,该JSON是Base64Url编码,以形成JWT的第一部分。

有效载荷

令牌的第二部分是有效负载,其中包含声明。声明是关于实体(通常是用户)和其他数据的语句。索赔有三种类型:注册索赔、公共索赔和私人索赔

签名

要创建签名部分,您必须获取编码的头部、编码的有效载荷、一个秘密、头部中指定的算法,并对其进行签名。

例如,如果使用HMAC SHA256算法,将按照如下方式创建签名:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

签名用于验证消息没有在此过程中被更改,并且,对于使用私钥签名的令牌,它还可以验证JwT的发送方是它所说的那个人。

工作原理

在身份验证中,当用户使用其凭证成功登录时,将返回一个JSON Web令牌。由于令牌是凭据,必须非常小心地防止安全问题。一般来说,不应该将令牌保存的时间超过所需的时间。由于缺乏安全性,也不应该在浏览器存储中存储敏感的会话数据。

当用户希望访问受保护的路由或资源时,用户代理应该发送JWT,通常是在使用承载模式的授权头中。标题的内容应该如下所示:

Authorization: Bearer <token>

流程图
在这里插入图片描述

认证流程
1.访问者在web前端提供用户访问信息,接着根据确认按钮将信息返回给后端
api,提交的过程一般是(http/https)POST请求,建议使用通过SSL加密传输
协议(https post)方式提交,从而避免敏感信息被嗅探

后端将前端传输过来的信息核对正确后,将用户的信息作为jwt payload(负载)
将其与头部进行Base64编码后拼接签名,形成jwt,例子如下
lll.xxx.zzz

jwt使用拦截器将拦截相关地址,通过业务逻辑进入对应的数据展示,错误则返回至登录页面

前端每次请求将iwt放至http head中的authorization位,解决了xss与xsrf问题

JWT使用

引入依赖

<dependency>
   <groupId>com.auth0</groupId>
   <artifactId>java-jwt</artifactId>
   <version>3.4.0</version>
</dependency>

生成token

package com.uncletj.fujii.demo;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import sun.misc.Cleaner;

import java.util.Calendar;
import java.util.HashMap;

/**
 * @Author Fujii Uncle
 * @Date 2021-08-08 22:29
 * @Version SpringBoot 2.2.2
 * @projectName token的生成
 */
public class JwtDemo {
    

    /**
     * 密钥:可以随意声明
     * 当然可以在方法内直接输入,但是我们使用变量声明更好找到去更改
     */
    private static final String sign = "!@#QFHF15Q.*";

    public static void main(String[] args) {
    
        HashMap<String,Object> map = new HashMap<>();

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,20);

        String jwt = JWT.create()
                .withHeader(map)//header
                .withClaim("username","Fujii Uncle")//payload
                .withClaim("password","123456")
                .withExpiresAt(instance.getTime())//指定过期时间
                .sign(Algorithm.HMAC256(sign));//签名

        //打印token
        System.out.println("打印token:"+jwt);
    }
}

执行结果
在这里插入图片描述

token验证

package com.uncletj.fujii.demo;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

/**
 * @Author Fujii uncle
 * @Date 2021-08-08 22:45
 * @Version SpringBoot 2.2.2
 * @projectName token验证
 */
public class VerifyDemo {
    

    public static void main(String[] args) {
    
        //创建验证对象
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("!@#QFHF15Q.*")).build();
        DecodedJWT jwt = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsImV4cCI6MTYyODQzNDQ0NywidXNlcm5hbWUiOiJGdWppaSBVbmNsZSJ9.dUj-bMgoQvSNNaKxnzTxjL52mI83xNrN2kMLeT9mnSI");
        //获取信息
        System.out.println("用户名:"+jwt.getClaim("username").asString());
        System.out.println("密码:"+jwt.getClaim("password").asString());
    }
}

过期异常,需在设置的时间内进行验证,否则token过期后无法验证
在这里插入图片描述
获取信息成功如下
在这里插入图片描述

封装工具类

从上面的案例得知我们在使用jwt主要是生成令牌以及基于令牌的验证与解密,在我们的web项目中也是根据获取到用户信息之后去生成令牌,在用户拿到令牌之后呢在去做相应的解析,这里我将这两部操作封装成一个工具类

package com.uncletj.fujii.demo;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

/**
 * @Date 2021/8/9 10:58
 * @Version SpringBoot 2.2.2
 * @packageName
 */
public class JwtUtils {
    

    private static final String sign = "!@QW#E$S7<G";


    /**
     * 生成token header.payload.sgin
     *
     */
    public static String getToken(Map<String,String>user){
    

        HashMap<String,Object> map = new HashMap<>();

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,120);

        //创建jwt builder
        JWTCreator.Builder builder = JWT.create();

        //payload
        user.forEach((k,v)->{
    
            builder.withClaim(k,v);
        });

        String token = JWT.create()
                .withHeader(map)//头部
                .withExpiresAt(instance.getTime())  //设置过期时间
                .sign(Algorithm.HMAC256(sign));//签名
        return token;
    }

    /**
     * 验证token
     */
    public static DecodedJWT verify(String token){
    
        DecodedJWT jwt = JWT.require(Algorithm.HMAC256(sign)).build().verify(token);
        return jwt;
    }
}

SpringBoot整合JWT的应用

依赖引入

		<!--spring web 组件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
		<!--mybatis plus 组件-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>

        <!--mybatis plus代码生成器 组件-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--spring boot 组件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

		<!--mysql 组件-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--lombok 组件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

配置文件

server:
  port: 8081

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/testdb?useUnicode=true&useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: root

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

编码

---------------------------------user实体类----------------------------------

package com.uncletj.fujii.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * @Date 2021/8/9 15:45
 * @Version SpringBoot 2.2.2
 * @packageName 用户实体类
 */
@Data
@TableName(value = "user")
@Accessors(chain = true)
public class User {
    

    @TableId(value = "id",type = IdType.AUTO)
    private long id;
    private String username;
    private String password;
}

----------------------------------------------------------------------------


---------------------------------user持久层----------------------------------

package com.uncletj.fujii.mapper;

import com.uncletj.fujii.entity.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * @Date 2021/8/9 15:47
 * @Version SpringBoot 2.2.2
 * @packageName 用户持久层
 */
@Mapper
public interface UserMapper {
    

    User login(String username,String password);
}

xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.uncletj.fujii.mapper.UserMapper">

    <select id="login" resultType="com.uncletj.fujii.entity.User">
        select * from user where username=#{
    username} and password=#{
    password}
    </select>

</mapper>
----------------------------------------------------------------------------


---------------------------------user业务层----------------------------------
service
package com.uncletj.fujii.service;

import com.uncletj.fujii.entity.User;

/**
 * @Date 2021/8/9 15:51
 * @Version SpringBoot 2.2.2
 * @packageName 用户业务层
 */
public interface UserService {
    

    User login(String username, String password);
}

serviceimpl
package com.uncletj.fujii.service.impl;

import com.uncletj.fujii.entity.User;
import com.uncletj.fujii.mapper.UserMapper;
import com.uncletj.fujii.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @Date 2021/8/9 15:52
 * @Version SpringBoot 2.2.2
 * @packageName
 */
@Service
public class UserServiceImpl implements UserService {
    

    @Resource
    private UserMapper userMapper;


    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public User login(String username, String password) {
    
        User user = userMapper.login(username,password);
        if (user!=null){
    
            return user;
        }
        throw new RuntimeException("用户名称或密码错误");
    }
}

---------------------------------user控制层----------------------------------
package com.uncletj.fujii.controller;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.uncletj.fujii.entity.User;
import com.uncletj.fujii.service.UserService;
import com.uncletj.fujii.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @Date 2021/8/9 15:55
 * @Version SpringBoot 2.2.2
 * @packageName 用户控制层
 */
@RestController
@Slf4j
@RequestMapping("/user")
public class LoginController {
    

    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public Map<String,Object> login(@RequestBody User user){
    
        log.info("用户名:"+user.getUsername());
        log.info("密码:"+user.getPassword());
        Map<String,Object> map = new HashMap<>();
        try {
    
            User userdb = userService.login(user.getUsername(),user.getPassword());
            //令牌生成
            Map<String,String> payload = new HashMap<>();
            payload.put("username",user.getUsername());
            payload.put("password",user.getPassword());
            String token = JwtUtils.getToken(payload);
            map.put("status",true);
            map.put("msg","认证成功");
            map.put("token",token);//token响应
        }catch (Exception e){
    
            map.put("status",false);
            map.put("msg","认证失败");
        }
        return map;
    }

    @GetMapping("/test")
    public Map<String,Object> getToken(String token){
    
        Map<String,Object> map = new HashMap<>();
        map.put("status",true);
        map.put("msg","请求成功");
        return map;

    }
}


----------------------------------------------------------------------------


---------------------------------user工具层----------------------------------
在如上工具层已展现
----------------------------------------------------------------------------

---------------------------------user拦截层----------------------------------

package com.uncletj.fujii.interceptors;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.uncletj.fujii.utils.JwtUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Date 2021/8/9 16:35
 * @Version SpringBoot 2.2.2
 * @packageName
 */
public class JWTInterceptors implements HandlerInterceptor {
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
        Map<String,Object> map = new HashMap<>();
        //获取请求头部令牌
        String token = request.getHeader("token");
        try {
    
            //验证令牌
            DecodedJWT decodedJWT = JwtUtils.verify(token);
            return true;
        }catch (SignatureVerificationException e){
    
            e.printStackTrace();
            map.put("msg","无效签名");
        }catch (TokenExpiredException e){
    
            e.printStackTrace();
            map.put("msg","token过期!");
        }catch (AlgorithmMismatchException e){
    
            e.printStackTrace();
            map.put("msg","token算法不一致!");
        }catch (Exception e){
    
            e.printStackTrace();
            map.put("msg","token无效!");
        }
        map.put("state",false); //状态

        //转json
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().println(json);
        return false;
    }
}

----------------------------------------------------------------------------

---------------------------------user配置层----------------------------------

package com.uncletj.fujii.config;

import com.uncletj.fujii.interceptors.JWTInterceptors;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Date 2021/8/9 16:42
 * @Version SpringBoot 2.2.2
 * @packageName
 */
@Configuration
public class InteceptorsConfig implements WebMvcConfigurer {
    

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
        registry.addInterceptor(new JWTInterceptors())
                .addPathPatterns("/user/test")  //需验证接口
                .excludePathPatterns("/user/login");//放行接口
    }
}

----------------------------------------------------------------------------

测试

根据以上编码得出以下测试结果,测试工具使用postman

用户登陆

在这里插入图片描述

其他数据信息

将用户登陆成功后的token放至header部,访问成功
在这里插入图片描述
结束语
若要前行,就要离开你现在停留的地方!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/UncleFujii/article/details/119500354

智能推荐

用git send-email发送patch-程序员宅基地

用 gmail 作为发送服务器。为了用git send-email通过gmail服务器发送patch。首先编辑 ~/.gitconfig , 写入你的账号配置[sendemail]smtpencryption = tlssmtpserver = smtp.gmail.comsmtpuser = [email protected] = 5

大数据——个性化推荐系统_大数据推荐系统-程序员宅基地

要知道什么是个性化推荐系统,那么就要先了解什么是推荐系统:什么是推荐系统推荐系统就是利用电子商务网站或APP向客户提供商品信息和建议,有意地引导用户的意向,帮助用户决定应该购买什么产品,模拟销售人员帮助客户完成整个购买过程。而个性化推荐系统是根据用户的兴趣特点和购买行为,向用户推荐用户感兴趣的信息和物品,使用户产生购买的意向。如果这些你对没有一个确切的概念,那么说购物网站上面每次所向你一些你..._大数据推荐系统

傅盛离职内情:从360叛将到腾讯马前卒-程序员宅基地

傅盛离职内情:从360叛将到腾讯马前卒投递人 itwriter发布于 2011-10-20 13:43评论(111)有14666人阅读原文链接 [收藏] « »  我是360的首席架构师李钊,傅盛的自述里谈到了我,涉及一些往事,我来说几句吧,关于傅盛、周鸿祎、雷军,还有马化腾之间那些不为人知的事情。  3Q大战的幕后总导演  先扯远点,抖个包袱吧。3Q

C# Forms.Timer无效,Tick无效_主界面invoke执行会使forms.timer不计时-程序员宅基地

今天遇到一个奇怪的问题,一个节目中,设置了两个渠道启动计时器(Forms.Timer),其中一个渠道计时器正常工作,另一个渠道计时器不工作。//按钮btnBedIn绑定的事件private void bedIn(object sender, EventArgs e){ //... timerTotalTime.Start();}//在独立线程中调用(无效,tim..._主界面invoke执行会使forms.timer不计时

如何自学游戏引擎的开发-程序员宅基地

如何自学游戏引擎的开发

一篇介绍k8s informer 机制的中文博客-程序员宅基地

https://www.kubernetes.org.cn/2693.html转载于:https://www.cnblogs.com/elnino/p/10195878.html

随便推点

刀片服务器的机箱显示器,思科 UCS 5100系列刀片服务器机箱-程序员宅基地

思科统一计算系统概述思科统一计算系统是下一代数据中心平台,在一个紧密结合的系统中整合了计算、网络、存储接入与虚拟化功能,旨在降低总体拥有成本(TCO),同时提高业务灵活性。该系统包含一个低延时无丢包万兆以太网统一网络阵列,以及多台企业级x86架构服务器。它是一个集成的可扩展多机箱平台,在统一的管理域中管理所有资源。(图1)。图1. 思科统一计算系统是一个紧密结合的高可用性架构产品概述Cisco ...

NGUI如何使用TweenScale使物体大小回到初始状态_ngui tween resettobeginning-程序员宅基地

NGUI如何使用TweenScale使物体大小回到初始状态gameObject.GetComponent().ResetToBeginning();_ngui tween resettobeginning

JavaScript 合并两个有序数组, 在有序数组中查找目标元素索引_let nums =[3, 6, 9, 12, 15, 18, 21, …, 195, 198];查-程序员宅基地

function merge(nums1, nums2) { var len1 = nums1.length - 1; var len2 = nums2.length - 1; var len = nums1.length + nums2.length - 1; while (len1 >= 0 && len2 >= 0) { nums1[l..._let nums =[3, 6, 9, 12, 15, 18, 21, …, 195, 198];查找72大的索引

centos ll 命令提示Input/output error-程序员宅基地

在内网一台服务器上面操作,忽然敲打一个ll命令提示Input/output errorLs,pwd常用命令都报这个错;ssh -v [email protected]_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017debug1: Reading configuration data /home/common/.ssh/conf..._ll 输入输出错误

win10 VS2017 安装OpenSSL_vs2017如何安裝openssl-程序员宅基地

安装openssl走了很多弯路,记录一下。1、下载原材料:openssl:https://www.openssl.org/source/vs2017:https://www.visualstudio.com/zh-hans/downloads/nasm:http://www.nasm.us/因为报错nasm not found,下了nasm,它是一个汇编语言编译程序。2、环境变量: 添加N_vs2017如何安裝openssl

51单片机项目设计——价格猜猜猜_单片机如何计算不同商品组合的价格呢-程序员宅基地

一、设计目的目标1、通过PC机发送数据到STC板上2、通过按下K2或使振动传感器受到震动开始游戏3、通过导航按键上(下)键调大(小)数字,并显示在数码管上4、通过按下K1确认数据5、通过LED灯表示显示所猜数据与实际数据的大小关系6、当猜对时蜂鸣器播放一段音乐,代表可以把该价格的物品带回家7、在数码管最后一位显示所剩下的猜数机会,当3次都没猜对时,数码管跳零报错,按下K..._单片机如何计算不同商品组合的价格呢

推荐文章

热门文章

相关标签