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(LoginUserHolder.getLoginUser().getUserId());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;
}

common.login.LoginUserHolder

package com.orchids.lovehouse.common.login;/*** @Author qwh* @Date 2024/6/2 22:15*/
public class LoginUserHolder {public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();public static void setLoginUser(LoginUser loginUser) {threadLocal.set(loginUser);}public static LoginUser getLoginUser() {return threadLocal.get();}public static void clear() {threadLocal.remove();}
}

common.login.LoginUser

package com.orchids.lovehouse.common.login;import lombok.AllArgsConstructor;
import lombok.Data;/*** @Author qwh* @Date 2024/6/2 22:16*/
@Data
@AllArgsConstructor
public class LoginUser {private Long userId;private String username;
}

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

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

相关文章

AWS联网和内容分发服务

概况 VPC Amazon Virtual Private Cloud (Amazon VPC) 让您能够全面地控制自己的虚拟网络环境&#xff0c;包括资源放置、连接性和安全性。首先在 AWS 服务控制台中设置 VPC。然后&#xff0c;向其中添加资源&#xff0c;例如 Amazon Elastic Compute Cloud (EC2) 和 Amazon …

数据分析必备:一步步教你如何用Pandas做数据分析(15)

1、Pandas 数据丢失 Pandas 数据丢失的操作实例 在现实生活中&#xff0c;数据丢失始终是一个问题。机器学习和数据挖掘等领域在模型预测的准确性方面面临严重问题&#xff0c;因为缺少值会导致数据质量较差。在这些领域中&#xff0c;缺失值处理是使模型更准确和有效的主要重…

定个小目标之每天刷LeetCode热题(7)

今天这道题是道简单题&#xff0c;使用双指针进行迭代即可&#xff0c;画了下草图如下 代码如下 class Solution {public ListNode reverseList(ListNode head) {if (head null || head.next null) {return head;}ListNode p head, q head.next, temp null;while (q ! nu…

【Python如何将EXCEL拆分】

文章目录 Python将一个EXCEL表拆分多个excel表Python将一个EXCEL表中一个sheet拆分多个sheet表 Python将一个EXCEL表拆分多个excel表 在Python中&#xff0c;你可以使用pandas库来读取Excel文件&#xff0c;并将一个大的Excel表格&#xff08;工作表&#xff09;拆分成多个单独…

Writerside生成在线帮助文档或用户手册软件基础使用教程

Writerside是JetBrains出的一个技术文档工具&#xff0c;既能用在JetBrains IDE上&#xff0c;也能单独用。它能帮你轻松写、建、测、发技术文档&#xff0c;像产品说明、API参考、开发指南等都能搞定。 特点&#xff1a; 文档即代码&#xff1a;它让你像管代码一样管文档&…

【大数据Spark】常见面试题(万字!建议收藏)

文章目录 入门级中等难度中高级难度数据倾斜解决方法 入门级 什么是Apache Spark&#xff1f;它与传统的MapReduce有何不同&#xff1f; Apache Spark是一个开源的分布式计算系统&#xff0c;它提供了高效的数据处理和分析能力。与传统的MapReduce相比&#xff0c;Spark具有更快…

海光CPU:国产信创的“芯“动力解读

国产信创CPU-海光CPU CPU&#xff1a;信创根基&#xff0c;国之重器 国产CPU形成三大阵营&#xff1a;自主架构、x86及ARM。自主阵营中&#xff0c;龙芯和申威以LoongArch和SW-64为基石&#xff1b;ARM阵营由鲲鹏、飞腾主导&#xff0c;依托ARM授权研发处理器&#xff1b;x86阵…

红帽练习 之逻辑卷 pv lv gv

逻辑卷习题 1 在/dev/sdb 存储设备上创建物理设备分区 创建2个大小各为256MB的分区 并设置为linux lvm类型 使用first 和second 作为这些分区的名称 parted /dev/sdb mklabel gpt parted /dev/sdb primary mkpart first 1M 256M parted /dev/sdb set 1 …

【Linux|数据恢复】extundelete和ext4magic数据恢复工具使用

环境&#xff1a;Centos7.6_x86 一、extundelete工具 1、extundelete介绍 Extundelete 是一个数据恢复工具&#xff0c;用于从 ext3 或 ext4 分区中恢复删除文件。根据官网0.2.4版本介绍是支持ext4&#xff0c;但实际上使用发现ext4格式有些问题&#xff0c;会报以下错误&…

动态SQL IF语句

IF语句学习 第一种写法(标准) 我们先来看以下标准写法: select * from .. <where> <if test""> and ....... <if test""> and ....... <where> 我们用了一个where标签 , 内嵌if语句 第二种写法: 这是第二种写法:不用where标…

大降分!重邮计算机专硕复试线大降50分!重庆邮电计算机考研考情分析!

重庆邮电大学&#xff08;Chongqing University of Posts and Telecommunications&#xff09;简称重邮&#xff0c;坐落于中国重庆市主城区南山风景区内&#xff0c;是中华人民共和国工业和信息化部与重庆市人民政府共建的教学研究型大学&#xff0c;入选国家“中西部高校基础…

一篇文章搞懂Go语言切片底层原理(图文并茂+举例讲解)

1. 切片和数组的底层关系 Go语言切片的数据结构是一个结构体&#xff1a; type slice struct {array unsafe.Pointerlen intcap int }Go语言中切片的内部结构包含地址、大小和容量。将数组比喻成一个蛋糕&#xff0c;那么切片就是需要切的那一块&#xff0c;而那一块的的…

c++学生管理系统

想要实现的功能 1&#xff0c;可以增加学生的信息&#xff0c;包括&#xff08;姓名&#xff0c;学号,c成绩&#xff0c;高数成绩&#xff0c;英语成绩&#xff09; 2&#xff0c;可以删除学生信息 3&#xff0c;修改学生信息 4&#xff0c;显示所有学生信息 5&#xff0c…

支持AMD GPU的llm.c

anthonix/llm.c: LLM training in simple, raw C/HIP for AMD GPUs (github.com) llm.c for AMD devices This is a fork of Andrej Karpathys llm.c with support for AMD devices. 性能 在单个7900 XTX显卡上使用默认设置&#xff0c;目前的训练步骤耗时约为79毫秒&#x…

Docker的安装、启动和配置镜像加速

前言&#xff1a; Docker 分为 CE 和 EE 两大版本。CE 即社区版&#xff08;免费&#xff0c;支持周期 7 个月&#xff09;&#xff0c;EE 即企业版&#xff0c;强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月。 而企业部署一般都是采用Linux操作系统&#xff0c;而…

【软件设计师】2022年上半年真题解析

​​冯诺依曼计算机体系结构的基本特点是&#xff1a; A. 程序指令和数据都采用二进制表示 - 这是正确的&#xff0c;因为冯诺依曼架构下的计算机使用二进制形式来表示和处理所有信息&#xff0c;包括指令和数据。 B. 程序指令总是存储在主存中&#xff0c;而数据则存储在高速…

Java基础语法详解——入门学习教程

Java 基础 目录 一、数据类型 基本类型包装类型缓存池 二、String 概览不可变的好处String, StringBuffer and StringBuilder String Poolnew String(“abc”) 三、运算 参数传递float 与 double隐式类型转换switch 四、关键字 finalstatic 五、Object 通用方法 概览equals()ha…

深入解析 MongoDB Map-Reduce:强大数据聚合与分析的利器

Map-Reduce 是一种用于处理和生成大数据集的方法&#xff0c;MongoDB 支持 Map-Reduce 操作以执行复杂的数据聚合任务。Map-Reduce 操作由两个阶段组成&#xff1a;Map 阶段和 Reduce 阶段。 基本语法 在 MongoDB 中&#xff0c;可以使用 db.collection.mapReduce() 方法执行…

IsoBench:多模态基础模型性能的基准测试与优化

随着多模态基础模型的快速发展&#xff0c;如何准确评估这些模型在不同输入模态下的性能成为了一个重要课题。本文提出了IsoBench&#xff0c;一个基准数据集&#xff0c;旨在通过提供多种同构&#xff08;isomorphic&#xff09;表示形式的问题&#xff0c;来测试和评估多模态…

算法(十三)回溯算法---N皇后问题

文章目录 算法概念经典例子 - N皇后问题什么是N皇后问题&#xff1f;实现思路 算法概念 回溯算法是类似枚举的深度优先搜索尝试过程&#xff0c;主要是再搜索尝试中寻找问题的解&#xff0c;当发生不满足求解条件时&#xff0c;就会”回溯“返回&#xff08;也就是递归返回&am…