【SpringBoot】SpringBoot整合JWT

目录

  • 先说token
  • 单点登录(SSO)
    • 简介
    • 原理
    • 单点登录的优势
    • 单点登录流程
    • 分布式单点登录方式
      • 方式一:session广播机制实现
      • 方式二:使用cookie+redis实现。
      • 方式三:token认证
  • JWT
    • 数字签名
    • JWT的作用
    • JWT和传统Session
      • 1、无状态:
      • 2、避免CSRF 攻击:
      • 3、适合移动端应用
      • 4、单点登录友好
      • 总结
    • JWT的核心应用
      • Authorization (授权)
      • Information Exchange (信息交换)
    • JWT的组成部分
      • header:头部信息
      • payload:有效载荷
      • signatur:签名算法
    • JWT的工作流程
      • 思路
      • 步骤
    • JWT的demo【单独案例】
      • 1.依赖
      • 2.生成Token
      • 3.输出结果
      • 解密
        • 在线token解密
        • 代码实现
        • 输出结果
      • 常见的异常整理
    • 常见异常信息
  • SpringBoot整合JWT
    • 1.依赖
    • 2.配置
    • 3.封装工具类
    • 4.其他类
    • 5.拦截器
    • 6.拦截器配置
    • 7.controller类
    • 8.测试

先说token

  • 随着前后端分离的普及以及分布式、微服务、Restful API的普遍应用,Token认证已经是所有系统都绕不开的一个技术话题。
  • 基于token的用户认证是一种服务端无状态的认证方式。所谓服务端无状态指的token本身包含登录用户所有的相关数据,而客户端在认证后的每次请求都会携带token,因此服务器端无需存放token数据。
  • 当用户认证后,服务端生成一个token发给客户端,客户端可以放到 cookie 或 localStorage 等本地存储中,在客户端每次发起请求时带上 token,服务端收到token通过验证后即可确认用户身份
  • 简单来说token就像是一个令牌,好比我们办的某个超市、健身房、酒店等机构的会员卡。而且如果某个机构是连锁性质的,那么这一个会员卡是可以在所有连锁单位都能认证的,这种就是当下流行的一个词:单点登录

单点登录(SSO)

简介

  • 单点登录(Single Sign-On,简称SSO)是一种身份验证和访问控制的技术,它允许用户使用一组凭据(如用户名和密码)登录到多个相关但相对独立的系统中,而不需要再次输入凭据。
  • 通过SSO,用户只需登录一次,就可以访问多个应用或系统,简化了用户的登录流程和管理,提高了用户体验。

原理

  • 在一个典型的SSO系统中,有一个中心身份提供者(Identity Provider,简称IdP),其负责处理用户的身份验证和生成令牌。
  • 当用户尝试访问其他应用或系统时,这些应用或系统会将用户重定向到IdP进行身份验证。
  • 一旦用户成功登录,IdP会生成一个令牌,并返回给应用或系统。
  • 应用或系统可以使用此令牌来验证用户的身份,并为其提供相应的访问权限。

单点登录的优势

  • SSO的好处包括提高用户体验、减少用户的密码负担、简化身份验证和访问管理、提高安全性等。
  • 通过使用SSO,用户可以通过一个登录凭据访问多个应用或系统,无需记住多个用户名和密码。
  • 同时,SSO还可以加强安全性,通过集中管理和控制用户的访问权限,减少安全漏洞的发生。

单点登录流程

在这里插入图片描述

分布式单点登录方式

方式一:session广播机制实现

  • 简单来说:就是把session复制到另一台服务器中
  • 缺点:
    • 模块较多时,拷贝session比较浪费资源;
    • 比如 中间会存在多份一样的数据 ;
    • session默认过期时间30分钟,过期需要重新登录

方式二:使用cookie+redis实现。

  • cookie客户端技术:存在浏览器中,每次发送请求,带着cookie值进行发送
  • redis,读取速度快,基于key-value存储(keys *)
  • 用户登录后,把数据分别放到两个地方cookie、redis
    • redis:在key里生成唯一随机值(ip、用户id、uuid) ,在value里放用户数据
    • cookie:把redis里面生成key值放到cookie里面。
  • 访问项目其他模块时,发送请求带着cookie进行发送,然后其他模块去获取cookie值,也就是拿着cookie去redis中查询,如果能查到数据表示这个用户已登录。

方式三:token认证

  • 按照一定规则生成字符串,字符串可以包含用户信息——jwt

JWT

  • JSON Web Token,通过数字签名的方式,以JSON对象为载体,在不同的服务终端之间安全的传输信息

数字签名

  • 数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。
  • 它是一种类似写在纸上的普通的物理签名,但是在使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。
  • 一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。
  • 数字签名是非对称密钥加密技术数字摘要技术的应用

JWT的作用

  • jwt最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含jwt
  • 系统在每次处理用户请求之前,都要先进行jwt安全校验,痛过之后才能进行处理

JWT和传统Session

1、无状态:

  • token 自身包含了身份验证所需要的所有信息,使得我们的服务器不需要存储 Session 信息,这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。
  • 也导致了它最大的缺点:当后端在token 有效期内废弃一个 token 或者更改它的权限的话,不会立即生效,一般需要等到有效期过后才可以。另外,当用户 Logout 的话,token 也还有效。除非,我们在后端增加额外的处理逻辑。

2、避免CSRF 攻击:

  • 攻击者就可以通过让用户误点攻击链接,达到攻击效果。
  • 防止误触操作,避免请求直接获取本地的session值进行请求访问。

3、适合移动端应用

  • 使用 Session 进行身份认证的话,需要保存一份信息在服务器端,而且这种方式会依赖到 Cookie(需要 Cookie 保存 SessionId),所以不适合移动端。
  • 但是,使用 token 进行身份认证就不会存在这种问题,因为只要 token 可以被客户端存储就能够使用,而且 token 还可以跨语言使用。

4、单点登录友好

  • 使用 Session 进行身份认证的话,实现单点登录,需要我们把用户的 Session 信息保存在一台电脑上,并且还会遇到常见的 Cookie 跨域的问题。
  • 但是,使用 token 进行认证的话, token 被保存在客户端,不会存在这些问题。

总结

JWT传统Session
存储位置客户端服务器
存储数据TokenSession ID + 服务器端存储的会话数据
存储方式无状态有状态
跨域支持支持需要额外配置
可扩展性
安全性
网络开销
扩展性
动态更改权限需要重新签发Token服务器端配置即可
服务器状态管理无需管理需要管理和维护

JWT的核心应用

Authorization (授权)

  • 这是使用JWT的最常见场景。
  • 一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。
  • 单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。

Information Exchange (信息交换)

  • 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。
  • 因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。
  • 另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

JWT的组成部分

JWT的结构由三部分组成,分别是标头、有效负载、签名算法,中间使用 点 进行隔开。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMmY0MzMyYy01MmRhLTQ0MDktODJjZS1hODBkZjNmMDIwYjMiLCJzdWIiOiJhbGwiLCJpYXQiOjE3MTcxMTc3MTMsImV4cCI6MTcxNzExOTUxMywidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsInVzZXJJZCI6IjEwMDEifQ.wfrMHoLZubZksALfad5BAG7oNUXbMwrXxHhgRTAtOtI

header:头部信息

  • 通常由两部分组成:令牌的类型 和 所用的加密算法
  • 然后将该JSON对象数据进行Base64 URL编码,得到的字符串就是JWT令牌的第一部分。
  • 例如:{ “typ”: “JWT”, “alg”: “HS256” }。然后要转成base64字符串。

payload:有效载荷

  • 有效数据存储区,主要定义自定义字段和内置字段数据。
  • 通常会把用户信息和令牌过期时间放在这里,同样也是一个JSON对象,里面的key和value可随意设置,然后经过Base64 URL编码后得到JWT令牌的第二部分
  • 由于这个部分是没有加密的(因为Base64是编码,可以直接解码),建议只存放一些非敏感信息
Payload的内置字段说明
iss(Issuer)令牌的签发者
sub(Subject)所面向的用户或实体
aud(Audience)令牌的接收者
exp(Expiration Time)令牌的过期时间(以UNIX时间戳表示)
nbf(Not Before)令牌的生效时间(以UNIX时间戳表示)
iat(Issued At)令牌的签发时间(以UNIX时间戳表示)
jti(JWT ID)令牌的唯一标识符

原文链接:https://blog.csdn.net/qq_46921028/article/details/131298671

signatur:签名算法

  • 使用头部Header定义的加密算法,对前两部分Base64编码拼接的结果进行加密
  • 加密时的秘钥服务私密保存
  • 加密后的结果在通过Base64Url编码得到JWT令牌的第三部分。
  • 签名的作用:防止JWT令牌被篡改。
var encodestr = base64urlEncode(header) + "." + base64urlEncode(paylod);
var signature = HMACSHA256(encodestr,"secret");

JWT的工作流程

思路

  • 在身份验证中,当用户成功登录系统时,授权服务器将会把 JSON Web Token(JWT)返回给客户端,用户需要将此凭证信息存储在本地(cookie或浏览器缓存)。
  • 当用户发起新的请求时,需要在请求头中附带此凭证信息,当服务器接收到用户请求时,会先检查请求头中有无凭证,是否过期,是否有效。
    • 如果凭证有效,将放行请求;
    • 若凭证非法或者过期,服务器将回跳到认证中心,重新对用户身份进行验证,直至用户身份验证成功。

步骤

  1. 发送登录请求,携带username、password
  2. 进行验证,验证通过返回JWT
  3. 请求头携带JWT发请求到应用服务
  4. 验证携带过来的JWT的合法性
  5. 验证通过返回,执行后续操作

在这里插入图片描述

JWT的demo【单独案例】

1.依赖

	<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.12.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent>...<properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency></dependencies>
  • 如果jdk大于1.8,还需要引入以下依赖

            <dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.0</version></dependency><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-impl</artifactId><version>2.3.0</version></dependency><dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-core</artifactId><version>2.3.0</version></dependency><dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency>
    

2.生成Token

@SpringBootTest
class JwtApplicationTests {/** AES 算法 */private static final String ALGORITHM_AES="AES";@Testpublic void testCreatJwt() throws NoSuchAlgorithmException {//定义秘钥,可以自己定义,随便一个字符串都可以,专业一些的话就用密钥生成工具吧String secretKey = getKey();System.out.println("生成的密钥是:" + secretKey);// 使用Jwts工具类构建一个令牌String token = Jwts.builder()// 1.设置JWT头部信息(类型和加密算法).setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256")// 2.设置JWT载荷数据.setId(UUID.randomUUID().toString()) //内置字段jti:表示唯一ID.setSubject("all") //内置字段sub:面向所有用户.setIssuedAt(new Date()) //内置字段ita:token创建时间.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000)) //内置字段exp:token过期时间,30分钟.claim("username", "zhangsan") //自定义字段,kv格式.claim("userId", "1001") //自定义字段// 3.设置JWT签名信息(加密算法,秘钥).signWith(SignatureAlgorithm.HS256, secretKey).compact(); //最后调用compact()方法生成最终的token//由于使用UUID生成唯一标识,所以每次生成的token都不一样System.out.println("token = " + token);}/*** 生成密钥* @return* @throws NoSuchAlgorithmException*/private String getKey() throws NoSuchAlgorithmException {/*** 创建KeyGenerator实例*      algorithm密钥算法*          AES*          DES*          DESede*          HmacSHA1*          HmacSHA224*          HmacSHA256*          HmacSHA384*          HmacSHA512*          RC2*/KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);//指定生成密钥的大小;AES密钥长度只能=128、192、256keyGenerator.init(256);//指定生成密钥随机源:keyGenerator.init(SecureRandom secureRandom)//指定生成密钥大小、随机源:keyGenerator.init(int size, SecureRandom secureRandom)/*** 借助Base64转换生成的密钥*      通常加密后要把密钥保存下来,解密时使用密钥重建SecertKey,生成的密钥是字节数组不利于保存,所以借助Base64转换成字符串*/return Base64.getEncoder().encodeToString(keyGenerator.generateKey().getEncoded());}
}

3.输出结果

生成的密钥是:pI9g7kkh0IgqHC27U7FYgAQtquy9PGPINCUvko2Qyyo=
token = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMmY0MzMyYy01MmRhLTQ0MDktODJjZS1hODBkZjNmMDIwYjMiLCJzdWIiOiJhbGwiLCJpYXQiOjE3MTcxMTc3MTMsImV4cCI6MTcxNzExOTUxMywidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsInVzZXJJZCI6IjEwMDEifQ.wfrMHoLZubZksALfad5BAG7oNUXbMwrXxHhgRTAtOtI

解密

在线token解密

地址:https://tooltt.com/jwt-decode/
在这里插入图片描述

代码实现

刚才的token过期了,重新生成了一下

    @Testpublic void testcheckToken() {// 秘钥,刚才生成的密钥String secretKey = "Y28Ijg521FgN31ZgpD1hZpOYd8fTMrZwNcMgds+D91I=";// 待验证的tokenString tokenStr = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4M2M3NTVkNC1jNzJlLTRlZjctYjY1MC1jYjdlZWRkYWNjYWIiLCJzdWIiOiJhbGwiLCJpYXQiOjE3MTcxMjQxNjQsImV4cCI6MTcxNzEyNTk2NCwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsInVzZXJJZCI6IjEwMDEifQ.gM89JWUOAQu8KpYgXbom9KGXB1ZcqSUqzj5eW8cg_HU";// 通过密钥验证签名是否被篡改JwtParser jwtParser = Jwts.parser();Jws<Claims> claimsJws = jwtParser.setSigningKey(secretKey).parseClaimsJws(tokenStr);// 获取头JwsHeader header = claimsJws.getHeader();// 获取载荷Claims body = claimsJws.getBody();// 获取签名String signature = claimsJws.getSignature();System.out.println("头信息:" + header);System.out.println("载荷信息:" + body);System.out.println("签名信息:" + signature);}
输出结果
头信息:{typ=JWT, alg=HS256}
载荷信息:{jti=83c755d4-c72e-4ef7-b650-cb7eeddaccab, sub=all, iat=1717124164, exp=1717125964, username=zhangsan, userId=1001}
签名信息:gM89JWUOAQu8KpYgXbom9KGXB1ZcqSUqzj5eW8cg_HU

常见的异常整理

常见异常信息

异常原因
SignatureVerificationException签名不一致异常
TokenExpiredException令牌过期异常
AlgorithmMismatchException算法不匹配异常
InvalidClaimException:失效的payload异常

SpringBoot整合JWT

1.依赖

用这个吧,反正后面要写OAuth2

        <dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency>

2.配置

# JWT 配置
jwt:secret: Y28Ijg521FgN31ZgpD1hZpOYd8fTMrZwNcMgds+D91I= # 加密密钥expire: 1800 # token有效时长 S
server:port: 9999
spring:datasource:type: com.zaxxer.hikari.HikariDataSource# ...... 其他配置

3.封装工具类

package com.kgc.utils;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.Calendar;
import java.util.Map;/*** @author: zjl* @datetime: 2024/5/31* @desc: 复兴Java,我辈义不容辞*/
@Component
public class JWTUtils {@Value("${jwt.secret}")public  String secret;@Value("${jwt.expire}")public  Integer tokenExpire;/*** 生成 JWT 令牌* @param map 传入的 Payload 数据* @return 返回生成的令牌*/public String getToken(Map<String,String> map){JWTCreator.Builder builder = JWT.create();// 遍历传入的 Payload 数据,并添加到 Builder 中map.forEach((k,v)->{builder.withClaim(k,v);});Calendar instance = Calendar.getInstance();instance.add(Calendar.SECOND,tokenExpire);// 设置过期时间为 XX 秒后builder.withExpiresAt(instance.getTime());// 使用 HMAC256 签名算法进行签名,并返回令牌字符串return builder.sign(Algorithm.HMAC256(secret)).toString();}/*** 获取令牌中的 Payload 数据* @param token 要解析的令牌字符串* @return 解码后的令牌对象(DecodedJWT)*/public DecodedJWT verify(String token){// 创建一个 JWTVerifier 实例,使用相同的密钥构建,并对令牌进行验证和解码return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);}
}

4.其他类

  • 实体类

    @Data
    @NoArgsConstructor
    @ToString
    @AllArgsConstructor
    public class User {private int id;private String userCode;private String userName;private String userPassword;private String phone;
    }
    
  • mapper

    public interface UserMapper {@Select("SELECT * FROM SMBMS_USER WHERE USERCODE=#{userCode}")User selectUserByUserCode(String userCode);
    }
    
  • service

    @Service
    @Slf4j
    public class UserService {@Resourceprivate UserMapper userMapper;public User login(String userCode,String userPassword){User user = userMapper.selectUserByUserCode(userCode);if(user!=null && userPassword.equals(user.getUserPassword())){return user;}return null;}
    }
    
  • 统一返回模板

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Result<T> {private int code;private String message;private T data;public Result(T data) {this.code = 200;this.message = "success";this.data = data;}public Result(T data, boolean success, String message) {if (success) {this.code = 200;this.message = "success";} else {this.code = 500; // 自定义错误状态码(示例为500)this.message = message;}this.data = data;}public Result(int code, String message) {this.code = code;this.message = message;this.data = null;}/*** 返回执行失败的结果(默认状态码为500)** @param message 提示信息* @return 失败的结果对象*/public static <T> Result<T> fail(String message) {return new Result<>(500, message);}/*** 返回执行失败的结果(自定义状态码和提示信息)** @param code    状态码* @param message 提示信息* @return 失败的结果对象*/public static <T> Result<T> fail(int code, String message) {return new Result<>(code, message);}
    }
    

5.拦截器

package com.kgc.interceptor;/*** @author: zjl* @datetime: 2024/5/31* @desc: 复兴Java,我辈义不容辞*/import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kgc.utils.JWTUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;/*** JWTInterceptor是一个拦截器,用于验证请求头中的JWT令牌是否有效。* 当有请求进入时,该拦截器会首先从请求头中获取令牌,并尝试验证其有效性。* 如果令牌验证成功,则放行请求;否则,拦截请求并返回相应的错误信息。*/
@Component
public class JWTInterceptor implements HandlerInterceptor {@Resourceprivate JWTUtils jwtUtils;@Resourceprivate ObjectMapper objectMapper;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 创建一个Map对象,用于存储响应信息Map<String, Object> map = new HashMap<>();// 从请求头中获取令牌String token = request.getHeader("token");try {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); // 设置状态为false// 将Map转化为JSON字符串(使用Jackson库)String json = objectMapper.writeValueAsString(map);response.setContentType("application/json;charset=UTF-8"); // 设置响应的Content-Typeresponse.getWriter().println(json); // 将JSON字符串写入响应中return false; // 不放行请求}
}

6.拦截器配置

/*** InterceptorConfig 是一个配置类,用于添加拦截器。* 在这个类中,我们可以配置需要拦截的接口路径以及排除不需要拦截的接口路径。* 在这个例子中,我们添加了JWTInterceptor拦截器来对请求进行token验证,* 并设置了"/user/test"接口需要进行验证,而"/user/login"接口则被排除在验证之外,即所有用户都放行登录接口。*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Resourceprivate JWTInterceptor jwtInterceptor;/*** 添加拦截器配置*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor).addPathPatterns("/user/test")         // 对"/user/test"接口进行token验证.excludePathPatterns("/user/login");  // 所有用户都放行登录接口}
}

7.controller类

package com.kgc.controller;import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.kgc.pojo.User;
import com.kgc.service.UserService;
import com.kgc.utils.JWTUtils;
import com.kgc.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;/*** @author: zjl* @datetime: 2024/5/31* @desc: 复兴Java,我辈义不容辞*/
@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {@Resourceprivate UserService userService;@Resourceprivate JWTUtils jwtUtils;@RequestMapping("/login")public Result<Map<String, Object>> login(User user) {// 打印用户名和密码log.info("用户名: [{}]", user.getUserCode());log.info("密码: [{}]", user.getUserPassword());// 创建结果对象Result<Map<String, Object>> result;try {// 调用userService的login方法进行用户认证User loginUser = userService.login(user.getUserCode(),user.getUserPassword());if(loginUser == null){return new Result<>(0, "认证失败");}// 获取用户ID和用户名,并将其放入payloadMap<String, String> payload = new HashMap<>();payload.put("id", String.valueOf(loginUser.getId()));payload.put("name", loginUser.getUserName());// 生成JWT的令牌String token = jwtUtils.getToken(payload);// 构造成功的结果对象result = new Result<>(200, "认证成功");result.setData(new HashMap<>());result.getData().put("token", token); // 响应token} catch (Exception e) {// 构造失败的结果对象result = Result.fail(500, e.getMessage());}return result;}@RequestMapping("/test")public Result<Map<String, Object>> test(HttpServletRequest request) {// 创建结果对象Result<Map<String, Object>> result;try {Map<String, Object> map = new HashMap<>();// 处理自己的业务逻辑// 从请求头中获取tokenString token = request.getHeader("token");if(StringUtils.isEmpty(token)){return new Result<>(0, "请先登录!");}// 校验并解析tokenDecodedJWT verify = jwtUtils.verify(token);// 打印解析出的用户id和用户名log.info("用户id: [{}]", verify.getClaim("id").asString());log.info("用户name: [{}]", verify.getClaim("name").asString());// 构造成功的结果对象result = new Result<>(200, "请求成功!");result.setData(map);} catch (Exception e) {// 构造失败的结果对象result = Result.fail(500, e.getMessage());}return result;}@RequestMapping("/other")public Map<String, Object> test(String token) {Map<String, Object> map = new HashMap<>();try {jwtUtils.verify(token);map.put("msg", "验证通过~~~");map.put("state", true);} catch (TokenExpiredException e) {map.put("state", false);map.put("msg", "Token已经过期!!!");} catch (SignatureVerificationException e){map.put("state", false);map.put("msg", "签名错误!!!");} catch (AlgorithmMismatchException e){map.put("state", false);map.put("msg", "加密算法不匹配!!!");} catch (Exception e) {e.printStackTrace();map.put("state", false);map.put("msg", "无效token~~");}return map;}
}

8.测试

  • 先登录,用户名密码正确、不正确的
  • 正确的生成token
  • 然后访问其他接口,带token和不带token的。【header里带token】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/19636.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux nohup命令详解:持久运行命令,无视终端退出

nohup &#xff08;全称为 “no hang up”&#xff09;&#xff0c;用于运行一个命令&#xff0c;使其在你退出 shell 或终端会话后继续运行。 基本语法 nohup command [arg1 ...] [&> output_file] &command 是你想要运行的命令。[arg1 ...] 是该命令的参数。&am…

微服务学习Day8

文章目录 Sentinel雪崩问题服务保护框架Sentinel配置 限流规则快速入门流控模式流控效果热点参数限流 隔离和降级FeignClient整合Sentinel线程隔离&#xff08;舱壁模式&#xff09;熔断降级 授权规则及规则持久化授权规则自定义异常结果持久化 Sentinel 雪崩问题 服务保护框架…

让WSL内核使用BBR拥塞控制算法

使用git命令从Linux内核的Git仓库中获取源代码,$ git clone --depth 1 https://github.com/microsoft/WSL2-Linux-Kernel.git,找到对应的内核版本$ git log --grep="5.15.146.1-microsoft-standard-WSL2",回退到本机安装的内核版本$ git checkout <commit-id&…

无界延迟队列DelayQueue

一:介绍 DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。 二: DelayQueue基本原理 DelayQueue是一个没有边界…

代码随想录算法训练营第十天|232.用栈实现队列、225. 用队列实现栈

232.用栈实现队列 题目链接&#xff1a;232. 用栈实现队列 文档讲解&#xff1a;代码随想录 状态&#xff1a;写出来 &#xff0c;但差强人意 思路&#xff1a; 定义两个容器&#xff0c;可以是Stack&#xff0c;也可以是Deque&#xff0c;stackIn相当于临时容器,用来存放元素&…

git随记

git status 查看文件状态 git status -s 比较简洁的查看文件状态。如下代表此文件是新建的&#xff0c;没有被git跟踪的文件&#xff1a; $ git status -s ?? abc.txtgit add abc.txt 将abc添加到暂存区。后再次git status -s $ git status -s A abc.txtgit reset 将暂存…

嵌入式开发--stm32cubeprogrammer写入选项字节

需要在批量烧写时写入选项字节&#xff0c;操作如下&#xff1a; 在下载页面&#xff0c;勾选选项字节命令&#xff0c; 输入以下命令&#xff0c;后面的0xBB表示选项字节是bb&#xff0c;表示读保护&#xff0c;也可以按需要写其他的字符 -ob rdp0xBB在需要的功能前打上勾&a…

知识图谱抽取实战

相关代码见文末 1.知识图谱应用场景 知识图谱是一种先进的数据组织形式,它通过图数据结构来表示实体(如人、地点、概念)及其之间的复杂关系,便于机器理解和处理。这种结构化知识库允许高效的信息检索、推理和知识发现,尤其适用于处理高度关联且需要深度理解的领域,如医学…

解决wireshark无法抓取mysql数据报文

我前几天在用wireshark抓取mysql协议&#xff0c;只能看到登录信息&#xff0c;完全看不到具体报文。显示如下&#xff1a; 找了多资料&#xff0c;我也没解决这问题。但用公司测试环境的数据库就能抓取到mysql数据报文&#xff0c;观察了下公司的数据库就只发现连接url上多了…

使用 Django Channels 构建实时聊天应用(包含用户认证和消息持久化)

文章目录 准备工作创建 Django 项目创建应用程序配置项目编写 Consumer编写路由创建 URL 路由运行应用用户认证消息持久化显示历史消息结论 Django Channels 是 Django 的一个扩展&#xff0c;允许在 Web 应用中添加实时功能&#xff0c;例如 Websockets、HTTP2 和其他协议。本…

oracle mysql索引区别

文章目录 1.引言1.1 索引的基本概念1.2 Oracle和MySQL的简介 2.Oracle索引2.1 Oracle索引的类型**B-Tree索引****Bitmap索引****Function-Based索引****Partitioned索引****Text索引** 2.2 Oracle索引的工作原理2.3 Oracle索引的实例代码 3.MySQL索引3.1 MySQL索引的类型**B-Tr…

[PyQt5] 窗口接收WM_COPY消息

#本程序是python qt5 创建的窗口&#xff0c;拦截外部消息给窗口发送的WM_COPY消息并显示出来。一般是用来作为窗口之间的通讯机制之一。 python文件如下&#xff1a;qt5拦截消息 #!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import QAp…

STM32-14-FSMC_LCD

STM32-01-认识单片机 STM32-02-基础知识 STM32-03-HAL库 STM32-04-时钟树 STM32-05-SYSTEM文件夹 STM32-06-GPIO STM32-07-外部中断 STM32-08-串口 STM32-09-IWDG和WWDG STM32-10-定时器 STM32-11-电容触摸按键 STM32-12-OLED模块 STM32-13-MPU 文章目录 1. 显示器分类2. LCD简…

掌握 NestJS 10.x:从零开始构建高效可扩展的服务器端应用详解

NestJS 是一个用于构建高效、可扩展的 Node.js 服务端应用的框架&#xff0c;基于 TypeScript 构建&#xff0c;并且受 Angular 的启发&#xff0c;提供了模块化、易测试、易维护的架构。NestJS 10.x 引入了一些新特性和改进&#xff0c;进一步提升了开发体验。本文将详细介绍如…

协方差和协方差矩阵是什么

协方差矩阵&#xff08;Covariance Matrix&#xff09;是一个矩阵&#xff0c;它包含多个随机变量之间的协方差。 协方差是衡量两个随机变量如何一起变化的度量。 协方差矩阵在多元统计分析和机器学习中非常重要&#xff0c;特别是在处理多元正态分布时。 详细解释 协方差&am…

生态融合促发展 YashanDB与丰图科技完成兼容性认证

近日&#xff0c;深圳计算科学研究院崖山数据库系统YashanDB V23与丰图科技智域城市数字孪生平台顺利完成兼容性互认证。经严格测试&#xff0c;双方产品完全兼容&#xff0c;稳定运行&#xff0c;充分满足企事业单位在高性能、高可用性、高稳定性及高可控性方面的核心需求&…

比较好的Python课程

最近在学习夜曲编程的Python进阶课程——办公效率化&#xff1b;夜曲编程之前有推出一款学习Python的入门课程&#xff0c;在手机端和电脑端都可以学习的&#xff0c;如果没有时间在手机端学习都很好的。每节课程学习下来&#xff0c;可以收集到Python入门的知识卡片&#xff0…

Ansys Mechanical|组装 External Mechanical Model

Assembling Finite Element Models 上文中介绍了如何导入外部模型并将其组合到单个模型中的示例。 如果要将外部模型与Workbench环境中已有的一个或多个模型组合在一起&#xff0c;该如何操作&#xff1f;本文将介绍这个工作流程。 Ansys Mechanical支持Mechanical Model和Ex…

FPGA新起点V1开发板(七-语法篇)——程序框架+高级语法(选择性做笔记)

文章目录 一、模块结构二、赋值三、条件语句 一、模块结构 默认是wire类型&#xff0c;assign是定义功能。 上面这两个always都是并行 例化 二、赋值 有两种赋值“”和“<” “”是阻塞赋值&#xff0c;也就是从上到下&#xff0c;依次完成 “”是非阻塞赋值&#xff0c;…

开展FMEA,这些准备工作你做足了吗?

在产品研发和制造过程中&#xff0c;失效模式与影响分析&#xff08;FMEA&#xff09;是一项至关重要的质量工具。它能够帮助团队提前识别和预防潜在的产品失效问题&#xff0c;从而提升产品的可靠性和质量。然而&#xff0c;要想充分发挥FMEA的效用&#xff0c;充分的准备工作…