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

news2024/11/19 5:35:26

第八章 项目实战

四、后台功能开发

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字符串获取userid
    public  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());
            //没有过期,有效,返回false
            return isExpire;
        }catch(Exception e) {
            //过期出现异常,返回true
            return true;
        }
    }
}
1.1.5.4 使用和测试
@org.springframework.boot.test.context.SpringBootTest
public class SpringBootTest {

    @Autowired
    private JwtHelper jwtHelper;

    @Test
    public 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 {


    @Autowired
    private 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{
    @Autowired
    private JwtHelper jwtHelper;
    @Autowired
    private UserMapper userMapper;

    /**
     * 登录业务实现
     * @param user
     * @return result封装
     */
    @Override
    public 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())))
        {
           //账号密码正确
            //根据用户唯一标识生成token
            String 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.coloradmin.cn/o/1117204.html

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

相关文章

git创建与合并分支

文章目录 创建与合并分支分支管理的概念实际操作 解决冲突分支管理策略Bug分支Feature分支多人协作 创建与合并分支 分支管理的概念 分支在实际中有什么用呢&#xff1f;假设你准备开发一个新功能&#xff0c;但是需要两周才能完成&#xff0c;第一周你写了50%的代码&#xf…

jdk21的外部函数和内存API(官方翻译)

1、jdk21&#xff1a; 引入一个 API&#xff0c;通过该 API&#xff0c;Java 程序可以与 Java 运行时之外的代码和数据进行互操作。通过有效地调用外部函数&#xff08;即JVM外部的代码&#xff09;和安全地访问外部内存&#xff08;即不由JVM管理的内存&#xff09;&#xf…

一种基于HTTPS实现的Web账号登录Linux桌面系统的实现方案

问题由来 客户需求计划列入支持第三方帐号系统&#xff0c;包括Web账号。需求来源是用户想要用它们的帐号直接登录Linux Deepin操作系统。一个失败的实现方案是用户以较小的成本改造帐号管理系统发布HTTP服务&#xff0c;我们开发一个PAM模块与Web服务器交互&#xff0c;数据格…

基于天鹰优化的BP神经网络(分类应用) - 附代码

基于天鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于天鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.天鹰优化BP神经网络3.1 BP神经网络参数设置3.2 天鹰算法应用 4.测试结果&#xff1a;5.M…

2023年【广东省安全员A证第四批(主要负责人)】考试试卷及广东省安全员A证第四批(主要负责人)模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 广东省安全员A证第四批&#xff08;主要负责人&#xff09;考试试卷根据新广东省安全员A证第四批&#xff08;主要负责人&#xff09;考试大纲要求&#xff0c;安全生产模拟考试一点通将广东省安全员A证第四批&#x…

小程序之后台数据动态交互及WXS的使用 (5)

⭐⭐ 小程序专栏&#xff1a;小程序开发专栏 ⭐⭐ 个人主页&#xff1a;个人主页 目录 一.前言 二.后台数据交互 2.1 准备工作 2.1 前台首页数据连接&#xff1a; 三.WXS的使用 今天就分享到这啦&#xff01;&#xff01;&#xff01; 一.前言 本文章续前面的文章的前端界面…

【JUC系列-15】深入理解CompletableFuture的基本使用

JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…

2023 10月8日 至 10 月16日学习总结

1.做的题目 [RootersCTF2019]I_&#xff1c;3_Flask_双层小牛堡的博客-CSDN博客 [NCTF2019]SQLi regexp 盲注-CSDN博客 [网鼎杯 2018]Comment git泄露 / 恢复 二次注入 .DS_Store bash_history文件查看-CSDN博客 PHP LFI 利用临时文件Getshell_双层小牛堡的博客-CSDN博客 …

《动手学深度学习 Pytorch版》 9.7 序列到序列学习(seq2seq)

循环神经网络编码器使用长度可变的序列作为输入&#xff0c;将其编码到循环神经网络编码器固定形状的隐状态中。 为了连续生成输出序列的词元&#xff0c;独立的循环神经网络解码器是基于输入序列的编码信息和输出序列已经看见的或者生成的词元来预测下一个词元。 要点&#x…

关于使用 vxe-table 时设置了 show-overflow tooltip 不展示的问题(Dialog 组件和 table 同时使用)

众所周知&#xff0c;vxe-table 是可以支撑万级数据渲染的表格组件&#xff0c;本质上还是用了虚拟滚动的实现。之前一直知道vxe-table, 但是基本没有机会用的上这个组件&#xff0c;最近在开发埋点数据的统计&#xff0c;后端一次性返回了上千条数据&#xff0c;elementui 的 …

【Shell】环境变量 自定义变量 特殊变量

Shell变量&#xff1a;环境变量 目标 1、理解什么是系统环境变量&#xff1f; 2、掌握常用的系统环境变量都有哪些&#xff1f; Shell变量的介绍 变量用于存储管理临时的数据, 这些数据都是在运行内存中的. 变量类型 系统环境变量 自定义变量 特殊符号变量 系统环境变…

golang查看CPU使用率与内存及源码中的//go:指令

golang查看CPU使用率与内存 1 psutil 1.1 概念与应用场景 psutil是业内一个用于监控OS资源的软件包&#xff0c;目前已经多种语言&#xff0c;包括但不限于Python、Go。 gopsutil是 Python 工具库psutil 的 Golang 移植版&#xff0c;可以帮助我们方便地获取各种系统和硬件信…

基于Java的实验室设备管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

如何在电脑上设置新的蓝牙耳机

本文介绍如何将蓝牙耳机连接到Windows或Mac电脑。 如何在Windows上设置新的蓝牙耳机 蓝牙耳机的设置过程因平台而异&#xff0c;但以下是Windows 11的步骤&#xff1a; 1、选择“开始”&#xff0c;然后在搜索框中输入蓝牙&#xff0c;以显示蓝牙和其他设备。 2、选择添加设…

【LeetCode:2316. 统计无向图中无法互相到达点对数 | BFS + 乘法原理】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

施密特正交化

相信大家在平时的期末考试中一定少不了对某某向量组执行标准正交化类型的题目。今天我们从这个题目入手&#xff0c;说明这个如何执行施密特正交化&#xff0c;以及为什么要进行正交化。 一、例子 例子&#xff1a;设 a 1 [ 1 2 − 1 ] a_1\begin{bmatrix}1\\2\\-1\end{bmat…

阿里妈妈Union Lab全量公测,你会用吗?

Union Lab是一种智能化的选品推荐、推广内容创作工具&#xff0c;它内置了大语言模型&#xff08;LLMs&#xff09;&#xff0c;使得选品、推广更加智能和简单。通过联盟底层货品服务&#xff0c;Union Lab可以实现智能化货品发现、分析、选品、投放、创意生产&#xff0c;基于…

嵌入式mqtt总线架构方案mosquitto+paho

一 mqtt通信模型 MQTT 协议提供一对多的消息发布&#xff0c;可以降低应用程序的耦合性&#xff0c;用户只需要编写极少量的应用代码就能完成一对多的消息发布与订阅&#xff0c;该协议是基于<客户端-服务器>模型&#xff0c;在协议中主要有三种身份&#xff1a;发布者&…

力扣第37题 解数独 c++ 难~ 回溯

题目 37. 解数独 困难 相关标签 数组 哈希表 回溯 矩阵 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫…

基于Java的文物管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…