使用SpringBoot+JWT+MybatisPlus实现简单的登陆状态验证
创始人
2025-06-01 00:09:35
0

文章目录

  • 前言
  • 一、JWT是什么?
  • 二、使用步骤
    • 1.创建项目,导入依赖,配置、引入工具类
    • 2.编写LoginController和UserController
    • 3.编写跨域拦截器和token验证拦截器
    • 4.全局拦截器配置
  • 三、业务逻辑
  • 四、测试
  • 总结


前言

登陆功能是每个系统的最基本功能,在SSM技术栈中,登陆状态验证一般会使用服务端的session,但是session并没有想象中的那么好用,经常会出现由于sessionid不一致导致的信息丢失,更好的解决方案就是使用JWT的Token生成。


一、JWT是什么?

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息,该信息可以被验证和信任,因为它是数字签名的,常用于单点登录。

JWT-token
JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:头部,载荷,签名

  • Header
    头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象,示例:{“typ”:“JWT”,“alg”:“HS256”}
    在头部指明了签名算法是HS256算法。 我们可以通过BASE64进行编码/解码:https://www.matools.com/base64/
  • Payload
    载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分。
    1.标准中注册的声明(建议但不强制使用):例如:sub表示jwt所面向的用户
    2.公共的声明:可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
    3.私有的声明:私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
    例如:定义一个payload:
    {“sub”:“1234567890”,“name”:“John Doe”,“admin”:true}
    然后将其进行base64加密,得到Jwt的第二部分:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
  • Signature
    jwt的第三部分是一个签证信息,这个签证信息由三部分组成:header + payload + secret
    这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
    注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了

本次使用jwt的token生成来实现一个登陆状态的验证。

二、使用步骤

1.创建项目,导入依赖,配置、引入工具类

本次使用mybatis-plus的一键生成项目,具体的步骤可以查看我之前的文章mybatis-plus详解http://t.csdn.cn/F0QcR
引入依赖

 io.jsonwebtokenjjwt-impl0.11.2runtimeio.jsonwebtokenjjwt-api0.11.2io.jsonwebtokenjjwt-jackson0.11.2runtimeorg.apache.commonscommons-pool22.7.0org.apache.commonscommons-text1.8

生成token的工具类:

package com.lzl.utils;import io.jsonwebtoken.*;
import org.apache.commons.lang3.time.DateUtils;import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;//jwt工具类
public class JwtHelper {// 生成Jwtpublic static String jwsWithHS(SignatureAlgorithm signatureAlgorithm, String userInfo, int num, String secret) {Key key = new SecretKeySpec(secret.getBytes(), signatureAlgorithm.getJcaName());Claims claims = Jwts.claims();claims.setSubject(userInfo);  // jwt的信息的本身claims.setExpiration(DateUtils.addSeconds(new Date(), num)); //设置jwt多少秒过期String jws = Jwts.builder().setClaims(claims).signWith(key, signatureAlgorithm).compact();return jws;}// 校验Jwtpublic static Jwt verifySign(String jws, String secret, SignatureAlgorithm signatureAlgorithm) {Key key = new SecretKeySpec(secret.getBytes(), signatureAlgorithm.getJcaName());Jwt jwt = Jwts.parserBuilder().setSigningKey(key).build().parse(jws);return jwt;}}

本次使用的secret:
在这里插入图片描述
由于我们需要在controller中使用,所以把两个参数,密钥和加密方式配置在yml文件中

这个secret可以去https://jwt.io/网站生成
在这里插入图片描述

上边选择加密方法,下边输入你想加密的信息,左侧会显示出加密后的信息。

2.编写LoginController和UserController

package com.lzl.controller;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lzl.pojo.User;
import com.lzl.service.UserService;
import com.lzl.utils.JwtHelper;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/3/21 08:21*/
@RestController
@RequestMapping("/login")
public class LoginController {@Value("${jwt.secret}")private String secret;@Value("${jwt.signature-algorithm}")private String algorithm;@Autowiredprivate UserService service;@RequestMapping("/verify")public Map loginVerify(User user){Map map = new HashMap<>();//去库中查询QueryWrapper wrapper = new QueryWrapper<>();wrapper.eq("username",user.getUsername()).eq("password",user.getPassword());User one = service.getOne(wrapper);if(one != null){map.put("username",one.getUsername());//登录成功,生成tokenString token = JwtHelper.jwsWithHS(SignatureAlgorithm.forName(algorithm),user.getUsername(),3600,secret);map.put("token",token);}return map;}
}
package com.lzl.controller;import com.lzl.pojo.User;
import com.lzl.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** 

* 前端控制器*

** @author zhenLong* @since 2023-03-20*/ @RestController @RequestMapping("/user") public class UserController {@Autowiredprivate UserService service;@RequestMapping("/getAll")public List getUser(){return service.list();} }

3.编写跨域拦截器和token验证拦截器

package com.lzl.interceptor;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** --效率,是成功的核心关键--* 拦截器,用于处理跨域请求** @Author lzl* @Date 2022/10/1 10:20*/public class CrossOriginInterceptor implements HandlerInterceptor {//主要逻辑:在handler之前执行:抽取handler中的冗余代码@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {String origin = request.getHeader("Origin");// 允许的跨域response.setHeader("Access-Control-Allow-Origin",origin);// 允许携带Cookieresponse.setHeader("Access-Control-Allow-Credentials","true");// 允许的请求头  预检请求需要这个设置response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization,Access-Token,token");response.setHeader("Access-Control-Expose-Headers", "*");//响应客户端的头部 允许携带Token 等等response.setHeader("Access-Control-Max-Age", "3600");   // 预检请求的结果缓存时间if (request.getMethod().equals("OPTIONS")){return false;}return true;}//在handler之后执行:进一步的响应定制@Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {}//在页面渲染完毕之后,执行:资源回收@Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex)throws Exception {}
}
package com.lzl.interceptor;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;/*** --效率,是成功的核心关键--* token验证拦截器* @Author lzl* @Date 2023/3/21 08:19*/public class VerifyTokenInterceptor implements HandlerInterceptor {//在controller之前执行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("token");/*请求走到这,证明是非login方法(login方法已经被放行),验证token是否存在,不存在直接拦截存在?验证是否有效,有效放行,无效拦截*/if (token == null){response.setStatus(500);Map map = new HashMap<>();response.setContentType("application/json;charset=utf-8");//is null or token unavailablemap.put("msg","登陆状态已过期");map.put("code",500);response.getWriter().write(new ObjectMapper().writeValueAsString(map));return false;}return true;}
}

4.全局拦截器配置

在springboot中配置拦截器需要对拦截器进行注册

package com.lzl.config;import com.lzl.interceptor.CrossOriginInterceptor;
import com.lzl.interceptor.VerifyTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** --效率,是成功的核心关键--* 拦截器配置** @Author lzl* @Date 2023/1/26 14:12*/
@Configuration
public class GlobalInterceptorConfig implements WebMvcConfigurer {@Override   //拦截器配置public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CrossOriginInterceptor()) //拦截器跨域注册对象.addPathPatterns("/**"); //指定要拦截的请求registry.addInterceptor(new VerifyTokenInterceptor())//token验证拦截器.addPathPatterns("/**").excludePathPatterns("/login/*"); //排除请求}}

三、业务逻辑

登陆状态验证的业务逻辑很简单,主要流程如下:
首先,用户没有登陆的情况下,用户无法访问除了login路径下的接口,以外的任何一个接口,访问的时候直接将页面重定向到登录页,若是前后端分离项目,则给前端返回错误状态码,让前端将用户跳转到登陆页。用户通过login接口输入正确的帐号和密码,后端生成一个token,并返回给前端,可以设置在请求头,或者以参数的形式传递,当前端拿到这个token时就可以访问登陆以外的页面了。

四、测试

当没有登陆状态时

在这里插入图片描述

访问login方法登陆,传回来一个token

在这里插入图片描述

携带token访问后端方法

在这里插入图片描述
访问成功


总结

这里只是对token简单的使用,在微服务架构中,token一般用于单点登陆验证,即登陆完成后,将token传到redis中存储,当访问除了登陆以外的其它服务时,去redis中查找。

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...