spring boot jwt 实现用户登录完整java

spring boot jwt 实现用户登录完整java

登录校验逻辑

用户登录的校验逻辑分为三个主要步骤,分别是校验验证码校验用户状态校验密码,具体逻辑如下

  • 前端发送usernamepasswordcaptchaKeycaptchaCode请求登录。
  • 判断captchaCode是否为空,若为空,则直接响应验证码为空;若不为空进行下一步判断。
  • 根据captchaKey从Redis中查询之前保存的code,若查询出来的code为空,则直接响应验证码已过期;若不为空进行下一步判断。
  • 比较captchaCodecode,若不相同,则直接响应验证码不正确;若相同则进行下一步判断。
  • 根据username查询数据库,若查询结果为空,则直接响应账号不存在;若不为空则进行下一步判断。
  • 查看用户状态,判断是否被禁用,若禁用,则直接响应账号被禁;若未被禁用,则进行下一步判断。
  • 比对password和数据库中查询的密码,若不一致,则直接响应账号或密码错误,若一致则进行入最后一步。
  • 创建JWT,并响应给浏览器。

请求数据结构

package com.orchids.springmybatisplus.model.entity;import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;/*** @Author qwh* @Date 2024/6/2 22:31*/
@Data
@Schema(description = "后台管理系统登录信息")
public class LoginVo {@Schema(description="用户名")private String username;@Schema(description="密码")private String password;@Schema(description="验证码key")private String captchaKey;@Schema(description="验证码code")private String captchaCode;
}

枚举类

package com.orchids.lovehouse.common.result;import lombok.Getter;/*** 统一返回结果状态信息类*/
@Getter
public enum ResultCodeEnum {SUCCESS(200, "成功"),FAIL(201, "失败"),PARAM_ERROR(202, "参数不正确"),SERVICE_ERROR(203, "服务异常"),DATA_ERROR(204, "数据异常"),ILLEGAL_REQUEST(205, "非法请求"),REPEAT_SUBMIT(206, "重复提交"),DELETE_ERROR(207, "请先删除子集"),ADMIN_ACCOUNT_EXIST_ERROR(301, "账号已存在"),ADMIN_CAPTCHA_CODE_ERROR(302, "验证码错误"),ADMIN_CAPTCHA_CODE_EXPIRED(303, "验证码已过期"),ADMIN_CAPTCHA_CODE_NOT_FOUND(304, "未输入验证码"),ADMIN_ACCOUNT_NOT_EXIST(330,"用户不存在"),ADMIN_LOGIN_AUTH(305, "未登陆"),ADMIN_ACCOUNT_NOT_EXIST_ERROR(306, "账号不存在"),ADMIN_ACCOUNT_ERROR(307, "用户名或密码错误"),ADMIN_ACCOUNT_DISABLED_ERROR(308, "该用户已被禁用"),ADMIN_ACCESS_FORBIDDEN(309, "无访问权限"),APP_LOGIN_AUTH(501, "未登陆"),APP_LOGIN_PHONE_EMPTY(502, "手机号码为空"),APP_LOGIN_CODE_EMPTY(503, "验证码为空"),APP_SEND_SMS_TOO_OFTEN(504, "验证法发送过于频繁"),APP_LOGIN_CODE_EXPIRED(505, "验证码已过期"),APP_LOGIN_CODE_ERROR(506, "验证码错误"),APP_ACCOUNT_DISABLED_ERROR(507, "该用户已被禁用"),TOKEN_EXPIRED(601, "token过期"),TOKEN_INVALID(602, "token非法");private final Integer code;private final String message;ResultCodeEnum(Integer code, String message) {this.code = code;this.message = message;}
}

全局异常处理

package com.orchids.lovehouse.common.exception;import com.orchids.lovehouse.common.result.ResultCodeEnum;
import lombok.Data;/*** @Author qwh* @Date 2024/6/1 20:18*/
@Data
public class LovehouseException extends RuntimeException {//异常状态码private Integer code;/*** 通过状态码和错误消息创建异常对象* @param message* @param code*/public LovehouseException(String message, Integer code) {super(message);this.code = code;}/*** 根据响应结果枚举对象创建异常对象* @param resultCodeEnum*/public LovehouseException(ResultCodeEnum resultCodeEnum) {super(resultCodeEnum.getMessage());this.code = resultCodeEnum.getCode();}@Overridepublic String toString() {return "LovehouseException{" +"code=" + code +", message=" + this.getMessage() +'}';}
}

配置所需依赖
登录接口需要为登录成功的用户创建并返回JWT,本项目使用开源的JWT工具Java-JWT,配置如下,具体内容可参考官方文档。

  • 引入Maven依赖
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.2</version>
</dependency><dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.2</version><scope>runtime</scope>
</dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.2</version><scope>runtime</scope>
</dependency>

创建JWT和工具类 common.utils.JwtUtil

package com.orchids.lovehouse.common.utils;import com.orchids.lovehouse.common.exception.LovehouseException;
import com.orchids.lovehouse.common.result.ResultCodeEnum;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;import javax.crypto.SecretKey;
import java.util.Date;/*** @Author qwh* @Date 2024/6/2 21:01*/
public class JwtUtil {private static long tokenExpiration = 60  * 60 * 1000L;public static SecretKey secretKey = Keys.hmacShaKeyFor("M0PKKI6pYGVWWfDZw90a0lTpGYX1d4AQ".getBytes());public static String createToken(Long userId,String username){String token  = Jwts.builder().setSubject("USER_INFO").setExpiration(new Date(System.currentTimeMillis()+tokenExpiration)).claim("userId",userId).claim("username",username).signWith(secretKey,SignatureAlgorithm.HS256).compact();return token;}public static Claims parsToken(String token){if (token==null) {throw new LovehouseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);}try {JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();Jws<Claims> claims = jwtParser.parseClaimsJws(token);return claims.getBody();} catch (ExpiredJwtException e) {throw new LovehouseException(ResultCodeEnum.TOKEN_EXPIRED);} catch (JwtException e){throw new LovehouseException(ResultCodeEnum.TOKEN_INVALID);}}public static void main(String[] args) {System.out.println(createToken(2l,"user"));}
}

controller逻辑

package com.orchids.lovehouse.web.admin.controller.login;import com.orchids.lovehouse.common.login.LoginUserHolder;
import com.orchids.lovehouse.common.result.Result;
import com.orchids.lovehouse.common.utils.JwtUtil;
import com.orchids.lovehouse.web.admin.service.LoginService;
import com.orchids.lovehouse.web.admin.vo.login.CaptchaVo;
import com.orchids.lovehouse.web.admin.vo.login.LoginVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserInfoVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserItemVo;
import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@Tag(name = "后台管理系统登录管理")
@RestController
@RequestMapping("/admin")
public class LoginController {@Autowiredprivate LoginService loginService;@Operation(summary = "获取图形验证码")@GetMapping("login/captcha")public Result<CaptchaVo> getCaptcha() {CaptchaVo captcha = loginService.getCaptcha();return Result.ok(captcha);}@Operation(summary = "登录")@PostMapping("login")public Result<String> login(@RequestBody LoginVo loginVo) {String token =  loginService.login(loginVo);return Result.ok(token);}@Operation(summary = "获取登陆用户个人信息")@GetMapping("info")public Result<SystemUserInfoVo> info () {SystemUserInfoVo systemUserInfo  = loginService.getLoginUserInfo();return Result.ok(systemUserInfo);}
}

service逻辑

package com.orchids.lovehouse.web.admin.service;import com.orchids.lovehouse.web.admin.vo.login.CaptchaVo;
import com.orchids.lovehouse.web.admin.vo.login.LoginVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserInfoVo;public interface LoginService {CaptchaVo getCaptcha();String login(LoginVo loginVo);SystemUserInfoVo getLoginUserInfo(Long userId);
}

sreviceImpl

package com.orchids.lovehouse.web.admin.service.impl;import com.orchids.lovehouse.common.constant.RedisConstant;
import com.orchids.lovehouse.common.exception.GlobalExceptionHandler;
import com.orchids.lovehouse.common.exception.LovehouseException;
import com.orchids.lovehouse.common.result.ResultCodeEnum;
import com.orchids.lovehouse.common.utils.JwtUtil;
import com.orchids.lovehouse.model.entity.SystemUser;
import com.orchids.lovehouse.model.enums.BaseStatus;
import com.orchids.lovehouse.web.admin.mapper.SystemUserMapper;
import com.orchids.lovehouse.web.admin.service.LoginService;
import com.orchids.lovehouse.web.admin.vo.login.CaptchaVo;
import com.orchids.lovehouse.web.admin.vo.login.LoginVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserInfoVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserItemVo;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;import java.util.UUID;
import java.util.concurrent.TimeUnit;@Service
public class LoginServiceImpl implements LoginService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate SystemUserMapper systemUserMapper;@Overridepublic CaptchaVo getCaptcha() {SpecCaptcha  specCaptcha = new SpecCaptcha(100, 40, 5);specCaptcha.setCharType(Captcha.TYPE_DEFAULT);String code = specCaptcha.text().toLowerCase();String key = RedisConstant.ADMIN_LOGIN_PREFIX + UUID.randomUUID();String img = specCaptcha.toBase64();stringRedisTemplate.opsForValue().set(key,code,60, TimeUnit.SECONDS);return new CaptchaVo(img,key);}@Overridepublic String login(LoginVo loginVo) {//判断是否输入验证码if (!StringUtils.hasText(loginVo.getCaptchaCode())) {throw new LovehouseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_NOT_FOUND);}//校验验证码String code = stringRedisTemplate.opsForValue().get(loginVo.getCaptchaKey());if (code == null){throw new LovehouseException(ResultCodeEnum.APP_LOGIN_CODE_EXPIRED);}if (!code.equals(loginVo.getCaptchaCode())){throw new LovehouseException(ResultCodeEnum.APP_LOGIN_CODE_ERROR);}//校验用户是否存在SystemUser systemUser = systemUserMapper.selectOneByUsername(loginVo.getUsername());if (systemUser == null) {throw new LovehouseException(ResultCodeEnum.ADMIN_ACCOUNT_NOT_EXIST);}if (systemUser.getStatus() == BaseStatus.DISABLE) {throw new LovehouseException(ResultCodeEnum.ADMIN_ACCOUNT_DISABLED_ERROR);}// 鏍¢獙鐢ㄦ埛瀵嗙爜if (!systemUser.getPassword().equals(DigestUtils.md5DigestAsHex(loginVo.getPassword().getBytes()))) {throw new LovehouseException(ResultCodeEnum.ADMIN_ACCOUNT_ERROR);}// 鍒涘缓骞惰繑鍥瀟okenreturn JwtUtil.createToken(systemUser.getId(),systemUser.getUsername());}@Overridepublic SystemUserInfoVo getLoginUserInfo(Long userId) {SystemUser systemUser = systemUserMapper.selectById(userId);SystemUserInfoVo systemUserInfoVo = new SystemUserInfoVo();systemUserInfoVo.setName(systemUser.getName());systemUserInfoVo.setAvatarUrl(systemUser.getAvatarUrl());return systemUserInfoVo;}}

编写mapper逻辑

SystemUser selectOneByUsername(String username);

mapper.xml

写入对应的sql到xml文件

编写HandlerInterceptor
保护所有受保护的接口增加jwt合法性逻辑 custom.interceptor.AuthenticationInterceptor

package com.orchids.lovehouse.web.admin.custom.interceptor;import com.orchids.lovehouse.common.login.LoginUser;
import com.orchids.lovehouse.common.login.LoginUserHolder;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.orchids.lovehouse.common.utils.JwtUtil;/*** @Author qwh* @Date 2024/6/2 21:55*/
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("access-token");Claims claims = JwtUtil.parsToken(token);Long userId = claims.get("userId", Long.class);String username = claims.get("username", String.class);LoginUserHolder.setLoginUser(new LoginUser(userId,username));return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {LoginUserHolder.clear();}
}

我们约定,前端登录后,后续请求都将JWT,放置于HTTP请求的Header中,其Header的key为access-token
注册HanderInterceptor config.WebMvcConfiguration

package com.orchids.lovehouse.web.admin.custom.config;import com.orchids.lovehouse.web.admin.custom.converter.StringToBaseEnumConverterFactory;
import com.orchids.lovehouse.web.admin.custom.converter.StringToItemTypeConverter;
import com.orchids.lovehouse.web.admin.custom.interceptor.AuthenticationInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {@Autowiredprivate AuthenticationInterceptor authenticationInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(this.authenticationInterceptor).addPathPatterns("/admin/**").excludePathPatterns("/admin/login/**");}
}

获取登录个人信息
查看请求和响应的数据结构

  • 响应的数据结构
@Schema(description = "员工基本信息")
@Data
public class SystemUserInfoVo {@Schema(description = "用户姓名")private String name;@Schema(description = "用户头像")private String avatarUrl;
}

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

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

相关文章

ubuntu 20.04禁止自动更新内核驱动、显卡驱动(使用命令行)

本文目录 一、禁止更新内核1.1 查看当前内核1.2 查看安装的内核1.3 根据需求&#xff0c;使用hold参数禁止固定内核1.4 查询被锁定不更新软件包的状态 二、禁止更新显卡驱动2.1 查看安装的显卡驱动2.2 查看详细的详细的显卡信息2.3 禁止显卡驱动更新2.4 查询显卡是否设置成功 前…

618网络机顶盒哪个牌子好?内行盘点网络机顶盒排行榜

因工作原因每天都在跟各种类型的网络机顶盒打交道&#xff0c;最近超多朋友问我网络机顶盒哪个牌子好&#xff0c;不知道如何挑选网络机顶盒&#xff0c;我将要分享目前最值得入手的网络机顶盒排行榜&#xff0c;想买网络机顶盒可以看看以下这些品牌&#xff1a; ★泰捷WEBOX 6…

【力扣】目标和

一、题目描述 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0c;nums [2, 1] &#xff0c;可以在 2 之前添加 &#xff0c;在 1 之前添加 - &a…

鸿蒙开发HarmonyOS Next 网络框架retrofit 封装 viemodel使用

新手刚开始学习harmonyos开发&#xff0c;之前搞安卓开发习惯使用retrofit,结果在三方库中还真搜到了&#xff0c;然后就模拟学习一下。有不对的地方请指点一下。新手新手 oh-package.json5 引入库 retofit 需要使用2.0.1-rc.0 以上版本&#xff0c;修复了retrofit发送网络请…

变压器绕组内部故障的Simulink仿真

​利用变压器纵联差动保护的Simulink仿真模型是无法进行变压器绕组内部故障仿真的。为了解决这一问题&#xff0c;可将图中的三相变压器模型改变为三个单相变压器 , 在变压器属性框中选中 “三绕组变压器” (Three windings Transformer), 从而构造出一个一次绕组, 两个二次绕组…

《Windows API每日一练》4.2 设备环境

在第三章我们已经使用设备环境句柄在窗口客户区绘图了。在图形输出设备&#xff08;比如屏幕或者打印机&#xff09;上绘制图形&#xff0c;必须首先获取设备环境&#xff0c;即DC的句柄。当 Windows把这个句柄交给你的程序&#xff0c;Windows同时也就给予你使用这个设备的权限…

MySQL学习——管理复制源服务器的SQL语句

管理复制源服务器的语句&#xff0c;主要是指数据库环境中主从复制&#xff08;master-slave replication&#xff09;或主主复制&#xff08;master-master replication&#xff09;的设置。这些设置用于在多个数据库服务器之间同步数据&#xff0c;以实现高可用性、备份或负载…

【TensorFlow深度学习】TensorFlow实现超参数搜索与网格搜索法

TensorFlow实现超参数搜索与网格搜索法 TensorFlow实现超参数搜索与网格搜索法&#xff1a;优化模型性能的艺术引言环境准备与库导入数据准备构建模型函数网格搜索法实现结果分析与讨论高效超参数优化策略简介结论 TensorFlow实现超参数搜索与网格搜索法&#xff1a;优化模型性…

LogicFlow 学习笔记——8. LogicFlow 基础 事件 Event

事件 Event 当我们使用鼠标或其他方式与画布交互时&#xff0c;会触发对应的事件。通过监听这些事件&#xff0c;可以获取其在触发时所产生的数据&#xff0c;根据这些数据来实现需要的功能。详细可监听事件见事件API。 监听事件 lf实例上提供on方法支持监听事件。 lf.on(&…

fs.1.10 ON rockeylinux8 dockerfile模式

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 rockeylinux8 docker上编译安装fs.1.10的流程记录&#xff0c;本文使用dockerfile模式。 环境 docker engine&#xff1a;Version 24.0.6 rockylinux docker&#xff1a;8 freeswitch&#xff1a;v1.10.7 dockerfi…

有没有硅基生命?AGI在哪里?

摘要 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;和生命科学的探索逐渐成为人们关注的焦点。其中&#xff0c;关于硅基生命的可能性与AGI&#xff08;Artificial General Intelligence&#xff0c;即人工通用智能&#xff09;的实现&#xff0c;更是引…

VisionOS的未来愿景:苹果VisionPro创业者的愿望清单

随着苹果公司在增强现实(AR)领域的不断探索,VisionPro作为其前沿产品,已经开始展现出改变我们与数字世界互动方式的潜力。作为一名VisionPro创业者,对未来VisionOS的更新充满了期待,并提出了一系列愿望清单,这些愿望不仅代表了个人的需求,也反映了用户社区对苹果AR生态的…

Qt 槽函数重载时通过函数指针绑定

文章目录 信号槽函数绑定 信号 public slots:void testShow();void testShow(int a);signals:void show(int a);槽函数 void A::testShow(){qDebug() <<"testShow(" QString::number(1) ")" << QThread::currentThreadId(); } void A::tes…

服务器如何远程桌面连接不上,服务器远程桌面连接不上解决办法

服务器远程桌面连接不上&#xff0c;是IT运维中常见的挑战之一。针对这一问题&#xff0c;专业的解决方法通常涉及以下几个方面的排查与操作&#xff1a; 首先&#xff0c;我们需要检查网络连接是否正常。远程桌面连接依赖于稳定的网络连接&#xff0c;因此&#xff0c;确认服务…

c++处理字符串

在C中&#xff0c;std::string类型提供了许多成员函数来处理字符串。以下是一些常用的std::string成员函数&#xff1a; 1.长度&#xff1a; 2.字串&#xff1a; size():返回字符串中的字符数&#xff08;不包括终止的空字符&#xff09;。length():与size()功能相同。empty…

MT1350 分数计算

题目 编写函数&#xff0c;实现分数加减运算并输出结果&#xff0c;注意结果要化为最简分数。不考虑不合理的输入等特殊情况&#xff0c;比如分母不能为0。 格式 输入格式 输入形式A/BC/D或者A/B-C/D&#xff0c;其中ABCD为整型。 输出格式 输出形式X/Y&#xff0c;或-X/…

【中台】数字中台整体建设技术方案(doc原件获取)

1. 中台概念 2. 推动企业组织模式演进 3. 建设方法 4 .中台内容 5. 数据安全体系 中台内容围绕数据中台建设评估、整体框架、数据采集&#xff0c;结构化、半结构化、非结构化的数据采集&#xff0c;数据计算能力、存储计算引擎、数据架构、数据挖掘、各种不同数据层建设、模型…

[大师C语言(第二十九篇)]C语言函数探秘

引言 函数是C语言中的基本单位&#xff0c;用于封装可重用的代码块。在C语言中&#xff0c;函数背后的技术包括函数的定义、调用、参数传递、返回值以及函数的内部实现等。本文将深入探讨C语言函数背后的技术&#xff0c;帮助你更好地理解和应用函数。 第一部分&#xff1a;函…

Visual Studio Code连接VMware虚拟机

1.安装VS Code插件 在拓展中安装插件 Remote-SSH 2.在虚拟机中安装OpenSSH服务器 使用超级用权限(root)更新软件包列表&#xff0c;Debian系统和Ubuntu系统使用apt包管理工具&#xff1a; sudo apt update CentOS系统使用yum或dnf包管理工具&#xff1a; sudo yum update …

“短剧制作新革命!揭秘开发高效系统的秘诀“

1、技术创新的应用&#xff1a; 云原生架构&#xff1a;采用云原生架构&#xff0c;以确保系统的高可用性和可扩展性&#xff0c;同时有效降低维护成本。 边缘计算&#xff1a;通过边缘计算技术&#xff0c;将内容分发到离用户最近的服务器&#xff0c;以减少延迟并提升播放速…