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 ...
随机推荐
- bat-注册表
注册表 注册表就像于是配置文件 linux下一切皆文件,windows下一切皆注册表 注册表(各种配置文件:系统设置.用户设置.软件的配置) HKEY_CLASSES_ROOT 超级管理员.系 ...
- 『现学现忘』Docker基础 — 41、将本地镜像推送到阿里云
目录 1.准备工作 2.阿里云容器镜像仓库的使用 (1)创建命名空间 (2)创建容器镜像 (3)查看阿里云镜像仓库的信息 3.将本地Docker镜像推送到阿里云 (1)登陆阿里云 (2)给镜像生成版本 ...
- 相约 DTCC 2021 | Tapdata 受邀分享:如何打造面向 TP 业务的数据平台架构
2021第十二届中国数据库技术大会(DTCC)将于2021年10月18-20日,在北京国际会议中心举行,Tapdata 创始人唐建法受邀分享:如何打造面向 TP 业务的数据平台架构. 演讲时间 ...
- Tapdata 实时数据融合平台解决方案(四):技术选型
作者介绍:TJ,唐建法,Tapdata 钛铂数据CTO,MongoDB中文社区主席,原MongoDB大中华区首席架构师,极客时间MongoDB视频课程讲师. 常见搭建数据中台的技术产品 数据中台包括: ...
- 基于单层决策树的AdaBoost算法原理+python实现
这里整理一下实验课实现的基于单层决策树的弱分类器的AdaBoost算法. 由于是初学,实验课在找资料的时候看到别人的代码中有太多英文的缩写,不容易看懂,而且还要同时看代码实现的细节.算法的原理什么的, ...
- 我的sql没问题为什么还是这么慢|MySQL加锁规则
前言 前阵子参与了字节跳动后端青训营,其中大项目编写涉及到数据持久化一般选择使用MySQL.由于时间原因,数据库使用我选择了无脑三板斧:1. 建立了索引加速查询.2. 关闭自动提交事务.3. 在需要确 ...
- 掌握CSS中的z-index
前言 z-index是一个用于控制文档中图层顺序的属性.具有较高z-index值的元素将会出现在具有较低值的元素之上.就像页面上的x轴和y轴决定一个元素在水平和垂直方向上的位置一样,z-index控制 ...
- 一些JS库汇总
作者:wlove 链接:https://www.zhihu.com/question/429436558/answer/2348777302 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权, ...
- Class对象功能概述和Class对象功能获取Field
Constructor[] getConstructors() Constructor getConstructor(类... parameterTypes) Constructor getDecla ...
- LeetCode题解-20.有效的括号
题目 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. 示例 ...