什么是JWT

  Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

为什么使用JWT

  1. 简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快。
  2. 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库。
  3. 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
  4. 不需要在服务端保存会话信息,特别适用于分布式微服务。

JWT的使用场景

  身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。

JWT的结构

  • 头部(包含令牌的类型与使用的签名算法)
{
"alg": "HS265",
"typ": "JWT"
}
  • 载荷(有关用户实体和其他数据的声明)
{
"name": "admin",
"pass": 123
}
  • 签证(使用编码后的header和payload以及一个指定密钥,然后使用header中指定的算法(HS265)进行签名.
    签名的作用是保证JWT没有被篡改过)

JWT的请求流程

SpringBoot整合JWT

引入依赖

     <!--引入jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency> <!--引入mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency> <!--引入mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency> 

编写配置文件

server.port=8989

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://8.192.12.37:3306/test?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root mybatis.type-aliases-package=com.boot.jwt.entity
mybatis.mapper-locations=com/boot/jwt/mapper/*.xml logging.level.com.baizhi.dao=debug 

token生成与验证工具类

package com.boot.jwt.config;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.boot.jwt.entity.User; import java.util.Date; /**
* @author laz
* @date 2022/09/09 14:55
*/
public class TokenUtil { //token到期时间60s
private static final long EXPIRE_TIME= 60*1000;
//密钥盐
private static final String TOKEN_SECRET="123456qwertyuiop789"; /**
* 创建一个token
* @param user
* @return
*/
public static String sign(User user){
String token=null;
try {
Date expireAt=new Date(System.currentTimeMillis()+EXPIRE_TIME);
token = JWT.create()
//发行人
.withIssuer("auth0")
//存放数据
.withClaim("username",user.getUsername())
.withClaim("password",user.getPassword())
//过期时间
.withExpiresAt(expireAt)
.sign(Algorithm.HMAC256(TOKEN_SECRET));
} catch (IllegalArgumentException|JWTCreationException je) { }
return token;
}
/**
* 对token进行验证
* @param token
* @return
*/
public static Boolean verify(String token){
try {
//创建token验证器
JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
DecodedJWT decodedJWT=jwtVerifier.verify(token);
System.out.println("认证通过:");
System.out.println("username: " + TokenUtil.getUserName(token));
System.out.println("过期时间: " + decodedJWT.getExpiresAt());
} catch (IllegalArgumentException |JWTVerificationException e) {
//抛出错误即为验证不通过
return false;
}
return true;
} /**
* 获取用户名
*/
public static String getUserName(String token){
try{
DecodedJWT jwt=JWT.decode(token);
return jwt.getClaim("username").asString();
}catch (JWTDecodeException e)
{
return null;
}
}
}

拦截器拦截token

package com.boot.jwt.config;

import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* @author laz
* @date 2022/09/09 14:56
*/
@Component
public class TokenInterceptor implements HandlerInterceptor { @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //跨域请求会首先发一个option请求,直接返回正常状态并通过拦截器
if(request.getMethod().equals("OPTIONS")){
response.setStatus(HttpServletResponse.SC_OK);
return true;
} //获取到token
String token = request.getHeader("token");
if (token!=null){
boolean result= TokenUtil.verify(token);
if (result){
System.out.println("通过拦截器");
return true;
}
}
try {
JSONObject json=new JSONObject();
json.put("msg","token verify fail");
json.put("code","500");
response.getWriter().append(json.toString());
System.out.println("认证失败,未通过拦截器");
} catch (Exception e) {
return false;
}
return false;
} }

设置拦截规则

package com.boot.jwt.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.ArrayList;
import java.util.List; /**
* @author laz
* @date 2022/09/09 13:56
*/
@Configuration
public class WebConfiguration implements WebMvcConfigurer { @Autowired
private TokenInterceptor tokenInterceptor; /**
* 配置拦截器、拦截路径
* 每次请求到拦截的路径,就会去执行拦截器中的方法
* @param
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
List<String> excludePath = new ArrayList<>();
//排除拦截,除了登录,其他都拦截
excludePath.add("/test/login");
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(excludePath);
WebMvcConfigurer.super.addInterceptors(registry); } }

建立User实体类

package com.boot.jwt.entity;

import lombok.Data;

/**
* @author laz
* @date 2022/09/09 14:54
*/
@Data
public class User {
private String id;
private String username;
private String password;
}

编写mapper和xml文件

package com.boot.jwt.mapper;

import com.boot.jwt.entity.User;

/**
* @author laz
* @date 2022/09/09 14:54
*/
public interface UserMapper { /**
* 登录
* @param user
* @return
*/
User login(User user);
}

  

<?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.boot.jwt.mapper.UserMapper"> <select id="login" parameterType="com.boot.jwt.entity.User" resultType="com.boot.jwt.entity.User">
select * from user where username=#{username} and password = #{password}
</select> </mapper> 

编写Service层接口以及实现类

package com.boot.jwt.service;

import com.boot.jwt.entity.User;
import com.boot.jwt.utils.LoginDto; /**
* @author laz
* @date 2022/09/09 14:54
*/
public interface IUserService { /**
* 登录接口
* @param user
* @return
*/
LoginDto login(User user); }

  

package com.boot.jwt.service.impl;

import com.boot.jwt.config.TokenUtil;
import com.boot.jwt.entity.User;
import com.boot.jwt.mapper.UserMapper;
import com.boot.jwt.service.IUserService;
import com.boot.jwt.utils.LoginDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; /**
* @author laz
* @date 2022/09/09 14:54
*/
@Service
public class IUserServiceImpl implements IUserService { @Autowired
private UserMapper userMapper; @Override
public LoginDto login(User user) {
LoginDto loginDto = new LoginDto();
User login = userMapper.login(user); if (login == null){
loginDto.setCode(400);
loginDto.setMsg("账号或密码错误!");
return loginDto;
}
String token= TokenUtil.sign(login); loginDto.setCode(200);
loginDto.setMsg("登录成功!");
loginDto.setUser(login);
loginDto.setToken(token);
return loginDto;
}
}

编写controller层测试接口

package com.boot.jwt.controller;

import com.boot.jwt.entity.User;
import com.boot.jwt.service.IUserService;
import com.boot.jwt.utils.LoginDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* @author laz
* @date 2022/09/09 14:58
*/
@RestController
@RequestMapping("/test")
public class LoginController { @Autowired
private IUserService userService; /**
* 登录
* @param user
* @return
*/
@PostMapping("/login")
public LoginDto login(@RequestBody User user){
LoginDto login = userService.login(user);
return login;
} /**
* 测试
* @return
*/
@RequestMapping("/test")
public Object test(){
return "访问成功!";
} }

到这里,代码就编写完成了,下面开始测试。

测试JWT

先调用测试接口

可以看到,接口被拦截了。

接下来调用login接口,获取token:

再次调用测试接口,带上token:

可以看到,接口成功访问。

SpringBoot整合JWT实现登录认证的更多相关文章

  1. springboot系列(十)springboot整合shiro实现登录认证

    关于shiro的概念和知识本篇不做详细介绍,但是shiro的概念还是需要做做功课的要不无法理解它的运作原理就无法理解使用shiro: 本篇主要讲解如何使用shiro实现登录认证,下篇讲解使用shiro ...

  2. SpringBoot 整合 Shiro 密码登录与邮件验证码登录(多 Realm 认证)

    导入依赖(pom.xml)  <!--整合Shiro安全框架--> <dependency> <groupId>org.apache.shiro</group ...

  3. 厉害!我带的实习生仅用四步就整合好SpringSecurity+JWT实现登录认证!

    小二是新来的实习生,作为技术 leader,我还是很负责任的,有什么锅都想甩给他,啊,不,一不小心怎么把心里话全说出来了呢?重来! 小二是新来的实习生,作为技术 leader,我还是很负责任的,有什么 ...

  4. SpringBoot集成JWT实现权限认证

    目录 一.JWT认证流程 二.SpringBoot整合JWT 三.测试 上一篇文章<一分钟带你了解JWT认证!>介绍了JWT的组成和认证原理,本文将介绍下SpringBoot整合JWT实现 ...

  5. 【项目实践】一文带你搞定Session和JWT的登录认证方式

    以项目驱动学习,以实践检验真知 前言 登录认证,估计是所有系统中最常见的功能了,并且也是最基础.最重要的功能.为了做好这一块而诞生了许多安全框架,比如最常见的Shiro.Spring Security ...

  6. 教你 Shiro + SpringBoot 整合 JWT

    本篇文章将教大家在 shiro + springBoot 的基础上整合 JWT (JSON Web Token) 如果对 shiro 如何整合 springBoot 还不了解的可以先去看我的上一篇文章 ...

  7. SpringBoot整合Shiro 四:认证+授权

    搭建环境见: SpringBoot整合Shiro 一:搭建环境 shiro配置类见: SpringBoot整合Shiro 二:Shiro配置类 shiro整合Mybatis见:SpringBoot整合 ...

  8. spring-boot整合shiro作权限认证

    spring-shiro属于轻量级权限框架,即使spring-security更新换代,市场上大多数企业还是选择shiro 废话不多说  引入pom文件 <!--shiro集成spring--& ...

  9. JWT实现登录认证实例

    JWT全称JSON Web Token,是一个紧凑的,自包含的,安全的信息交换协议.JWT有很多方面的应用,例如权限认证,信息交换等.本文将简单介绍JWT登录权限认证的一个实例操作. JWT组成 JW ...

随机推荐

  1. 5.RDD操作综合实例

    一.词频统计 A. 分步骤实现 1.准备文件 (1)下载小说或长篇新闻稿 (2)上传到hdfs上 2.读文件创建RDD 3.分词 4. ·排除大小写lower(),map() ·标点符号re.spli ...

  2. 图扑 Web 可视化引擎在仿真分析领域的应用

    ​ 前言 在数字孪生和仿真研究过程中,会产生大量和三维空间相关的数值信息,比如设备外观的扫描数据.地形扫描数据.生产设备温度场/压力场.流体的速度场.流体扩散,以及各种仿真数据:速度,压力,应力,温度 ...

  3. dfs-1756:八皇后及1700:八皇后问题

    总时间限制: 1000ms 内存限制: 65536kB 描述 会下国际象棋的人都很清楚:皇后可以在横.竖.斜线上不限步数地吃掉其他棋子.如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被 ...

  4. 淘淘蓝蓝的CSP-S神妙膜你赛2-淘淘蓝蓝喜欢01串 题解

    问题简述 给定\(n\)个盒子,每个盒子的容器为\(b[i]\),里面装有\(a[i]\)个物品.今有\(q\)组询问,每组询问给出一个正整数\(k(k<=n)\),已知一个盒子里的一件物品转移 ...

  5. MPI学习笔记(二):矩阵相乘的两种实现方法

    mpi矩阵乘法(C=αAB+βC) 最近领导让把之前安装的软件lapack.blas里的dgemm运算提取出来独立作为一套程序,然后把这段程序改为并行的,并测试一下进程规模扩展到128时的并行效率.  ...

  6. 迭代器和增强for循环

    iterator 迭代:即Collection集合元素的通过获取方法,在获取元素之前先要判断集合中有没有元素,如果有就把这个元素取出来,然后在判断,如果还有就再去除卡u,一直把集合中的所有元素全部拿出 ...

  7. 标准的Switch语句和穿透的Switch语句

    第三章 选择语句 3.1选择语句--Switch switch语句格式: ```java switch(表达式){ case 常量值1: 语句体1; break; case 常量值2: 语句体2; b ...

  8. 可以级联的以太网远程IO模块的优点与适用场景

    可以级联的以太网远程IO模块的优点与具体的适用场景 对于数据采集控制点是按照线性分布的场景,比如智慧园区的路灯.桥梁.路灯.数字化工厂.停车场车位监测.智慧停车场.智能停车架.楼宇自动控制系统等场景, ...

  9. PHP,javascript实现大文件上传

    HTML代码 <!doctype html> <html lang="en"> <head> <meta charset="UT ...

  10. How to code like a pro in 2022 and avoid If-Else

    在浏览文章的时候发现了一篇叙述有关if-else语句的文章,这篇文章作者是Thai Tran,他原文是用英语写的,然后看着文章浅显易懂,便尝试翻译成汉语.如有不妥还望指出. 原文链接:https:// ...