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 ...
随机推荐
- python小题目练习(十二)
题目:如下图所示 代码展示: """Author:mllContent:春节集五福Date:2020-01-17"""import rand ...
- Hdfs存储策略
一.磁盘选择策略 1.1.介绍 在HDFS中,所有的数据都是存在各个DataNode上的.而这些DataNode上的数据都是存放于节点机器上的各个目录中的,而一般每个目录我们会对应到1个独立的盘,以便 ...
- React技巧之导入组件
正文从这开始~ 总览 在React中,从其他文件中导入组件: 从A文件中导出组件.比如说,export function Button() {} . 在B文件中导入组件.比如说,import {But ...
- idea中enter键不能换行
idea中enter键不能换行 按enter键只能往下移动 如下图 解决办法: 方式一:按住window + Insert 方式二: 按住Fn + Insert 两种方式总有一种可以 之后就可以按en ...
- 【MAUI】为 Label、Image 等控件添加点击事件
一.前言 已经习惯了 WPF.WinForm 中"万物皆可点击"的方式. 但是在 MAUI 中却不行了. 在 MAUI 中,点击.双击的效果,是需要通过"手势识别器&qu ...
- Conversation Modeling on Reddit Using a Graph-Structured LSTM
publish: Transactions of the Association for Computational Linguistics,2016 tasks: predicting popul ...
- NOI / 2.1基本算法之枚举-8759:火车上的人数
8759:火车上的人数 总时间限制: 1000ms 内存限制: 65536kB 描述 火车从始发站(称为第1站)开出,在始发站上车的人数为a,然后到达第2站,在第2站有人上.下车,但上.下 ...
- 谷歌的SRE和开发是如何合作的
本文是一篇比较有价值的.介绍SRE的文章.国内的所谓SRE职责其实并不明确,大部分其实还是干普通运维的事.但文中介绍的谷歌的运作方式起点还是相对比较高的,无论对SRE.对开发,甚至对公司都有很高的要求 ...
- 【ASP.NET Core】选项模式的相关接口
在 .NET 中,配置与选项模式其实有联系的(这些功能现在不仅限于 ASP.NET Core,而是作为平台扩展来提供,在其他.NET 项目中都能用).配置一般从多个来源(上一篇水文中的例子,记得否?) ...
- BACnet IP转OPC UA网关
BACnet是楼宇自动化和控制网络数据通信协议的缩写.它是为楼宇自动化网络开发的数据通信协议 根据1999年底互联网上楼宇自动化网络的信息,全球已有数百家国际知名制造商支持BACnet,包括楼宇自 ...