SSM - Springboot - MyBatis-Plus 全栈体系(三十四)

第八章 项目实战

四、后台功能开发

1. 用户模块开发

1.1 jwt 和 token 介绍
1.1.1 token 介绍
  • 令牌(Token):在计算机领域,令牌是一种代表某种访问权限或身份认证信息的令牌。它可以是一串随机生成的字符或数字,用于验证用户的身份或授权用户对特定资源的访问。普通的令牌可能以各种形式出现,如访问令牌、身份令牌、刷新令牌等。

在这里插入图片描述

  • 简单理解 : 每个用户生成的唯一字符串标识,可以进行用户识别和校验
  • 类似技术: 天王盖地虎 , 小鸡炖蘑菇
  • 优势: token 验证标识无法直接识别用户的信息,盗取 token 后也无法登录程序! 相对安全!
1.1.2 jwt 介绍
  • Token 是一项规范和标准(接口)
  • JWT(JSON Web Token)是具体可以生成,校验,解析等动作 Token 的技术(实现类)

在这里插入图片描述

1.1.3. jwt 工作流程
  • 用户提供其凭据(通常是用户名和密码)进行身份验证。
  • 服务器对这些凭据进行验证,并在验证成功后创建一个 JWT。
  • 服务器将 JWT 发送给客户端,并客户端在后续的请求中将 JWT 附加在请求头或参数中。
  • 服务器接收到请求后,验证 JWT 的签名和有效性,并根据 JWT 中的声明进行身份验证和授权操作
1.1.4 jwt 数据组成和包含信息
  • JWT 由三部分组成: header(头部).payload(载荷).signature(签名)

在这里插入图片描述

  • 我们需要理解的是, jwt 可以携带很多信息! 一般情况,需要加入:有效时间,签名秘钥,其他用户标识信息!
  • 有效时间为了保证 token 的时效性,过期可以重新登录获取!
  • 签名秘钥为了防止其他人随意解析和校验 token 数据!
  • 用户信息为了我们自己解析的时候,知道 Token 对应的具体用户!
1.1.5 jwt 使用和测试
1.1.5.1 导入依赖
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.0</version>
</dependency>
1.1.5.2 编写配置
  • application.yaml
#jwt配置
jwt:token:tokenExpiration: 120 #有效时间,单位分钟tokenSignKey: headline123456  #当前程序签名秘钥 自定义
1.1.5.3 导入工具类
  • 封装 jwt 技术工具类
package com.alex.utils;import com.alibaba.druid.util.StringUtils;
import io.jsonwebtoken.*;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;import java.util.Date;@Data
@Component
@ConfigurationProperties(prefix = "jwt.token")
public class JwtHelper {private  long tokenExpiration; //有效时间,单位毫秒 1000毫秒 == 1秒private  String tokenSignKey;  //当前程序签名秘钥//生成token字符串public  String createToken(Long userId) {System.out.println("tokenExpiration = " + tokenExpiration);System.out.println("tokenSignKey = " + tokenSignKey);String token = Jwts.builder().setSubject("YYGH-USER").setExpiration(new Date(System.currentTimeMillis() + tokenExpiration*1000*60)) //单位分钟.claim("userId", userId).signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();return token;}//从token字符串获取useridpublic  Long getUserId(String token) {if(StringUtils.isEmpty(token)) return null;Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);Claims claims = claimsJws.getBody();Integer userId = (Integer)claims.get("userId");return userId.longValue();}//判断token是否有效public  boolean isExpiration(String token){try {boolean isExpire = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getExpiration().before(new Date());//没有过期,有效,返回falsereturn isExpire;}catch(Exception e) {//过期出现异常,返回truereturn true;}}
}
1.1.5.4 使用和测试
@org.springframework.boot.test.context.SpringBootTest
public class SpringBootTest {@Autowiredprivate JwtHelper jwtHelper;@Testpublic void test(){//生成 传入用户标识String token = jwtHelper.createToken(1L);System.out.println("token = " + token);//解析用户标识int userId = jwtHelper.getUserId(token).intValue();System.out.println("userId = " + userId);//校验是否到期! false 未到期 true到期boolean expiration = jwtHelper.isExpiration(token);System.out.println("expiration = " + expiration);}}
1.2. 登录功能实现
1.2.1 需求描述

在这里插入图片描述

  • 用户在客户端输入用户名密码并向后端提交,后端根据用户名和密码判断登录是否成功,用户有误或者密码有误响应不同的提示信息!
1.2.2 接口描述
  • url 地址: user/login

  • 请求方式:POST

  • 请求参数:

{"username":"zhangsan", //用户名"userPwd":"123456"     //明文密码
}
  • 响应数据:

    • 成功
    {"code":"200",         // 成功状态码"message":"success"   // 成功状态描述"data":{"token":"... ..." // 用户id的token}
    }
    
    • 失败
    {"code":"501","message":"用户名有误""data":{}
    }
    
    {"code":"503","message":"密码有误""data":{}
    }
    
1.2.3 实现代码
1.2.3.1 controller
@RestController
@RequestMapping("user")
@CrossOrigin
public class UserController {@Autowiredprivate UserService userService;/*** 登录需求* 地址: /user/login* 方式: post* 参数:*    {*     "username":"zhangsan", //用户名*     "userPwd":"123456"     //明文密码*    }* 返回:*   {*    "code":"200",         // 成功状态码*    "message":"success"   // 成功状态描述*    "data":{*         "token":"... ..." // 用户id的token*     }*  }** 大概流程:*    1. 账号进行数据库查询 返回用户对象*    2. 对比用户密码(md5加密)*    3. 成功,根据userId生成token -> map key=token value=token值 - result封装*    4. 失败,判断账号还是密码错误,封装对应的枚举错误即可*/@PostMapping("login")public Result login(@RequestBody User user){Result result = userService.login(user);System.out.println("result = " + result);return result;}}
1.2.3.2 service
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService{@Autowiredprivate JwtHelper jwtHelper;@Autowiredprivate UserMapper userMapper;/*** 登录业务实现* @param user* @return result封装*/@Overridepublic Result login(User user) {//根据账号查询LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUsername,user.getUsername());User loginUser = userMapper.selectOne(queryWrapper);//账号判断if (loginUser == null) {//账号错误return Result.build(null, ResultCodeEnum.USERNAME_ERROR);}//判断密码if (!StringUtils.isEmpty(user.getUserPwd())&& loginUser.getUserPwd().equals(MD5Util.encrypt(user.getUserPwd()))){//账号密码正确//根据用户唯一标识生成tokenString token = jwtHelper.createToken(Long.valueOf(loginUser.getUid()));Map data = new HashMap();data.put("token",token);return Result.ok(data);}//密码错误return Result.build(null,ResultCodeEnum.PASSWORD_ERROR);}
}
1.3 根据 token 获取用户数据
1.3.1 需求描述
  • 客户端发送请求,提交 token 请求头,后端根据 token 请求头获取登录用户的详细信息并响应给客户端进行存储
1.3.2 接口描述
  • url 地址:user/getUserInfo

  • 请求方式:GET

  • 请求头:

    token: token内容
    
  • 响应数据:

    • 成功
    {"code": 200,"message": "success","data": {"loginUser": {"uid": 1,"username": "zhangsan","userPwd": "","nickName": "张三"}}
    }
    
    • 失败
    {"code": 504,"message": "notLogin","data": null
    }
    
1.3.3 代码实现
1.3.3.1 controller
/*** 地址: user/getUserInfo* 方式: get* 请求头: token = token内容* 返回:*    {*     "code": 200,*     "message": "success",*     "data": {*         "loginUser": {*             "uid": 1,*             "username": "zhangsan",*             "userPwd": "",*             "nickName": "张三"*         }*      }*   }** 大概流程:*    1.获取token,解析token对应的userId*    2.根据userId,查询用户数据*    3.将用户数据的密码置空,并且把用户数据封装到结果中key = loginUser*    4.失败返回504 (本次先写到当前业务,后期提取到拦截器和全局异常处理器)*/
@GetMapping("getUserInfo")
public Result userInfo(@RequestHeader String token){Result result = userService.getUserInfo(token);return result;
}
1.3.3.2 service
/*** 查询用户数据* @param token* @return result封装*/
@Override
public Result getUserInfo(String token) {//1.判定是否有效期if (jwtHelper.isExpiration(token)) {//true过期,直接返回未登录return Result.build(null,ResultCodeEnum.NOTLOGIN);}//2.获取token对应的用户int userId = jwtHelper.getUserId(token).intValue();//3.查询数据User user = userMapper.selectById(userId);if (user != null) {user.setUserPwd(null);Map data = new HashMap();data.put("loginUser",user);return Result.ok(data);}return Result.build(null,ResultCodeEnum.NOTLOGIN);
}
1.4 注册用户名检查
1.4.1 需求描述

在这里插入图片描述

  • 用户在注册时输入用户名时,立刻将用户名发送给后端,后端根据用户名查询用户名是否可用并做出响应
1.4.2 接口描述
  • url 地址:user/checkUserName
  • 请求方式:POST
  • 请求参数:param 形式
username=zhangsan
  • 响应数据:

    • 成功
    {"code":"200","message":"success""data":{}
    }
    
    • 失败
    {"code":"505","message":"用户名占用""data":{}
    }
    
1.4.3 代码实现
1.4.3.1 controller
/*** url地址:user/checkUserName* 请求方式:POST* 请求参数:param形式* username=zhangsan* 响应数据:* {*    "code":"200",*    "message":"success"*    "data":{}* }** 实现步骤:*   1. 获取账号数据*   2. 根据账号进行数据库查询*   3. 结果封装*/
@PostMapping("checkUserName")
public Result checkUserName(String username){Result result = userService.checkUserName(username);return result;
}
1.4.3.2 service
/*** 检查账号是否可以注册** @param username 账号信息* @return*/
@Override
public Result checkUserName(String username) {LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUsername,username);User user = userMapper.selectOne(queryWrapper);if (user != null){return Result.build(null,ResultCodeEnum.USERNAME_USED);}return Result.ok(null);
}
1.5 用户注册功能
1.5.1 需求描述

在这里插入图片描述

  • 客户端将新用户信息发送给服务端,服务端将新用户存入数据库,存入之前做用户名是否被占用校验,校验通过响应成功提示,否则响应失败提示
1.5.2 接口描述
  • url 地址:user/regist

  • 请求方式:POST

  • 请求参数:

{"username":"zhangsan","userPwd":"123456","nickName":"张三"
}
  • 响应数据:

    • 成功
    {"code":"200","message":"success""data":{}
    }
    
    • 失败
    {"code":"505","message":"用户名占用""data":{}
    }
    
1.5.3 代码实现
1.5.3.1 controller
/**
* url地址:user/regist
* 请求方式:POST
* 请求参数:
* {
*     "username":"zhangsan",
*     "userPwd":"123456",
*     "nickName":"张三"
* }
* 响应数据:
* {
*    "code":"200",
*    "message":"success"
*    "data":{}
* }
*
* 实现步骤:
*   1. 将密码加密
*   2. 将数据插入
*   3. 判断结果,成 返回200 失败 505
*/@PostMapping("regist")
public Result regist(@RequestBody User user){Result result = userService.regist(user);return result;
}
1.5.3.2 service
@Override
public Result regist(User user) {LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUsername,user.getUsername());Long count = userMapper.selectCount(queryWrapper);if (count > 0){return Result.build(null,ResultCodeEnum.USERNAME_USED);}user.setUserPwd(MD5Util.encrypt(user.getUserPwd()));int rows = userMapper.insert(user);System.out.println("rows = " + rows);return Result.ok(null);
}

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

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

相关文章

Python哪个版本最稳定好用2023.10.19

环境&#xff1a; win10 专业版 Python 问题描述&#xff1a; python哪个版本最稳定好用 解决方案&#xff1a; 目前&#xff0c;Python 的最新版本是 3.11.0。然而&#xff0c;对于大多数用户来说&#xff0c;Python 3.8 和 3.9 是最稳定和好用的版本&#xff0c;因为它们…

下拉选择框监听el-option的方式

<el-select v-model"form.expenseType" placeholder"请选择费用类型" clearable filterable size"small"><el-option v-for"item in expenseNameList" :key"item.value" :label"item.label" :value"…

python控制负数以16进制整型格式输出

实际使用时候&#xff0c;发现 python输出负数进程是 十进制和16进制一样的&#xff0c;就是16进制多了一个负号&#xff0c;和预期结果不同&#xff1b;比如我想要 -1输出 0xFFFFFFFF&#xff0c;可以参考如下方式&#xff1b; def TestPrintf(): ret -3print("test1 r…

csapp-Machine-Level Representation of Program-review

Machine-Level Representation of Program收获和思考 Basics Machine-Level Programming可以看成是机器执行对于上层代码的一种翻译&#xff0c;即硬件是如何通过一个个的指令去解释每一行代码&#xff0c;然后操纵各种硬件执行出对应的结果。 Machine-Level Programming有2种…

根据Excel表格数据去修改数据库数据

一、背景 项目上线&#xff0c;实施任务发来一份Excel表格数据 需要将供应商和生产厂商进行绑定&#xff0c;因为数据过多&#xff0c;实施人员一个个绑定时间成本过高&#xff0c;想让开发给出一个脚本。 二、操作 比如这些数据 生产厂商为A 供应商为B 以update 语句为例 …

怎么理解函数式编程思维?

文章目录 &#xff08;2023年9.29号&#xff0c;正月十五家乡的月亮&#xff09; ​ 理解函数式编程要注重思维的转变。函数式编程聚焦于简洁的高阶函数&#xff0c;高阶函数注重封装底层运作原理来解决复杂的业务场景&#xff0c;比如 Scala、Groovy、Clojure 语言&#xff1a…

webrtc gcc算法(1)

老的webrtc gcc算法,大概流程&#xff1a; 这两个拥塞控制算法分别是在发送端和接收端实现的&#xff0c; 接收端的拥塞控制算法所计算出的估计带宽&#xff0c; 会通过RTCP的remb反馈到发送端&#xff0c; 发送端综合两个控制算法的结果得到一个最终的发送码率&#xff0c;并以…

iZotope RX 10for Mac /Windows- 音频修复的终极解决方案

随着音乐和电影制作的复杂性日益增加&#xff0c;高质量的音频修复变得越来越重要。iZotope RX 10&#xff0c;作为业界公认的专业音频修复软件&#xff0c;为你提供了强大、精确的工具&#xff0c;让你的声音变得清晰、纯净。 在音频修复领域&#xff0c;iZotope RX 10凭借其…

国产低功耗MCU芯片:Si24R03

Si24R03集成了基于RISC-V核的低功耗MCU和工作在2.4GHz ISM频段的无线收发器模块&#xff0c;是一款高度集成的低功耗SOC片。 应用领域&#xff1a; 1、物联网 2、智N门锁 3、电机控制 4、消费电子 5、工业控制 其无线收发器模块是专为低功耗无线场合设计&#xff0c;在关…

maven聚合和继承

一、什么是maven的聚合和继承&why 随着技术飞速发展&#xff0c;各类用户对软件的要求越来越高&#xff0c;软件也变得越来越复杂。 软件设计人员往往会采用各种方式对软件划分模块&#xff0c;已得到更加清晰的设计及更高的复用性。 当把Maven应用到实际项目中的时候&am…

CCF ChinaSoft 2023 论坛巡礼|形式验证@EDA论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

uni-app:实现当前时间的获取,并且根据当前时间判断所在时间段为早上,下午还是晚上

效果图 核心代码 获取当前时间 toString()方法将数字转换为字符串 padStart(2, 0)&#xff1a;padStart()方法用于在字符串头部填充指定的字符&#xff0c;使其达到指定的长度。该方法接受两个参数&#xff1a;第一个参数为期望得到的字符串长度&#xff0c;第二个参数为要填充…

pytorch教程

文章目录 1 pytorch的安装2 PyTorch基础知识2.1 张量简介2.2 初始化2.3 张量的属性2.4 ndarray与tensor互转2.5 索引、切片、变形、聚合、矩阵拼接、切割、转置 3 pytorch自动微分4 线性回归5 分类5.1 写法一5.2 写法二 1 pytorch的安装 pytorch官网 https://pytorch.org/get-…

5256C 5G终端综合测试仪

01 5256C 5G终端综合测试仪 产品综述&#xff1a; 5256C 5G终端综合测试仪主要用于5G终端、基带芯片的研发、生产、校准、检测、认证和教学等领域。该仪表具备5G信号发送功能、5G信号功率特性、解调特性和频谱特性分析功能&#xff0c;支持5G终端的产线高速校准及终端发射机…

Simple RPC - 02 通用高性能序列化和反序列化设计与实现

文章目录 概述设计实现通用的序列化接口通用的序列化实现【推荐】 vs 专用的序列化实现专用序列化接口定义序列化实现 概述 网络传输和序列化这两部分的功能相对来说是非常通用并且独立的&#xff0c;在设计的时候&#xff0c;只要能做到比较好的抽象&#xff0c;这两部的实现…

全光谱护眼灯有哪些?2023全光谱护眼台灯推荐

随着电子设备的不断普及&#xff0c;手机、平板电脑、显示器、电视机等几乎是家家户户的必备品&#xff0c;也正因为眼睛有那么多时间、那么多机会去盯着屏幕&#xff0c;所以如今近视低龄化现象也越来越严重了。随着科技的不断发展&#xff0c;台灯的发展也越来越多样化&#…

成都瀚网科技有限公司:开抖音店铺有哪些注意事项?

成功经营一个小店不仅仅是发布产品视频那么简单&#xff0c;还需要注意一些重要的事情。开抖音店铺需要注意以下几点&#xff1a; 1、开抖音店铺有哪些注意事项&#xff1f; 合规管理&#xff1a;在抖音开店&#xff0c;首先要确保自己的运营合规。遵守相关法律法规及平台规定&…

Elasticsearch学习笔记

1.核心概念 bucket: 一个数据分组&#xff08;类似于sql group by以后的数据&#xff09;metric&#xff1a;对bucket执行的某种聚合分析的操作&#xff0c;比如说求平均值&#xff0c;最大值&#xff0c;最小值。一些系列的统计方法(类似 select count(1) MAX MIN AVG) 请…

CUDA学习笔记5——CUDA程序错误检测

CUDA程序错误检测 所有CUDA的API函数都有一个类型为cudaError_t的返回值&#xff0c;代表了一种错误信息&#xff1b;只有返回cudaSuccess时&#xff0c;才是成功调用。 cudaGetLastError()用来检测核函数的执行是否出错cudaGetErrorString()输出错误信息 #include <stdi…

【lesson13】进程控制初识

文章目录 进程创建 进程创建 请你描述一下&#xff0c;fork创建子进程操作系统都做了什么&#xff1f; fork创建子进程&#xff0c;系统里多了一个进程&#xff0c;进程 内核数据结构 进程代码数据&#xff0c;内核数据结构由OS维护&#xff0c;进程代码数据一般由磁盘维护。…