Spring Boot+Vue3前后端分离实战wiki知识库系统之用户管理单点登录
创始人
2024-04-10 12:24:40

在这里插入图片描述

用户表设计与代码生成

用户表设计

在这里插入图片描述

生成持久层代码

同样是在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 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;}/*** 不写入堆栈信息,提高性能*/@Overridepublic 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)@ResponseBodypublic 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)@ResponseBodypublic 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 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();}
});

用户密码初始化

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

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...