SpringBoot整合JWT实现token验证

介绍

在这里插入图片描述
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部,访问成功
在这里插入图片描述
结束语
若要前行,就要离开你现在停留的地方!

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

藤井大叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值