Spring Boot+Vue3前后端分离实战wiki知识库系统之用户管理单点登录

news2025/1/14 10:10:44

在这里插入图片描述

用户表设计与代码生成

用户表设计

在这里插入图片描述

生成持久层代码

同样是在gennerator.xml中添加并执行:
在这里插入图片描述
在这里插入图片描述

完成用户表基本增删查改功能

首先我们应该改造controller
在这里插入图片描述

接着是service,service的查询条件要根据loginname来查找:
在这里插入图片描述
接着修改三个实体:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意密码的规则。
接着我们来修改前端,我们前端就不用像ebook那样查找所有的分类,字段名要修改:
在这里插入图片描述

我们先新增一条用户信息:
在这里插入图片描述
路由增加:
在这里插入图片描述
在这里插入图片描述
菜单也要增加一个用户管理:
在这里插入图片描述
查看效果:
在这里插入图片描述
输入不符合规则的密码会触发弹窗:
在这里插入图片描述

用户名重复校验与自定义异常

新增用户时,增加用户名重复校验

保存用户信息的时候我们要先判断是否数据库已经有相同用户名的信息了:

    /**
     * 保存
     */
    public void save(UserSaveReq req) {
        User user = CopyUtil.copy(req, User.class);
        if (ObjectUtils.isEmpty(req.getId())) {
            User userDB = selectByLoginName(req.getLoginName());
            if (ObjectUtils.isEmpty(userDB)) {
                // 新增
                user.setId(snowFlake.nextId());
                userMapper.insert(user);
            } else {
                // 用户名已存在
                throw new BusinessException(BusinessExceptionCode.USER_LOGIN_NAME_EXIST);
            }
        } else {
            // 更新
            user.setLoginName(null);
            user.setPassword(null);
            userMapper.updateByPrimaryKeySelective(user);
        }
    }

    public User selectByLoginName(String LoginName) {
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andLoginNameEqualTo(LoginName);
        List<User> userList = userMapper.selectByExample(userExample);
        if (CollectionUtils.isEmpty(userList)) {
            return null;
        } else {
            return userList.get(0);
        }
    }

校验重复时,抛出自定义异常

这里我们自定义了一个异常:

public class BusinessException extends RuntimeException{

    private BusinessExceptionCode code;

    public BusinessException (BusinessExceptionCode code) {
        super(code.getDesc());
        this.code = code;
    }

    public BusinessExceptionCode getCode() {
        return code;
    }

    public void setCode(BusinessExceptionCode code) {
        this.code = code;
    }

    /**
     * 不写入堆栈信息,提高性能
     */
    @Override
    public Throwable fillInStackTrace() {
        return this;
    }
}

异常的枚举:

public enum BusinessExceptionCode {

    USER_LOGIN_NAME_EXIST("登录名已存在"),
    LOGIN_USER_ERROR("用户名不存在或密码错误"),
    VOTE_REPEAT("您已点赞过"),
    ;

    private String desc;

    BusinessExceptionCode(String desc) {
        this.desc = desc;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}

我们要在统一异常处理的类中处理这个异常:

    /**
     * 校验异常统一处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public CommonResp validExceptionHandler(BusinessException e) {
        CommonResp commonResp = new CommonResp();
        LOG.warn("业务异常:{}", e.getCode().getDesc());
        commonResp.setSuccess(false);
        commonResp.setMessage(e.getCode().getDesc());
        return commonResp;
    }

除了这些异常我们还需要有个处理所有异常的方法:

    /**
     * 校验异常统一处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public CommonResp validExceptionHandler(Exception e) {
        CommonResp commonResp = new CommonResp();
        LOG.error("系统异常:", e);
        commonResp.setSuccess(false);
        commonResp.setMessage("系统出现异常,请联系管理员");
        return commonResp;
    }

修改时,用户名不能修改

我们增加一个disabled属性判断当前能否修改用户名,只有添加用户的时候才能好修改这个字段,编辑的时候不能修改。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
但是我们只写了前端的校验,这是可以通过修改代码绕过的,我么现在写一下后端的校验:
在这里插入图片描述
Selective的意思是有值才会更新,没值就不更新。

关于密码的两层加密处理

密码加密存储

现在我们遇到一个问题,就是存储在数据库的数据是明文的,这样就很容易泄漏,我们应该将明文变成密文。我们在保存的时候用md5加密就行了。
在这里插入图片描述
在这里插入图片描述

密码加密传输

但是我我们的密码在前端也是明文传输的,所以我们在前端也要加密,这样就提高了安全性,例如一些钓鱼wifi就可以截取到你明文传输的密码。
我们创建了一个md5算法的js脚本,在index.html中引入:
在这里插入图片描述

我们在admin-user.vue中使用:
在这里插入图片描述
在这里插入图片描述
声明两个变量,表示这两个是存在的:
在这里插入图片描述
在这里插入图片描述
这样我们就实现了在传输的时候加密了:
在这里插入图片描述

增加重置密码功能

修改用户时,不能修改密码

在编辑的时候我们要隐藏密码:
在这里插入图片描述

在这里插入图片描述
上面写错了,应该放到item里:
在这里插入图片描述
这样我们点击编辑的时候密码就不会显示出来了,点击新增还会显示出来:
在这里插入图片描述

单独开发设置密码表单和接口

重置密码:


    /**
     * 修改密码
     */
    public void resetPassword(UserResetPasswordReq req) {
        User user = CopyUtil.copy(req, User.class);
        userMapper.updateByPrimaryKeySelective(user);
    }

重置密码请求我们只需要传入密码就行:
在这里插入图片描述
重置密码接口:

    @PostMapping("/reset-password")
    public CommonResp resetPassword(@Valid @RequestBody UserResetPasswordReq req) {
        req.setPassword(DigestUtils.md5DigestAsHex(req.getPassword().getBytes()));
        CommonResp resp = new CommonResp<>();
        userService.resetPassword(req);
        return resp;
    }

我们可以模仿编辑按钮写一个模态框:
在这里插入图片描述
网络请求:
在这里插入图片描述

单点登录token与JWT介绍

在这里插入图片描述

在这里插入图片描述

登录功能开发

后端增加登录接口

登录的请求参数只有账号和密码:在这里插入图片描述

返回参数需要返回token:
在这里插入图片描述
接口:

在这里插入图片描述
service同样需要抛出我们自定义的异常:

    /**
     * 登录
     */
    public UserLoginResp login(UserLoginReq req) {
        User userDb = selectByLoginName(req.getLoginName());
        if (ObjectUtils.isEmpty(userDb)) {
            // 用户名不存在
            LOG.info("用户名不存在, {}", req.getLoginName());
            throw new BusinessException(BusinessExceptionCode.LOGIN_USER_ERROR);
        } else {
            if (userDb.getPassword().equals(req.getPassword())) {
                // 登录成功
                UserLoginResp userLoginResp = CopyUtil.copy(userDb, UserLoginResp.class);
                return userLoginResp;
            } else {
                // 密码不对
                LOG.info("密码不对, 输入密码:{}, 数据库密码:{}", req.getPassword(), userDb.getPassword());
                throw new BusinessException(BusinessExceptionCode.LOGIN_USER_ERROR);
            }
        }
    }

前端增加登录模态框

我们登录的模态框可以放到header里面。因为每个页面都有。
导入:
在这里插入图片描述

增加登录按钮:
在这里插入图片描述
a标签是支持点击的,换成按钮也可以。
设置样式,移到右边,并且文字为白色:
在这里插入图片描述
添加模态框:
在这里插入图片描述
下面是js的定义:
在这里插入图片描述

登录成功处理并集成Vuex

在这里插入图片描述

后端保存用户信息

导入redis依赖:
在这里插入图片描述
注入redis模板:
在这里插入图片描述
通过雪花算法生成token,并放到redis里面:
在这里插入图片描述
给response添加token:
在这里插入图片描述

    @PostMapping("/login")
    public CommonResp login(@Valid @RequestBody UserLoginReq req) {
        req.setPassword(DigestUtils.md5DigestAsHex(req.getPassword().getBytes()));
        CommonResp<UserLoginResp> resp = new CommonResp<>();
        UserLoginResp userLoginResp = userService.login(req);

        Long token = snowFlake.nextId();
        LOG.info("生成单点登录token:{},并放入redis中", token);
        userLoginResp.setToken(token.toString());
        redisTemplate.opsForValue().set(token.toString(), JSONObject.toJSONString(userLoginResp), 3600 * 24, TimeUnit.SECONDS);

        resp.setContent(userLoginResp);
        return resp;
    }

配置redis:
在这里插入图片描述
我们如果要将对象放到redis中,需要先序列化:
在这里插入图片描述
可以直接将实体类实现serializable的接口:
在这里插入图片描述
也可以直接将要对象转化成json:
在这里插入图片描述
为了测试redis是否设置成功,我们编写两个接口:
在这里插入图片描述
测试成功:
在这里插入图片描述

前端显示登录用户

新增一个登录后保存用户的响应式变量,初始化为空对象:
在这里插入图片描述
登陆成功后赋值:
在这里插入图片描述

通过当前的user是否有id判断是否登录,从而决定要显示什么:在这里插入图片描述
我们来测试一下:
在这里插入图片描述

在这里插入图片描述
但是这里存在一个问题,我们刷新页面后登陆信息就没有了。接下来我们用vuex来存储我们的登陆信息,让我们的登陆信息变成一个全局变量。在Index.ts中定义:
在这里插入图片描述
在登陆成功的时候赋值全局变量,先导入:
在这里插入图片描述
赋值:
在这里插入图片描述
我们在footer中测试一下:
在这里插入图片描述
同样也需要导入
在这里插入图片描述
测试:
发现登陆成功后footer也会有值:
在这里插入图片描述
在这里插入图片描述
但是还是存在之前那个问题,刷新这些信息就没有了。
我们编写一个存储信息的工具方法:

SessionStorage = {
    get: function (key) {
        var v = sessionStorage.getItem(key);
        if (v && typeof(v) !== "undefined" && v !== "undefined") {
            return JSON.parse(v);
        }
    },
    set: function (key, data) {
        sessionStorage.setItem(key, JSON.stringify(data));
    },
    remove: function (key) {
        sessionStorage.removeItem(key);
    },
    clearAll: function () {
        sessionStorage.clear();
    }
};

导入:
在这里插入图片描述
我们可以将Sessinstorage继承到idnex.ts中,其他页面要用变量的话从这里面拿就行了
在这里插入图片描述
在这里插入图片描述
放到缓存中:
在这里插入图片描述
我们这时候刷新发现footer还保存着,但footer没有了,现在来改造一下header
监听store:
在这里插入图片描述
登陆成功只需要给store赋值:
在这里插入图片描述
这样我们就解决了刷新数据丢失的问题

增加退出登录功能

在这里插入图片描述
编写后端退出登录接口:
在这里插入图片描述
在这里插入图片描述
前端:
在这里插入图片描述

添加一个确认框,防止用户误点一次就退出登录了:
在这里插入图片描述

在这里插入图片描述

后端接口增加登录校验

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
有些接口不需要拦截,我们就在SpringMvcConfig中配置一下:
在这里插入图片描述
两个*表示后面是任意的。
前端拦截器,在请求头中添加header(token):
在这里插入图片描述

前端界面增加登录校验

在这里插入图片描述

未登录时,管理菜单要隐藏

通过当前是否用用户id判断菜单是否需要隐藏:
在这里插入图片描述
但这里存在一个问题,我们直接手敲url依然能访问到界面:
在这里插入图片描述

对路由做判断,防止用户通过手敲url访问管理界面

在这里插入图片描述
我们在路由文件中给要拦截的属性加一个meta,例如:
在这里插入图片描述
加入判断逻辑:

// 路由登录拦截
router.beforeEach((to, from, next) => {
  // 要不要对meta.loginRequire属性做监控拦截
  if (to.matched.some(function (item) {
    console.log(item, "是否需要登录校验:", item.meta.loginRequire);
    return item.meta.loginRequire
  })) {
    const loginUser = store.state.user;
    if (Tool.isEmpty(loginUser)) {
      console.log("用户未登录!");
      next('/');
    } else {
      next();
    }
  } else {
    next();
  }
});

用户密码初始化

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Windows OpenGL 图像单色

目录 一.OpenGL 图像单色 1.原始图片2.效果演示 二.OpenGL 图像单色源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基础 OpenGL ES 学习…

基于stm32单片机的电压报警系统Proteus仿真

资料编号&#xff1a;112 下面是相关功能视频演示&#xff1a; 112-基于stm32单片机的电压报警系统Proteus仿真&#xff08;源码仿真全套资料&#xff09;功能介绍&#xff1a; 采用stm32单片机的12位ADC采集电压&#xff0c;当电压值超过设置值蜂鸣器和LED产生报警&#xff…

知识蒸馏 | YOLOv7知识蒸馏实战篇 | 2/2*

知识蒸馏 | YOLOv7知识蒸馏实战篇 | 2/2* 文章目录 知识蒸馏 | YOLOv7知识蒸馏实战篇 | 2/2*0. 环境准备1. 训练学生网络2. 训练教师网络3. 知识蒸馏训练4. YOLOv7官方项目修改说明5. 源码0. 环境准备 终端键入: pip install -r requirements.txt -i https://pypi.tuna.tsin…

11.19 - 每日一题 - 408

每日一句&#xff1a;人生难得几回搏&#xff0c;此时不搏待何时。 数据结构 1 在下列叙述中&#xff0c;正确的叙述是______ A.树的先序遍历和中序遍历可以得到树的后序遍历B.将一棵树转换成二叉树后&#xff0c;根结点没有右子树C.采用二叉链表作存储结构&#xff0c;树的…

序列召回基础+GRU4Rec论文阅读

1. 推荐系统简介 推荐系统&#xff0c;即就是为当用户推荐一些他感兴趣的项目、商品、视频等等&#xff0c;当然在对于小的项目库中能进行很快的推荐&#xff0c;但是随着不断的增加&#xff0c;数据量剧增&#xff0c;这时候就需要我们进行分步骤进行推荐&#xff0c;这就把推…

【Spring系列】- Bean生命周期底层原理

Bean生命周期底层原理 &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 一个有梦有戏的人 怒放吧德德 &#x1f31d;分享学习心得&#xff0c;欢迎指正&#xf…

SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.6 SpringBoot 读写 Redis 的客户端

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇4 数据层解决方案4.6 SpringBoot 读写 Redis 的客户端4.6.1 问题引入…

pytorch深度学习实战lesson21

第二十一课 卷积神经网络之池化层 卷积神经网络的一个重要概念就是池化层&#xff0c;一般是在卷积层之后。池化层对输入做降采样&#xff0c;常用的池化做法是对每个滤波器的输出求最大值&#xff0c;平均值&#xff0c;中位数等。下面我们和沐神详细学习一下池化层的原理与实…

第七章第三节:散列表(Hash Table)

文章目录教程1. 散列表&#xff08;Hash Table&#xff09;1.1 散列表的基本概念1.2 散列函数的构造方法1.2.1 除留佘数法1.2.2 直接定址法1.2.3 数字分析法1.2.4 平方取中法1.3 处理冲突的方法1.3.1 拉链法1.3.2 开放定址法1.3.2.1 线性探测法(常考)1.3.2.2 平方探测法1.3.2.3…

数据结构题目收录(十九)

1、在下图所示的平衡二叉树中插入关键字48后得到一棵新平衡二叉树&#xff0c;在新平衡二叉树中&#xff0c;关键字37所在结点的左、右子结点中保存的关键字分别是&#xff08;&#xff09;。 A&#xff1a;13,48B&#xff1a;24,48C&#xff1a;24,53D&#xff1a;24,90 解析…

windows驱动开发环境搭建以及helloworld

文章目录前言编译环境-WDK的安装搭建测试驱动的虚拟机win11虚拟机win10虚拟机在测试机器上运行驱动上面驱动代码含义其他前言 参考&#xff1a;windows驱动开发环境搭建以及helloworld | qwertwwwe 搭建驱动环境–编写hello驱动–安装测试虚拟机–安装驱动 编译环境-WDK的安装…

web前端-第三次作业-按钮

<!DOCTYPE html> <!-- 2022/11/16 --> <html lang"ch"> <head><meta charset"UTF-8"><title>按钮</title><style>*{margin: 0;padding: 0;box-sizing: border-box;}body{display: flex;justify-content: …

AutoDWG DWG 转换 PDF 控制组件-ActiveX

AutoDWG DWG 到 PDF 控制组件&#xff0c;比以往任何时候都更快&#xff01; DWG2PDF-X &#xff0c;一个控制组件允许您直接将 dwg 转换为 pdf&#xff0c;dxf 和 dwf 直接转换为 pdf&#xff0c;不需要 AutoCAD。 主要特征&#xff1a; 支持 R2.5 到 2019 版本的 DWG、DXF 和…

关于Conversational QA 的一些调研

文章目录Paper1: Understanding User Satisfaction with Task-oriented Dialogue SystemsMotivation:Classification:Contributions:DatasetKnowledge:Paper2: Evaluating Mixed-initiative Conversational Search Systems via User SimulationMotivationClassification:Contri…

java计算机毕业设计ssm建设路小学读背兴趣任务管理系统

项目介绍 随着互联网技术的发展,计算机技术广泛应用在人们的生活中,逐渐成为日常工作、生活不可或缺的工具。目前,各种在线学习平台层出不穷。建设路小学读背兴趣任务繁重,如何快速的学习提高小学生的读背兴趣任务,是老师非常关注的问题。为小学读背兴趣任务开发必要的程序,能…

ES6 入门教程 13 Symbol 13.8 内置的 Symbol 值

ES6 入门教程 ECMAScript 6 入门 作者&#xff1a;阮一峰 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录ES6 入门教程13 Symbol13.8 内置的 Symbol 值13.8.1 Symbol.hasInstance13.8.2 Symbol.isConcatSpreadable13.8.3 Symbol.species13.8.4 …

Gillespie 随机模拟算法附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

Python最佳实践-构建自己的第三方库

移植自本人博客&#xff1a;Python最佳实践-构建自己的第三方库 Introduction 在写一个项目的时候需要用到发布订阅者模式&#xff08;又叫广播模式&#xff09;&#xff0c;于是就实现了一下&#xff0c;写完之后感觉可以封装成库&#xff0c;于是查阅了一下如何在python上开…

全自动采集软件-自动采集为原创发布工具

随着时代不停地发展。互联网无时不刻地出现在我们的生活中&#xff0c;大家也越来越注重效率&#xff0c;今天小编就给大家来分享一款全自动采集软件。只需要点几下鼠标就能轻松获取数据&#xff0c;不管是导出还是发布到网上。详细参考图片一、二、三、四&#xff01; 企业人员…

CanOpen协议的伺服驱动控制

一、CanOpen的基本介绍&#xff1a;1、基本介绍&#xff1a;CanOpen在CAN网络7层协议中&#xff0c;处于应用层。CANopen协议是在20世纪90年代末&#xff0c;由CIA组织CAN-in-Automation&#xff0c;&#xff08;http://www.can-cia.org &#xff09;在CAL&#xff08;CAN Appl…