SpringBoot项目实现登录——集成JWT令牌和验证码的登录业务

目录

前言

一、初步认识JWT令牌

二、利用JWT令牌实现登录功能 

1.配置登录拦截器:

2.实现后端的登录接口

三、在登录中添加验证码功能

点此查看:完整的,附带验证码和JWT令牌验证功能的登录流程,完整代码


前言

        在我们的项目中,如何防止用户在未登录的情况下就去访问网站内其他的资源呢?

答:

        通常我们使用JWT令牌(token)来实现一个功能完善的登录模块,在用户登录成功后,服务器会返回给前端一串加密过的长字符串作为令牌(token),下次前端再进行请求时带上该令牌作为参数,后端服务器识别后就会允许前端访问其它站点。

一、初步认识JWT令牌

JWT(全称:Json Web Token)是我们Web开发中最常用的令牌规范,

1. 令牌包含的基本功能:

  • 承载业务数据:令牌字符串中应该包含用户的基本信息或其他所需数据,方便后端识别;

  • 具有防篡改、防伪功能,保证信息的合法性和有效性

 

2. JWT字符串令牌的组成

一串令牌有三部分组成:Header(头)、Payload(有效载荷)、Signature(签名)

  • 第一部分:Header(头),记录令牌类型、签名算法等。例如:{"alg":HS256”,"type":"JWT”}
  • 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。例如:{“id":"1”,“userame”:"Tom”)
  • 第三部分:Signature(签名),防止Token被篡改、确保安全性,将header、payioad,并加入指定秘钥,通过指定签名算法计算而来,

 具体情况如下图所示:

一定要注意的是:令牌原本是Json格式的数据,是通过一种常见的编码方式(Base64编码),编码成了一串字符串,因此在第二部分Payload(有效载荷)中不要存放一些私密数据,因为他人通过反编码就可以得到这部分数据。

3. 生成JWT令牌

①引入依赖

<!--    引入java-jwt令牌的相关依赖    -->
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version>
</dependency>
<dependency><groupId>org.junit.platform</groupId><artifactId>junit-platform-launcher</artifactId><scope>test</scope>
</dependency>

②利用JWT .create()方法进行生成

@Testpublic void testCreateJWT(){//创建一个map集合作为载荷Map<String, Object> claims = new HashMap<>();//向载荷中添加一些令牌需要携带的信息claims.put("id",1);claims.put("username","大码农123");//生成JWT令牌String token = JWT.create().withClaim("user",claims)//添加载荷.withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12))//设置到期时间.sign(Algorithm.HMAC256("yzx_zxx"));//设置签名部分加密所用的算法,算法中要自定义加密密钥System.out.println(token);}

 输出结果:

token令牌:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuWkp-eggeWGnDEyMyJ9LCJleHAiOjE3MzE3MjAwMzF9.gEep6Ro_0B2uEFvNeyeGzXmZhiXfifFiFPsGAPW5E1g


 

4.验证JWT令牌

代码如下:

@Test//验证tokenpublic void parseToken(){String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +"eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuWkp-eggeWGnDEyMyJ9LCJleHAiOjE3MzE3MjAyMzV9." +"vXCQ9cl4_aMpEUI2C4HB7LmGlP4zvKxRNR95ltwW6U8";//用JWT生成一个token解析对象,并传入指定加密算法和密钥JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("yzx_zxx")).build();//用刚刚生成的解析对象去解析token,生成一个解析后的JWT token的对象DecodedJWT decodedJWT = jwtVerifier.verify(token);//通过解析后的JWT对象,可以获取token中有效载荷部分的信息Map<String, Claim> claims = decodedJWT.getClaims();//此处的claims是token中所有claim的集合,包含了claim01,claim02,claim03....System.out.println(claims.get("user"));}

输出结果:{"id":1,"username":"大码农123"}

若令牌已经失效,则报错如下:

com.auth0.jwt.exceptions.TokenExpiredException: The Token has expired on 2024-08-10T21:19:51Z.

注意事项:

  •  JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的
  • 如果JWT令牌解析校验时报错,则说明JWT令牌被篡改 或 失效了,令牌非法。

在上述token验证的过程中,验证失败的三种情况:

  • 第一种:token字符串中的Header(头部)和Payload(有效载荷)部分如果被篡改了,则会验证失败,抛出如下异常:
//头部被修改
com.auth0.jwt.exceptions.JWTDecodeException: The string {"alg":"HS256","typ":"JWT"}' doesn't have a valid JSON format.//有效载荷被修改
com.auth0.jwt.exceptions.JWTDecodeException: The string "user":{"id":1,"username":"大码农123"},"exp":1723324791}' doesn't have a valid JSON format.
  • 第二种:生成token时设置了密钥,如果在验证token时密钥输入不正确,或者Signature(签名)部分被篡改时,会验证失败,抛出如下异常:
    //验证token时密钥输入错误
    com.auth0.jwt.exceptions.SignatureVerificationException: The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA256
  •  第三种:当token的时效到期时,也会验证失败,抛出如下异常:
    //token过期
    com.auth0.jwt.exceptions.TokenExpiredException: The Token has expired on 2024-08-10T11:32:10Z.

二、利用JWT令牌实现登录功能 

1.配置登录拦截器:

拦截器的作用:拦截所有除用户登录以外的请求,若用户携带token并验证成功后则放行,否则提示用户先进行登录;

下面在IDEA中演示,如何配置登录拦截器:

(1)先创建一个interceptor包,在该包中创建一个登录拦截器LoginInterceptor,用@Component注解将其注入到IOC容器中;

第一种写法:从前端请求的请求头header中获取token并验证

(该写法是在后端登录接口中,将JWT生成的token信息直接返回给前端,再由前端添加到请求头header中)

代码如下:

@Component//通过该注解,将拦截器注入到IOC容器中
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {/*通过HttpServletRequest的实例request,可以访问到所有的前端请求;1.再调用request中的getHeader()方法访问前端请求中的请求头2.请求头中"Authorization"关键字对应的是token字符串*///获取前端请求中的tokenString token = request.getHeader("Authorization");//验证tokentry {Map<String, Object> claims = JWTUtils.parseToken(token);//如果成功获得token中的claims信息,说明token验证成功,返回true(放行,用户已登陆)return true;} catch (Exception e) {//如果捕捉到异常,说明token验证失败,因此无法获得其中的claims信息//将响应状态码设置成401,并返回false(不放行,用户未登陆)response.setStatus(401);return false;}}
}
第二种写法:从前端请求的cookie中获取token并验证

(该写法是在后端登录接口中,将JWT生成的token信息添加到cookie中,然后将cookie添加进HttpServletResponse响应体)

代码如下:

/*** 拦截器*/
@Component
public class LoginInterceptor implements HandlerInterceptor {/*** 除了登录请求以外的其他请求都要进行过滤* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Cookie[] cookies = request.getCookies();if(cookies != null){for (Cookie cookie:cookies){if("token".equals(cookie.getName())){String userToken = cookie.getValue();if(!StringUtils.hasText(userToken)){response.sendError(HttpServletResponse.SC_UNAUTHORIZED);}//解析token看看是否成功try {Map<String,Object> claims = JWTUtils.parseToken(userToken);//将业务数据存入到ThreadLocal中ThreadLocalUtil.set(claims);//如果,get不到则会报错,然后被catch抓取}catch (Exception e){//e.printStackTrace();System.out.println("token信息出错");return false;}return true;  //放行}}}return false;}}
那上述两种写法各有什么优缺点呢?
  • 添加到请求头(Header)的优点:

        ①安全性相对较高:因为 HTTP 请求头在正常情况下不会被浏览器等客户端自动处理(与 Cookie 不同,Cookie 会被浏览器自动添加到请求中),这就减少了跨站脚本攻击(XSS)获取 token 的风险。

        ②适合前后端分离架构:在前后端分离的应用中,前端通过 JavaScript 发送请求时可以方便地在请求头中设置 token。例如,在使用 Axios 库的 Vue.js 项目中,设置请求头携带 token 非常方便,像 Axios 可以通过axios.defaults.headers.common['Authorization'] = token这样的方式统一为所有请求添加包含 token 的请求头。

  • 添加到请求头(Header)的缺点

         跨域问题可能更复杂:当涉及跨域请求时,浏览器会限制对请求头的访问。这时需要后端进行额外的配置,来允许携带特定的请求头。否则浏览器会阻止带有 token 的跨域请求。

  • 添加到 Cookie 的优点

        兼容性好:几乎所有的浏览器都能自动处理 Cookie。对于传统的 Web 应用,不需要额外的客户端代码来处理 token 的传递。这使得开发过程更加简单,尤其是对于那些没有使用复杂前端框架的应用。

  • 添加到 Cookie 的缺点

        安全性风险:容易受到跨站脚本攻击(XSS)。例如,在一个存在漏洞的 Web 页面中,攻击者注入的脚本可以通过document.cookie获取所有的 Cookie 信息,包括 token。

(2)通过配置类来配置登录拦截器

建一个config包,在该包定义一个WebConfig配置类中,用@Configuration注解标明该类是一个配置类,作用是配置登录拦截器

配置类中的代码如下:

  • addPathPatterns("/**")方法中配置要拦截的请求,/**代表所有请求
  • excludePathPatterns()方法中,指定那些请求不要拦截,如登录、静态资源等请求通常不拦截
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;/*** @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor)//添加拦截器.addPathPatterns("/**")  //配置要拦截的路径.excludePathPatterns("/login/**","/static/**","/templates/**","/");//配置不拦截的路径}
}

 至此,在SpringBoot项目中,一个登录拦截器就配置完成了;那么接下来我们演示如何实现登录的接口;

2.实现后端的登录接口

(1)借助JWTUtils工具类

首先,我们把生成JWT令牌和验证JWT令牌这两段代码封装成一个工具类,方便我们使用

JWTUtis工具类:

package com.yzx.core.utils;import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;import java.util.Date;
import java.util.Map;public class JWTUtils {private static final String KEY = "yzx_zxx";//接受用户信息,返回生成的tokenpublic static String getToken(Map<String, Object> claims){return JWT.create().withClaim("claims",claims).withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12))//设置有效期.sign(Algorithm.HMAC256(KEY));}//接收并验证token,并返回token所携带的信息public static Map<String,Object> parseToken(String token){return JWT.require(Algorithm.HMAC256(KEY)).build().verify(token).getClaim("claims").asMap();}
}

        这样做的好处:在一个应用程序中,可能有多个地方需要生成或验证 JWT 令牌。这样我们代码复用性就会提高了;如果 JWT 令牌的生成或验证逻辑需要修改,那么我们直接修改该工具类即可。

(2)在LoginController中实现登录的接口代码:
@PostMapping("/loginManage")@ResponseBodypublic Result<String> login(String username,String password, HttpServletRequest request, HttpServletResponse response){HttpSession session = request.getSession();User loginUser = userDao.findByUsername(username);if (loginUser == null){return Result.error("用户不存在!");}if (password.equals(loginUser.getPassword())){Map<String, Object> claims = new HashMap();claims.put("email",loginUser.getEmail());claims.put("id",loginUser.getId());String token = JWTUtils.getToken(claims);//将token信息设置如cookie当中Cookie cookie = new Cookie("token",token)cookie.setPath("/");   //设置浏览器的访问路径cookie.setMaxAge(36000); //设置cookie的过期时间response.addCookie(cookie);return Result.success("登录成功");}else{return Result.error("密码错误!");}}

       (其中的User是用来封装用户信息的普通pojo类。 )

 注意:上述代码,演示的是将JWT生成的token信息添加到cookie中,然后将cookie添加进HttpServletResponse响应体,对应的是拦截器的第二种写法:从前端请求的cookie中获取token并验证;    

(大家可以自己探索一下,将token信息返回给前端,然后添加进请求头的写法如何实现)

三、在登录中添加验证码功能

        上面,我们已经完整的完成了一个带有 JWT 令牌验证的登录功能;在实际应用场景中,为了进一步增强登录的安全性,防止恶意攻击,如暴力破解用户密码等情况的发生,我们需要在登录流程中添加验证码功能。

1.验证码生成方式选择

        我们可以采用多种方式来生成验证码。比较常见且简单的的方法是使用图形验证码,通过生成包含随机数字和字母组合的图片来实现;

实现图形验证码的工具类:VerifyCodeUtil 

(完整代码资源,附在文章开头,点击可下载,大家也可自行复制下面的代码)

package com.yzx.springbootdemo.utils;import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;import javax.imageio.ImageIO;/*** 	生成验证码的工具类* @author 王**/
public class VerifyCode {private int w = 70;//设置缓冲区的宽private int h = 35;//设置缓冲区的宽private Random r = new Random();{"宋体", "华文楷体", "黑体", "华文新魏", "华文隶书", "微软雅黑", "楷体_GB2312"}private String[] fontNames  = {"宋体", "华文楷体", "黑体", "华文新魏", "华文隶书", "微软雅黑", "楷体_GB2312"};//源private String codes  = "123456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ";// 背景颜色private Color bgColor  = new Color(255, 255, 255);// 保存随机生成的图片当中的内容。private String text ;// 随机生成颜色private Color randomColor () {int red = r.nextInt(150);int green = r.nextInt(150);int blue = r.nextInt(150);return new Color(red, green, blue);}// 随机生成字体private Font randomFont () {int index = r.nextInt(fontNames.length);String fontName = fontNames[index];//根据随机的索引,获取随机字体int style = r.nextInt(4);//0,1,2,3, 0:没有任何样式,1,加粗,2,斜体,3,加粗和斜体  PLAIN(0)、BOLD(1)、ITALIC(2) 或 BOLD+ITALIC(3)。int size = r.nextInt(5) + 24; //随机生成字号return new Font(fontName, style, size);}// 画干扰线private void drawLine (BufferedImage image) {int num  = 3;//花三条干扰线Graphics2D g2 = (Graphics2D)image.getGraphics();for(int i = 0; i < num; i++) {int x1 = r.nextInt(w);int y1 = r.nextInt(h);int x2 = r.nextInt(w);int y2 = r.nextInt(h);g2.setStroke(new BasicStroke(1.5F));g2.setColor(Color.BLUE); //给干扰线设置了颜色g2.drawLine(x1, y1, x2, y2);//划线}}//随机生成字符private char randomChar () {int index = r.nextInt(codes.length());return codes.charAt(index);}// 得到一个缓冲区private BufferedImage createImage () {// 获取一个缓冲区BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);// 得到一个画笔Graphics2D g2 = (Graphics2D)image.getGraphics();// 设置画笔的颜色 白颜色g2.setColor(this.bgColor);// 填充图片的缓冲区。g2.fillRect(0, 0, w, h);// 将缓冲区返回。return image;}// 调用该方法,可以得到验证码public BufferedImage getImage () {BufferedImage image = createImage();//创建图片的缓冲区Graphics2D g2 = (Graphics2D)image.getGraphics();//得到绘制环境(画笔)StringBuilder sb = new StringBuilder();//定义一个容器,用来装在生成的验证码//向图片当中画四个字符for(int i = 0; i < 4; i++)  {//循环四次,每次生成一个字符String s = randomChar() + "";//随机成成一个字符sb.append(s); //将生成的字符放在缓冲区float x = i * 1.0F * w / 4; //设置当前字符的x轴坐标g2.setFont(randomFont()); //设置随机生成的字体g2.setColor(randomColor()); //设置字符的随机颜色g2.drawString(s, x, h-5); //画图}this.text = sb.toString(); //随机生成的图片的内容复制给this.textdrawLine(image); //画干扰线return image;}// 获取图片当中的内容public String getText() {return text;}// 保存图片到指定的输出流public static void output (BufferedImage image, OutputStream out)throws IOException {ImageIO.write(image, "JPEG", out);}
}

该工具类VerifyCode主要用于生成图形验证码相关的功能,具体如下:

  • 获取验证码内容getText()方法用于获取生成的验证码图片中的字符内容,即返回text变量的值。(该方法用于后端获取验证码内容)
  • 保存图片output()方法是一个静态方法,用于将生成的BufferedImage格式的验证码图片以 "JPEG" 格式保存到指定的输出流out中;(该方法用于将验证码的图片输出到前端页面 )

 下面演示实现过程:

(1)前端代码:

点击更换验证码功能:通过reloadCode函数实现点击验证码图片更换验证码的功能。当用户点击验证码图片时,函数会获取当前的时间戳(var time = new Date().getTime();),然后将img标签的src属性重新设置为/login/VerifyCode?id=加上获取到的时间戳(document.getElementById("imgCode").src="/login/VerifyCode?id="+time;)。这样每次点击图片时,由于src属性值发生变化(通过添加不同的时间戳来区分每次请求),图片标签就会重新调用后端/login/VerifyCode接口来获取新的验证码图片,实现了验证码的动态更新。

<div><input type="text" name="Imgcode" placeholder="验证码" id="verifyCode" class="input-item"/><img alt="验证码" src="/login/VerifyCode" id="imgCode" onclick="reloadCode()">
</div>
<script>//点击更换验证码function reloadCode(){var time = new Date().getTime();//鼠标每单击一次验证码图片,设置img标签的src属性,然后图片标签就会调用src指向的资源document.getElementById("imgCode").src="/login/VerifyCode?id="+time;}
</script>
(2)后端代码(验证码生成接口)

/login/VerifyCode

/*** 获取验证码* @param request* @param response* @throws Exception*/@RequestMapping("/VerifyCode")public void VerifyCode(HttpServletRequest request, HttpServletResponse response) throws Exception {request.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");VerifyCode code = new VerifyCode();BufferedImage image = code.getImage();  //得到验证码图片String text = code.getText(); //得到验证码的文本//保存验证码的值HttpSession session = request.getSession(); //将验证码的值存放到session中session.setAttribute("verify", text);VerifyCode.output(image, response.getOutputStream()); //将验证码图片输出到前端页面}

       在上述几段代码中,实现了前端展示验证码图片、可点击更新验证码,后端生成验证码图片和文本、存储验证码文本并将图片输出到前端的完整流程,是一个常见的验证码功能实现方式。
 

完整的,附带验证码和JWT令牌验证功能的登录流程,完整代码如下:

1.前端页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>高考帮登录</title><script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<!--	<script src="https://cdn.staticfile.org/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>-->
<!--    <script src="../static/js/test.js" defer></script>--><style>* {margin: 0;padding: 0;}html {height: 100%;}body {height: 100%;}.container {height: 100%;background-image: linear-gradient(to right, rgb(54,200,180), #a6eea6);}.login-wrapper {background-color: #fff;width: 358px;height: 588px;border-radius: 15px;padding: 0 50px;position: relative;left: 50%;top: 50%;transform: translate(-50%, -50%);}.header {font-size: 38px;font-weight: bold;text-align: center;line-height: 90px;}.header img{margin-top: 50px;border-radius: 15px;width: 200px;}.input-item {display: block;width: 100%;margin-bottom: 20px;border: 0;padding: 10px;border-bottom: 1px solid rgb(128, 125, 125);font-size: 15px;outline: none;}.input-item:placeholder {text-transform: uppercase;}.btn {text-align: center;padding: 10px;width: 100%;margin-top: 40px;background-image: linear-gradient(to right, rgb(54,200,180), #a6eea6);color: #fff;}.btn:hover{opacity:0.7;}.msg {text-align: center;line-height: 88px;}a {text-decoration-line: none;color: #abc1ee;}</style>
</head>
<body><div class="container"><div class="login-wrapper"><div class="header"><img src="../static/img/志愿帮01.png" alt=""></div><form id="form1"  class="form_box"><div class="layui-form layui-row layui-col-space16"><div class="form-wrapper input-item" ><div class="layui-col-md6">登录身份<select id = "type"><option value="">请选择</option><option value="111">管理员</option><option value="000">用户</option></select></div></div><input type="text" name="username" id="username" placeholder="用户名" class="input-item"><input type="password" name="password" id="password" placeholder="password" class="input-item"><input type="text" name="Imgcode" placeholder="验证码" id="verifyCode" class="input-item"/><img alt="验证码" src="/login/VerifyCode" id="imgCode" onclick="reloadCode()"><div class="btn" onclick="login()">Login</div></div></form><div class="msg">还没有账号?<a href="/login/toRegister">去注册</a></div></div></div><script>//更换验证码function reloadCode(){var time = new Date().getTime();//鼠标每单击一次验证码图片,设置img标签的src属性,然后图片标签就会调用src指向的资源document.getElementById("imgCode").src="/login/VerifyCode?id="+time;}//提交function login(){var username = $("#username").val();var password = $("#password").val();var verifyCode = $("#verifyCode").val();var type = $("#type").val();console.log(type)if(username === "" || username.length===0){alert("用户名为空");}else if(password === "" || password.length===0){alert("密码为空");}else if(verifyCode === "" || verifyCode.length===0){alert("验证码为空");}else{$.ajax({url: "/login/loginManage",   // 地址type:"post",data:{"username":username,"password":password,"Imgcode":verifyCode},success: function (value){console.log(value)if(value.code===0){if(type==111){window.location.href = '/login/toManager';}else{//添加token,并且跳转到system页面window.location.href = '/system/';}}else if(value.code === 1){alert(value.message);}},error:function (){alert("网络出错");}});}}
</script></body>
</html>

2.后端接口

@Controller
@RequestMapping("/login")
public class LoginController{@Autowiredprivate UserDao userDao;/*** 1.获取验证码的接口* @param request* @param response* @throws Exception*/@RequestMapping("/VerifyCode")public void VerifyCode(HttpServletRequest request, HttpServletResponse response) throws Exception {request.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");VerifyCode code = new VerifyCode();BufferedImage image = code.getImage();  //得到验证码图片String text = code.getText(); //得到验证码的文本//保存验证码的值HttpSession session = request.getSession(); //将验证码的值存放到session中session.setAttribute("verify", text);VerifyCode.output(image, response.getOutputStream()); //将验证码图片输出到页面}/*** 2.登录接口* @param login* @param request* @param response* @return*/@PostMapping("/loginManage")@ResponseBodypublic Result<String> login(Login login, HttpServletRequest request, HttpServletResponse response){HttpSession session = request.getSession();
//        System.out.println("实际的"+session.getAttribute("verify"));
//        System.out.println("我输入的:"+login.getImgcode());if(session.getAttribute("verify").equals(login.getImgcode())){//验证码正确再去做登录判断UserloginUser = userDao.findByUsername(login.getUsername());if (loginUser == null){return Result.error("用户不存在!");}System.out.println("我输入的密码"+login.getPassword());System.out.println("数据库的密码"+loginUser.getPassword());System.out.println(login.getPassword()==loginUser.getPassword());if (login.getPassword().equals(loginUser.getPassword())){Map<String, Object> claims = new HashMap();claims.put("email",loginUser.getEmail());claims.put("id",loginUser.getId());String token = JWTUtils.getToken(claims);//将token信息设置如cookie当中Cookie cookie = new Cookie("token",token);cookie.setPath("/");   //设置浏览器的访问路径cookie.setMaxAge(36000); //设置cookie的过期时间response.addCookie(cookie);return Result.success("登录成功");}else{return Result.error("密码错误!");}}else {return Result.error("验证码错误");}}
}

用于实现登录接口而封装的pojo类:login类

package com.yzx.springbootdemo.pojo;public class Login {private Integer id;private String username;private String password;private String Imgcode;private Integer type;//有参和无参构造器...//setter和getter方法....
}

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

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

相关文章

网络常用特殊地址-127.0.0.1

借用Medium博客的一张图 经常在问题解答群里留意到如下关于127.0.0.1的消息 ”如果单机版&#xff0c;不需要配置IP&#xff0c;所有配置IP的地方都写死127.0.0.1就可以” “ip: 根据实际情况填写&#xff08;在 xxx-init.conf 里可以给一个默认值 127.0.0.1 &#xff0c;方便…

【模拟仿真】基于区间观测器的故障诊断与容错控制

摘要 本文提出了一种基于区间观测器的故障诊断与容错控制方法。该方法通过构建区间观测器&#xff0c;实现对系统状态的上下边界估计&#xff0c;从而在存在不确定性和外部噪声的情况下进行高效的故障诊断。进一步地&#xff0c;本文设计了一种容错控制策略&#xff0c;以保证…

CC4学习记录

&#x1f338; CC4 CC4要求的commons-collections的版本是4.0的大版本。 其实后半条链是和cc3一样的&#xff0c;但是前面由于commons-collections进行了大的升级&#xff0c;所以出现了新的前半段链子。 配置文件&#xff1a; <dependency><groupId>org.apach…

自动化报表怎么写

自动化报表设计 标题 日期 筛选器 具体字段自由字段 迷你图 同环比 条件格式 步骤 填充数值 1、先筛选战区日期sumifs(纯数值-注册人数&#xff0c;纯数值-战区列&#xff0c;周报-战区单元格&#xff0c;纯数值-日期&#xff0c;周报-日期单元格) 需要注意⚠️纯数值里的单元格…

魔改log4j2的JsonLayout,支持自定义json格式日志

小伙伴们&#xff0c;你们好&#xff0c;我是老寇&#xff0c;我又回来辣&#xff0c;1个多月不见甚是想念啊&#xff01;&#xff01;&#xff01;跟我一起魔改源码吧 1.自定义json格式【PatternLayout】 大部分教程都是这个&#xff0c;因此&#xff0c;我就简单给个配置&a…

笔记分享: 西安交通大学COMP551705数据仓库与数据挖掘——02. 关联规则挖掘

文章目录 1. \textbf{1. } 1. 基本概念 2. \textbf{2. } 2. 布尔关联规则 2.1. \textbf{2.1. } 2.1. 一些基本概念 2.2. \textbf{2.2.} 2.2. Apriori \textbf{Apriori} Apriori算法 2.3. \textbf{2.3.} 2.3. Apriori \textbf{Apriori} Apriori算法示例 3. \textbf{3. } 3. 多…

基于标签相关性的多标签学习

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

网站小程序app怎么查有没有备案?

网站小程序app怎么查有没有备案&#xff1f;只需要官方一个网址就可以&#xff0c;工信部备案查询官网地址有且只有一个&#xff0c;百度搜索 "ICP备案查询" 找到官方gov.cn网站即可查询&#xff01; 注&#xff1a;网站小程序app备案查询&#xff0c;可通过输入单位…

手撸 chatgpt 大模型:简述 LLM 的架构,算法和训练流程

本节我们自顶向下看看大模型的相关概念&#xff0c;了解其基本架构以及从零手撸大模型的基本流程。自从 openai 释放了 chatgpt 后&#xff0c;人工智能就立马进入了大模型时代&#xff0c;我还记得在此之前 NLP 的处理主要依赖于深度学习的 LSTM&#xff0c;GRU 等模型架构。这…

爬虫——JSON数据处理

第三节&#xff1a;JSON数据处理 在爬虫开发中&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;是最常见的数据格式之一&#xff0c;特别是在从API或动态网页中抓取数据时。JSON格式因其结构简单、可读性强、易于与其他系统交互而广泛应用于前端与后端的数…

SpringBoot集成itext导出PDF

添加依赖 <!-- PDF导出 --><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version></dependency><dependency><groupId>com.itextpdf</groupId>&l…

【快速解决】kafka崩了,重启之后,想继续消费,怎么做?

目录 一、怎么寻找我们关心的主题在崩溃之前消费到了哪里&#xff1f; 1、一个问题&#xff1a; 2、查看消费者消费主题__consumer_offsets 3、一个重要前提&#xff1a;消费时要提交offset 二、指定 Offset 消费 假如遇到kafka崩了&#xff0c;你重启kafka之后&#xff0…

查询DBA_FREE_SPACE缓慢问题

这个是一个常见的问题&#xff0c;理论上应该也算是一个bug&#xff0c;在oracle10g&#xff0c;到19c&#xff0c;我都曾经遇到过&#xff1b;今天在给两套新建的19C RAC添加监控脚本时&#xff0c;又发现了这个问题&#xff0c;在这里记录一下。 Symptoms 环境&#xff1a;…

用OMS进行 OceanBase 租户间数据迁移的测评

基本概念 OceanBase迁移服务&#xff08;&#xff0c;简称OMS&#xff09;&#xff0c;可以让用户在同构或异构 RDBMS 与OceanBase 数据库之间进行数据交互&#xff0c;支持数据的在线迁移&#xff0c;以及实时增量同步的复制功能。 OMS 提供了可视化的集中管控平台&#xff…

IDEA一键部署SpringBoot项目到服务器

安装Alibaba Cloud Toolkit插件 配置部署环境 1&#xff1a;设置服务名称 2&#xff1a;选择文件上传的类型 3:选择打包之后的jar文件 4: 添加需要上传的服务器信息 5:需要上传到服务器的地址 输入绝对路径 6: 选择上传文件后执行的脚本 可以参考另一篇文章 Linux启…

渗透测试之信息收集 DNS主机发现探测方式NetBIOS 协议发现主机 以及相关PorCheck scanline工具的使用哟

目录 主机发现 利用NetBIOS 协议发现主机 利用TCP/UDP发现主机 PorCheck scanline 利用DNS协议发现主机 主机发现 信息收集中的一项重要工作是发现内网中的主机、数据库、IP段网络设备、安全设备等资产&#xff0c;以便于更快地获取更多权限和密码&#xff0c;更加接近红…

打造专业问答社区:Windows部署Apache Answer结合cpolar实现公网访问

文章目录 前言1. 本地安装Docker2. 本地部署Apache Answer2.1 设置语言选择简体中文2.2 配置数据库2.3 创建配置文件2.4 填写基本信息 3. 如何使用Apache Answer3.1 后台管理3.2 提问与回答3.3 查看主页回答情况 4. 公网远程访问本地 Apache Answer4.1 内网穿透工具安装4.2 创建…

基于java的医院门诊信息管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

67页PDF |埃森哲_XX集团信息发展规划IT治理优化方案(限免下载)

一、前言 这份报告是埃森哲_XX集团信息发展规划IT治理优化方案&#xff0c;报告中详细阐述了XX集团如何优化IT治理结构以适应新的要求。报告还分析了集团管控模式的变化&#xff0c;提出了六大业务中心的差异化管控策略&#xff0c;并探讨了这些变化对IT治理模式的影响。报告进…

【AI大模型】ELMo模型介绍:深度理解语言模型的嵌入艺术

学习目标 了解什么是ELMo.掌握ELMo的架构.掌握ELMo的预训练任务.了解ELMo的效果和成绩.了解ELMo的优缺点. 目录 &#x1f354; ELMo简介 &#x1f354; ELMo的架构 2.1 总体架构 2.2 Embedding模块 2.3 两部分的双层LSTM模块 2.4 词向量表征模块 &#x1f354; ELMo的预…