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()方法进行生成
@Test
public 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
//验证token
public 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 {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/*通过HttpServletRequest的实例request,可以访问到所有的前端请求;
1.再调用request中的getHeader()方法访问前端请求中的请求头
2.请求头中"Authorization"关键字对应的是token字符串
*/
//获取前端请求中的token
String token = request.getHeader("Authorization");
//验证token
try {
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
*/
@Override
public 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 {
@Autowired
private LoginInterceptor loginInterceptor;
/**
* @param registry
*/
@Override
public 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";
//接受用户信息,返回生成的token
public 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")
@ResponseBody
public 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.text
drawLine(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{
@Autowired
private 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")
@ResponseBody
public 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方法....
}