SpringBoot整合JWT实现登录认证
什么是JWT
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
为什么使用JWT
- 简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快。
- 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库。
- 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
- 不需要在服务端保存会话信息,特别适用于分布式微服务。
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实现登录认证的更多相关文章
- springboot系列(十)springboot整合shiro实现登录认证
关于shiro的概念和知识本篇不做详细介绍,但是shiro的概念还是需要做做功课的要不无法理解它的运作原理就无法理解使用shiro: 本篇主要讲解如何使用shiro实现登录认证,下篇讲解使用shiro ...
- SpringBoot 整合 Shiro 密码登录与邮件验证码登录(多 Realm 认证)
导入依赖(pom.xml) <!--整合Shiro安全框架--> <dependency> <groupId>org.apache.shiro</group ...
- 厉害!我带的实习生仅用四步就整合好SpringSecurity+JWT实现登录认证!
小二是新来的实习生,作为技术 leader,我还是很负责任的,有什么锅都想甩给他,啊,不,一不小心怎么把心里话全说出来了呢?重来! 小二是新来的实习生,作为技术 leader,我还是很负责任的,有什么 ...
- SpringBoot集成JWT实现权限认证
目录 一.JWT认证流程 二.SpringBoot整合JWT 三.测试 上一篇文章<一分钟带你了解JWT认证!>介绍了JWT的组成和认证原理,本文将介绍下SpringBoot整合JWT实现 ...
- 【项目实践】一文带你搞定Session和JWT的登录认证方式
以项目驱动学习,以实践检验真知 前言 登录认证,估计是所有系统中最常见的功能了,并且也是最基础.最重要的功能.为了做好这一块而诞生了许多安全框架,比如最常见的Shiro.Spring Security ...
- 教你 Shiro + SpringBoot 整合 JWT
本篇文章将教大家在 shiro + springBoot 的基础上整合 JWT (JSON Web Token) 如果对 shiro 如何整合 springBoot 还不了解的可以先去看我的上一篇文章 ...
- SpringBoot整合Shiro 四:认证+授权
搭建环境见: SpringBoot整合Shiro 一:搭建环境 shiro配置类见: SpringBoot整合Shiro 二:Shiro配置类 shiro整合Mybatis见:SpringBoot整合 ...
- spring-boot整合shiro作权限认证
spring-shiro属于轻量级权限框架,即使spring-security更新换代,市场上大多数企业还是选择shiro 废话不多说 引入pom文件 <!--shiro集成spring--& ...
- JWT实现登录认证实例
JWT全称JSON Web Token,是一个紧凑的,自包含的,安全的信息交换协议.JWT有很多方面的应用,例如权限认证,信息交换等.本文将简单介绍JWT登录权限认证的一个实例操作. JWT组成 JW ...
随机推荐
- VM Ware 给Centos虚拟机配置静态IP
前言:在大家的日常运维工作中,肯定多多少少都会用到虚拟机,但是默认情况下VMware创建的虚拟机每次启动都有可能更换IP这就对我们的工作造成了一定的麻烦,下面我就给大家介绍下,如何给centos虚拟机 ...
- Elasticsearch深度应用(下)
Query文档搜索机制剖析 1. query then fetch(默认搜索方式) 搜索步骤如下: 发送查询到每个shard 找到所有匹配的文档,并使用本地的Term/Document Frequer ...
- 聊聊 C++ 中几类特殊成员函数
一:背景 在 C# 中要说类默认给我们定义的特殊成员函数,莫过于 构造函数,但在 C++ 中这样的特殊函数高达 6 种,有必要整合一下聊一聊. 二:特殊成员函数 1. 默认构造函数 和 C# 一样,很 ...
- 网络通信协议分类和IP地址
网络通信协议分类 通信的协议还是比较复杂的,java.net 包中包含的类和接口,它们提供低层次的通信细节.我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节 java.net ...
- jdbc 01: 连接mysql,并实现数据插入
jdbc连接mysql,并实现数据插入 package com.examples.jdbc.o1_连接与插入; import java.sql.*; /* jdbc数据库连接六步 */ public ...
- CF1612D X-Magic Pair
题意: 给一个数对 \((a,b)\) ,每次可以进行操作 \((a,b) \to (|a-b|,b)\) 或 \((a,b) \to (a,∣a−b∣)\),问最后能否令 \(a=x\) 或 \(b ...
- 搞懂前端二进制系列(二):🍈File、FileReader与Base64
参考资料: JavaScript高级程序设计第四版:File API https://juejin.cn/post/7046313942938812424[前端二进制一次搞清楚] 一.File 类型 ...
- 常用的函数式接口_Consumer接口和常用的函数式接口_Consumer接口的默认方法andThen
Consumer接口 java,util.function.Consumer接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定 抽象方法:accept C ...
- 20220724-Java的继承
目录 含义 代码示例 使用方法和注意事项 个人理解 含义 继承Extends 面向对象最显著的一个特性,继承是从已有的类中派生出新的类,新的类能吸收已有类的性和方法,并能扩展新的能力. 代码示例 cl ...
- Git 08 IDEA撤销添加
参考源 https://www.bilibili.com/video/BV1FE411P7B3?spm_id_from=333.999.0.0 版本 本文章基于 Git 2.35.1.2 如果将不想添 ...