最简单详细的jwt用户登录校验教程(新手必看)

首先简单建张用户表。

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,`username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,`password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

用户注册

业务逻辑:向用户表插入一条用户数据,处理用户已存在的异常

1.确定请求路径,确定dto(请求参数)和vo(响应参数)

请求方式肯定为post,设计为/admin/register

dto设计如下。

加入了非空和正则校验,pom先引入依赖。

  <!--   参数校验     --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
public class UserDTO {@NotEmpty(message = "【名称】不能为空")private String username;@NotEmpty(message = "【密码】不能为空")@Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,32}$", message = "【密码】规则不正确")private String password;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}

vo不需要返回数据,定义一个通用的返回类

public class Result<T> implements Serializable {private Integer code; //编码:1成功,0和其它数字为失败private String msg; //错误信息private T data; //数据public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getData() {return data;}public void setData(T data) {this.data = data;}public static <T> Result<T> success() {Result<T> result = new Result<T>();result.code = 1;return result;}public static <T> Result<T> success(T object) {Result<T> result = new Result<T>();result.data = object;result.code = 1;return result;}public static <T> Result<T> error(String msg) {Result result = new Result();result.msg = msg;result.code = 0;return result;}}

2.编写Controller

@RestController
@RequestMapping("/admin")public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate JwtProperties jwtProperties;/*** 注册*/@PostMapping("/register")public Result Register(@RequestBody UserRegisterDTO userRegisterDTO) {userService.Register(userRegisterDTO);return Result.success();}
}

3.编写Service接口和实现类

public interface UserService {/*** 注册* @param userRegisterDTO*/void Register(UserRegisterDTO userRegisterDTO);
}
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;/*** 用户注册* @param userRegisterDTO*/@Overridepublic void Register(UserRegisterDTO userRegisterDTO) {//判断当前用户是否存在User user = userMapper.getByUsername(userRegisterDTO.getUsername());if(user!=null){throw new AccountFoundException(MessageConstant.ALREADY_EXISTS);}User user1 = new User();//类的属性拷贝BeanUtils.copyProperties(userRegisterDTO,user1);String password = userRegisterDTO.getPassword();//进行md5加密password= DigestUtils.md5DigestAsHex(password.getBytes());user1.setPassword(password);userMapper.insert(user1);}
}

其中我们编写了几个对应的异常类和一个异常信息常量类,并对异常类进行了统一处理

建一个Exception包,放以下几个异常类

基本业务异常类

/*** 业务异常*/
public class BaseException extends RuntimeException {public BaseException() {}public BaseException(String msg) {super(msg);}}

账号已存在异常类

/*** 账号已存在异常*/
public class AccountFoundException extends BaseException{public AccountFoundException() {}public AccountFoundException(String msg) {super(msg);}
}

账号不存在异常类

/*** 账号不存在异常*/
public class AccountNotFoundException extends BaseException {public AccountNotFoundException() {}public AccountNotFoundException(String msg) {super(msg);}}

密码错误异常类

/*** 密码错误异常*/
public class PasswordErrorException extends BaseException {public PasswordErrorException() {}public PasswordErrorException(String msg) {super(msg);}}

异常信息常量类

/*** 信息提示常量类*/
public class MessageConstant {public static final String PASSWORD_ERROR = "密码错误";public static final String ACCOUNT_NOT_FOUND = "账号不存在";public static final String ALREADY_EXISTS = "账号已存在";}

异常统一处理类(建个handler包)

/*** 全局异常处理器,处理项目中抛出的业务异常*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {/*** 捕获业务异常* @param ex* @return*/@ExceptionHandlerpublic Result exceptionHandler(BaseException ex){log.error("异常信息:{}", ex.getMessage());return Result.error(ex.getMessage());}}

4.编写mapper

@Mapper
public interface UserMapper {/*** 根据用户名查询用户* @param username* @return*/@Select("select * from user where username = #{username}")User getByUsername(String username);/*** 插入一条用户* @param user*/@Insert("insert into User(name,username,password)" +"values "+"(#{name},#{username},#{password})")void insert(User user);
}

postman测试,查看数据库

二次注册

用户登录

业务逻辑:根据用户名查询数据库中的数据,处理用户名不存在,密码不正确的异常,如果用户登录成功,则生成jwt令牌(token),以便其他页面访问时进行jwt校验(拦截器)

1.确定请求路径,确定dto(请求参数)和vo(响应参数)

请求方式为post,路径为请求参数为username和password就不特意封装dto类了,vo设计如下。data注解自动装配get、set函数,另外增加注解builder构造器,无参构造方法,有参构造方法

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserVO implements Serializable {private Long id;private String userName;private String name;private String token;}

2.引入jwt

(1)引入pom依赖

        <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version></dependency><dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency>

(2)引入jwt生成类(生成token)

public class JwtUtil {/*** 生成jwt* 使用Hs256算法, 私匙使用固定秘钥** @param secretKey jwt秘钥* @param ttlMillis jwt过期时间(毫秒)* @param claims    设置的信息* @return*/// 使用Keys类生成安全的密钥private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {// 指定签名的时候使用的签名算法,也就是header那部分SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;// 生成JWT的时间long expMillis = System.currentTimeMillis() + ttlMillis;Date exp = new Date(expMillis);String jws = Jwts.builder().setClaims(claims).setExpiration(exp).signWith(SECRET_KEY).compact();return jws;}/*** Token解密** @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个* @param token     加密后的token* @return*/public static Jws<Claims> parseJWT(String secretKey, String token) {Jws<Claims> claims = Jwts.parserBuilder().setSigningKey(SECRET_KEY).build().parseClaimsJws(token);return claims;}}

(3)引入jwt所需属性类,并在配置文件(yml/properties)中配置

@Component
@ConfigurationProperties(prefix = "user.jwt")
@Data
public class JwtProperties {/*** 用户生成jwt令牌相关配置*/private String adminSecretKey;private long adminTtl;private String adminTokenName;}

user.jwt.admin-secret-key=itcast
user.jwt.admin-ttl=7200000
user.jwt.admin-token-name=token

(4)设置threadlocal类(用于获取,设置,移除当前用户id)

public class BaseContext {public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();public static void setCurrentId(Long id) {threadLocal.set(id);}public static Long getCurrentId() {return threadLocal.get();}public static void removeCurrentId() {threadLocal.remove();}}

(5)引入jwt拦截器,用于校验用户token

@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {@Autowiredprivate JwtProperties jwtProperties;/*** 校验jwt** @param request* @param response* @param handler* @return* @throws Exception*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("当前线程的id:"+Thread.currentThread().getId());//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法,直接放行return true;}//1、从请求头中获取令牌String token = request.getHeader(jwtProperties.getAdminTokenName());//2、校验令牌try {log.info("jwt校验:{}", token);Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token).getBody();Long id = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());log.info("当前使用者id:", id);BaseContext.setCurrentId(id);//3、通过,放行return true;} catch (Exception ex) {System.out.println(ex.getStackTrace());//4、不通过,响应401状态码response.setStatus(401);return false;}}
}

(5)开启统一拦截器,添加jwt拦截器,excludePathPatterns中为放行类(浏览器直接能够访问)

/*** 配置类,注册web层相关组件*/
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {@Autowiredprivate JwtTokenAdminInterceptor jwtTokenAdminInterceptor;/*** 注册自定义拦截器** @param registry*/protected void addInterceptors(InterceptorRegistry registry) {log.info("开始注册自定义拦截器...");registry.addInterceptor(jwtTokenAdminInterceptor).addPathPatterns("/**").excludePathPatterns("/admin/register").excludePathPatterns("/admin/login");}}

以上关于jwt的配置完成,我们来编写业务代码。

2.编写Controller层

 /*** 登录** @param userDTO* @return*/@PostMapping("/login")public Result<UserVO> login(@RequestBody UserDTO userDTO) {User user = userService.login(userDTO);//登录成功后,生成jwt令牌Map<String, Object> claims = new HashMap<>();claims.put(JwtClaimsConstant.USER_ID, user.getId());String token = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(),jwtProperties.getAdminTtl(),claims);//构造返回类UserVO userLoginVO = UserVO.builder().id(user.getId()).userName(user.getUsername()).name(user.getName()).token(token).build();return Result.success(userLoginVO);}

User实体类如下

@Data
public class User {private Long id;private String username;private String name;private String password;}

3.编写Service层

 /*** 登录* @param userDTO* @return*/User login(UserDTO userDTO);
 /*** 用户登录** @param* @return*/public User login(UserDTO userDTO) {String username = userDTO.getUsername();String password = userDTO.getPassword();//1、根据用户名查询数据库中的数据User user = userMapper.getByUsername(username);//2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)if (user == null) {//账号不存在throw new IllegalArgumentException(MessageConstant.ACCOUNT_NOT_FOUND);}//密码比对//进行md5加密password= DigestUtils.md5DigestAsHex(password.getBytes());if (!password.equals(user.getPassword())) {//密码错误throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);}//3、返回实体对象return user;}

postman测试,完结撒花。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

C++ 编译体系入门指北

前言 之从入坑C之后&#xff0c;项目中的编译构建就经常跟CMake打交道&#xff0c;但对它缺乏系统的了解&#xff0c;遇到问题又陷入盲人摸象。对C的编译体系是如何发展的&#xff0c;为什么要用CMake&#xff0c;它的运作原理是如何的比较感兴趣&#xff0c;所以就想系统学习…

云手机批量操作使用场景,从Amazon、TK等软件分析

云手机目前所具备的群控&#xff0c;批量操作&#xff0c;自动化等功能&#xff0c;对于电商&#xff0c;软测&#xff0c;办公&#xff0c;直播&#xff0c;营销等行业有很好的减负作用。 针对于具体的海外APP&#xff0c;云手机具体可以做哪些事情来帮助我们减轻压力&#x…

伺服【禾川X6】

驱动器&#xff1a; A&#xff1a;脉冲 B&#xff1a;EtherCAT // SV-X6 FB 040 AA 一套360 N&#xff1a;CANopen R&#xff1a;PROFINET 电机&#xff1a; SV-X6 MA 040A-B2 KA

MongoDB - 集合和文档的增删改查操作

文章目录 1. MongoDB 运行命令2. MongoDB CRUD操作1. 新增文档1. 新增单个文档 insertOne2. 批量新增文档 insertMany 2. 查询文档1. 查询所有文档2. 指定相等条件3. 使用查询操作符指定条件4. 指定逻辑操作符 (AND / OR) 3. 更新文档1. 更新操作符语法2. 更新单个文档 updateO…

Linux--线程ID封装管理原生线程

目录 1.线程的tid&#xff08;本质是线程属性集合的起始虚拟地址&#xff09; 1.1pthread库中线程的tid是什么&#xff1f; 1.2理解库 1.3phtread库中做了什么&#xff1f; 1.4线程的tid&#xff0c;和内核中的lwp 1.5线程的局部存储 2.封装管理原生线程库 1.线程的tid…

odoo视图继承

odoo视图继承 在模型时候&#xff0c;不对视图、菜单等进行修改&#xff0c;原视图和菜单等视图数据仍然可以使用&#xff0c;不需要重新构建 form视图继承案例 model&#xff1a;为对应模型 inherit_id&#xff1a;为继承的视图&#xff0c;ref:为继承视图的id&#xff0…

帝特(DTECH)USB转RS485/422串口线在Ubuntu系统中的安装

因为测试需要&#xff0c;买了一根帝特&#xff08;DTECH&#xff09;USB转RS485/422串口线&#xff0c;今天测试了一下在Ubuntu 22.04系统上的使用。帝特的网站上提供了驱动程序&#xff0c;下载以后发现接口芯片是CP2102&#xff0c;厂商只提供了Linux内核2.6和3.x版本的驱动…

新版FMEA培训未能达到预期效果怎么办?

在制造业的质量管理中&#xff0c;FMEA&#xff08;Failure Mode and Effects Analysis&#xff0c;失效模式与影响分析&#xff09;是一项至关重要的工具&#xff0c;它帮助企业识别和评估产品或过程中潜在的失效模式&#xff0c;以及这些失效模式可能导致的后果。然而&#x…

AIGC技术引领创意设计行业革新,“谁”能成职业发展新引擎?

随着科技的日新月异&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;技术正迅速崛起&#xff0c;成为创意设计领域的一股强大新势力。该技术不仅显著提升了设计师的工作效率&#xff0c;更为他们打开了前所未有的创意空间。在这一波技术浪潮中&#xff0c;Adobe国际认证…

AutoMQ 与蚂蚁数科达成战略合作

近期&#xff0c;AutoMQ 与蚂蚁数科正式签署战略合作协议&#xff0c;将和蚂蚁数科云原生 PaaS 平台 SOFAStack 在产品研发、生态集成、市场合作、技术社区影响力等多方面开展深度合作。 AutoMQ 是业内领先的消息和流存储服务提供商&#xff0c;基于云原生基础设施重新设计了 …

如何整合生成的人工智能?(GenAI)为你未来的工作增加动力

生成人工智能(GenAI)它发展迅速&#xff0c;以前所未有的速度取得了突破。人工智能将继续改变各行各业&#xff0c;预计2023年至2030年的年增长率将达到37.3%。由于一种新的知识工作者现在面临被取代的风险&#xff0c;生成式人工智能的惊人崛起进一步加剧了这种紧迫性。据《未…

虚拟机内安装vue-dev-tools

前言 项目开发调试都需要在Citrix在虚拟机环境下&#xff0c;Citrix内连接不到外网&#xff0c;在这边文章&#xff0c;我将介绍自己在Citrix环境内安装 vue-dev-tools的经验 环境 vue 步骤 1. 下载.crx文件 百度网盘里的 .crx文件的 下载链接 2. 加载.crx文件 打开浏览…

即时通讯平台项目测试(主页面)

http://8.130.98.211:8080/login.html项目访问地址&#xff1a;即时通讯平台http://8.130.98.211:8080/login.html 本篇文章进行项目主页面的测试。 在测试前需要先对待测内容进行分类&#xff0c;按照功能进行分类可以分为&#xff1a;个人信息设置、发送/接收消息、添加好友…

领夹麦克风哪个品牌好,哪个麦克风好,热门无线麦克风品牌推荐

​无线领夹麦克风是现代沟通的重要工具&#xff0c;它不仅提高了语音交流的清晰度&#xff0c;还展现了使用者的专业形象。随着技术发展&#xff0c;这些麦克风已经变得更加轻便、时尚&#xff0c;易于使用。在各种场合&#xff0c;如演讲、教育和网络直播中&#xff0c;当然&a…

请跳至打印机属性的“Adobe PDF设置”页面,取消选择“仅停靠系统字体;不使用文档字体”

场景&#xff1a; 当使用adobe pdf打印时&#xff0c;出现如下提示“请跳至打印机属性的“Adobe PDF设置”页面&#xff0c;取消选择“仅停靠系统字体&#xff1b;不使用文档字体””&#xff0c;该如何解决。 描述 □“仅停靠系统字体&#xff1b;不使用文档字体” 复选本框…

Git常见命令和用法

Git 文件状态 Git 文件 2 种状态: 未跟踪:新文件&#xff0c;从未被 Git 管理过已跟踪:Git 已经知道和管理的文件 常用命令 命令作用注意git -v查看 git 版本git init初始化 git 仓库初始化之后有工作区、暂存区(本地库)、版本库git add 文件标识暂存某个文件文件标识以终…

如何分析软件测试中发现的Bug!

假如你是一名软件测试工程师&#xff0c;每天面对的就是那些“刁钻”的Bug&#xff0c;它们像是隐藏在黑暗中的敌人&#xff0c;时不时跳出来给你一个“惊喜”。那么&#xff0c;如何才能有效地分析和处理这些Bug&#xff0c;让你的测试工作变得高效且有趣呢&#xff1f;今天我…

Python的语言特性

1&#xff0c;python是动态语言 在编译期间就确定变量类型的语言是静态语言 在运行期间才知道变量类型的是动态语言 2&#xff0c;python是强类型语言 不同类型的变量是否允许隐式转换

院内导航:如何用科技破解就医找路难题

自2019年开始“院内导航”被纳入医院智慧服务评估体系以来&#xff0c;到2023年改善就医服务升级的部署&#xff0c;每一步都见证了我国医疗卫生体系向智能化、人性化迈进的坚实步伐。 面对庞大复杂的医院环境与日益增长的就诊需求&#xff0c;如何让患者在茫茫人海中迅速找到就…

Tita的OKR:公司和CEO的OKR案例

为什么为您的组织选择OKR OKR或目标和关键结果是一种协作性的目标设定工具&#xff0c;被团队和组织用来设定具有挑战性的、雄心勃勃的目标和可衡量的结果&#xff0c;OKR是你跟踪进展、创造一致、鼓励参与可衡量的目标的方式。 OKR可以成为创造一个员工能够有目的的工作环境的…