02-登录页面、动态路由、权限等模块开发

权限模块开发流程

  • 前端login页面开发
  • 后端SpringSecurity配置
  • 后端login接口开发
  • 前端页面框架搭建
  • 前端路由守卫,状态管理开发
  • 前后端完成认证流程

开发Login页面

  • 创建Login页面
  • 创建router,可以跳转到Login页面

Login页面

使用element-plus开发

认证功能流程

前端

  • 集成axios,并对axios进行封装
  • 配置路由守卫
    • 对请求和响应的拦截
  • 登录接口的调用
  • 登录成功之后的页面跳转
    • 跳转到首页
    • 页面就是由左侧导航、头部导航、页面主体

后端

  • 配置SpringScurity
  • 写登录接口
    • 返回用户的token、使用jwt生成token
    • 返回用户的权限信息【路由页面】

配置Redis

引入依赖

<!--引入redis-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>3.1.5</version>
</dependency>

配置连接

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://rm-2ze9013333m0paw8ywo.mysql.rds.aliyuncs.com:3306/daocao?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: puhe_shitianpassword: Shitian@9527data:redis:# 地址host: localhost# 数据库索引database: 2# 密码password: $2a$10$4NR/# 连接超时时间timeout: 10slettuce:pool:# 连接池中的最小空闲连接min-idle: 0# 连接池中的最大空闲连接max-idle: 8# 连接池的最大数据库连接数max-active: 8# #连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms

配置Redis数据序列化

public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>{public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");private Class<T> clazz;public FastJson2JsonRedisSerializer(Class<T> clazz){super();this.clazz = clazz;}@Overridepublic byte[] serialize(T t) throws SerializationException{if (t == null){return new byte[0];}return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);}@Overridepublic T deserialize(byte[] bytes) throws SerializationException{if (bytes == null || bytes.length <= 0){return null;}String str = new String(bytes, DEFAULT_CHARSET);return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType);}
}
package com.daocao.support.config.redis;import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** @author 石添* @date 2023/11/15 9:31*/
@Configuration
@EnableCaching
public class RedisConfig {@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}@Beanpublic DefaultRedisScript<Long> limitScript() {DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptText(limitScriptText());redisScript.setResultType(Long.class);return redisScript;}/*** 限流脚本*/private String limitScriptText() {return "local key = KEYS[1]\n" +"local count = tonumber(ARGV[1])\n" +"local time = tonumber(ARGV[2])\n" +"local current = redis.call('get', key);\n" +"if current and tonumber(current) > count then\n" +"    return tonumber(current);\n" +"end\n" +"current = redis.call('incr', key)\n" +"if tonumber(current) == 1 then\n" +"    redis.call('expire', key, time)\n" +"end\n" +"return tonumber(current);";}
}

工具类

package com.daocao.common.redis;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;import java.util.*;
import java.util.concurrent.TimeUnit;/*** @author ShiTian* @date 2022/8/1 17:19*/
@Component
public class RedisCache {@Autowiredpublic RedisTemplate redisTemplate;/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值*/public <T> void setCacheObject(final String key, final T value){redisTemplate.opsForValue().set(key, value);}/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值* @param timeout 时间* @param timeUnit 时间颗粒度*/public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit){redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/*** 设置有效时间** @param key Redis键* @param timeout 超时时间* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout){return expire(key, timeout, TimeUnit.SECONDS);}/*** 设置有效时间** @param key Redis键* @param timeout 超时时间* @param unit 时间单位* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout, final TimeUnit unit){return redisTemplate.expire(key, timeout, unit);}/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T getCacheObject(final String key){ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 删除单个对象** @param key*/public boolean deleteObject(final String key){return redisTemplate.delete(key);}/*** 删除集合对象** @param collection 多个对象* @return*/public long deleteObject(final Collection collection){return redisTemplate.delete(collection);}/*** 缓存List数据** @param key 缓存的键值* @param dataList 待缓存的List数据* @return 缓存的对象*/public <T> long setCacheList(final String key, final List<T> dataList){Long count = redisTemplate.opsForList().rightPushAll(key, dataList);return count == null ? 0 : count;}/*** 获得缓存的list对象** @param key 缓存的键值* @return 缓存键值对应的数据*/public <T> List<T> getCacheList(final String key){return redisTemplate.opsForList().range(key, 0, -1);}/*** 缓存Set** @param key 缓存键值* @param dataSet 缓存的数据* @return 缓存数据的对象*/public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet){BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);Iterator<T> it = dataSet.iterator();while (it.hasNext()){setOperation.add(it.next());}return setOperation;}/*** 获得缓存的set** @param key* @return*/public <T> Set<T> getCacheSet(final String key){return redisTemplate.opsForSet().members(key);}/*** 缓存Map** @param key* @param dataMap*/public <T> void setCacheMap(final String key, final Map<String, T> dataMap){if (dataMap != null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/*** 获得缓存的Map** @param key* @return*/public <T> Map<String, T> getCacheMap(final String key){return redisTemplate.opsForHash().entries(key);}/*** 往Hash中存入数据** @param key Redis键* @param hKey Hash键* @param value 值*/public <T> void setCacheMapValue(final String key, final String hKey, final T value){redisTemplate.opsForHash().put(key, hKey, value);}/*** 获取Hash中的数据** @param key Redis键* @param hKey Hash键* @return Hash中的对象*/public <T> T getCacheMapValue(final String key, final String hKey){HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/*** 删除Hash中的数据** @param key* @param hKey*/public void delCacheMapValue(final String key, final String hKey){HashOperations hashOperations = redisTemplate.opsForHash();hashOperations.delete(key, hKey);}/*** 获取多个Hash中的数据** @param key Redis键* @param hKeys Hash键集合* @return Hash对象集合*/public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys){return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 获得缓存的基本对象列表** @param pattern 字符串前缀* @return 对象列表*/public Collection<String> keys(final String pattern){return redisTemplate.keys(pattern);}
}

认证功能实现

认证功能主要由三部分组成:

  • 根据用户名和密码登陆,获取返回的token保存下来
  • 携带token请求用户的路由权限信息,跳转到项目首页
  • 通过路由守卫,实现动态路由配置

登陆

登录方法中获取token,将token存储到sessionStorage中【或者pinia】。在axios请求中,获取token,将token传到后端

请求路由信息

后端:创建过滤器,获取request中的token,获取token之后,需要刷新token

获取路由数据,根据token获取用户信息,根据用户id查询对应的权限

存储路由数据

使用pinia进行全局存储【localstorage存储,sessionstorage存储】,前提保障项目中引入pinia

路由页面

使用vue-router路由页面,保障项目中引入vue-router

创建核心页面

页面其实单页面,所有的路由都是跳转到同一个组件下的,只不过在main容器中,替换不同的vue文件【页面】就可以了

核心页面由三部分组成

  • 左侧导航【Aside】
    • 树形结构的菜单,里边包含了菜单名以及路由信息
  • 头部【Header】
  • 主体【Main】

可以使用element-plus实现

动态路由

两步走:

  • 路由查询出来之后,需要渲染动态路由结构
  • 在路由页面的时候,可以知道页面跳转的位置【通过路由守卫实现】

pinia持久化

安装持久化插件

npm install pinia-plugin-persistedstate

main.js配置

import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

store文件

export const xxxx = defineStore('xxx', {state: () => ({ ...... }),getter: {},actions: {},// 持久化persist: {enabled: true,storage: localStorage,key: 'useMenu',path: ['xxxx','xxxx']}
})

自定义icon

导入依赖

npm install fast-globnpm install vite-plugin-svg-icons

vite.config.js

// 引入path
import path from 'path'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'export default defineConfig({plugins: [vue(),createSvgIconsPlugin({iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],symbolId: '[name]'})],resolve: {// 使用import导入文件时刻省略后缀extensions: ['.js', '.vue', '.json'],alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}}
})

编写组件

components下创建 SvgIcon\index.vue

<template><!-- svg:图标外层容器节点,内部需要与use标签结合使用 --><svg :style="{ width, height }"><!-- xlink:href执行用哪一个图标,属性值务必#icon-图标名字 --><!-- use标签fill属性可以设置图标的颜色 --><use :xlink:href="prefix + name" :fill="color"></use></svg>
</template><script setup>//接受父组件传递过来的参数
defineProps({//xlink:href属性值前缀prefix: {type: String,default: "#",},//提供使用的图标名字name: String,//接受父组件传递颜色color: {type: String,default: "",},//接受父组件传递过来的图标的宽度width: {type: String,default: "16px",},//接受父组件传递过来的图标的高度height: {type: String,default: "16px",},
})
</script><style lang="scss" scoped></style>

全局注册,在main.js添加

import SvgIcon from '@/components/SvgIcon/index.vue';
import 'virtual:svg-icons-register';app.component('svg-icon', SvgIcon);

使用

<template #prefix>
<svg-iconv-if="form.icon"slot="prefix":name="form.icon" width="16px" height="25px"/>
</template>

使用elemet-plus的icon

头部tabs开发

Main组件

Aside组件

配置省略后缀

在 vite.config.js文件中配置

export default defineConfig({......resolve: {extensions: ['.js','.vue','.json','.css'],alias: {'@': fileURLToPath(new URL('./src',import.meta.url))}}
})

安装vue3 devtools插件

参考链接

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

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

相关文章

力扣75. 颜色分类

Problem: 75. 颜色分类 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 由于题目只提供0&#xff0c;1&#xff0c;2分别代表颜色红、白、蓝&#xff0c;并按此排序&#xff0c;那么我们可以遍历两次数组&#xff0c;第一次将0&#xff0c;全部放到数组前面一部分…

移动 App 入侵与逆向破解技术-iOS 篇

如果您有耐心看完这篇文章&#xff0c;您将懂得如何着手进行app的分析、追踪、注入等实用的破解技术&#xff0c;另外&#xff0c;通过“入侵”&#xff0c;将帮助您理解如何规避常见的安全漏洞&#xff0c;文章大纲&#xff1a; 简单介绍ios二进制文件结构与入侵的原理介绍入…

软考考试需要达到多少分才能及格?

当然是45分&#xff01;45分&#xff01;45分&#xff01;而且是各科45&#xff01; 初级和中级考两科 综合知识考试时长为150分钟&#xff0c;笔试&#xff0c;选择题&#xff08;上午9:00-11:30&#xff09; 案例分析考试时长为90分钟&#xff0c;笔试&#xff0c;问答题&…

计算机毕业设计 | vue+springboot图书借阅 书籍管理系统(附源码)

1. 开发目的 实现图书的智能化、信息化和简单化&#xff1b;实现图书信息的增加、删除、修改、查找、借阅、还书、收藏的显示操作及实时数据库的提交和更改和对普通用户的增、删、改、查&#xff1b;提高图书管理员工作信息报送及反馈的工作效率&#xff0c;减轻管理员的劳动负…

Unity组件入门篇目录

Audio AudioChorusFilter......................................点击导航AudioDistortionFilter..................................点击导航AudioEchoFilter.........................................点击导航AudioHighPassFilter..................................点击导…

设计模式Java实现-迭代器模式

✨这里是第七人格的博客✨小七&#xff0c;欢迎您的到来~✨ &#x1f345;系列专栏&#xff1a;设计模式&#x1f345; ✈️本篇内容: 迭代器模式✈️ &#x1f371; 本篇收录完整代码地址&#xff1a;https://gitee.com/diqirenge/design-pattern &#x1f371; 楔子 很久…

Java Spring 中使用缓存来提高性能

在Spring框架中&#xff0c;缓存是一种用于提高应用程序性能的重要机制。通过缓存&#xff0c;可以减少对数据库或其他外部资源的访问次数&#xff0c;从而加快应用程序的响应速度。以下是如何在Spring中使用缓存来提高性能的详细过程&#xff1a; 1. 引入缓存依赖 首先&…

苍穹外卖Day06笔记(复习了jwt的加密解密和传递)

疯玩了一个月&#xff0c;效率好低&#xff0c;今天开始捡起来苍穹外卖~ 1. 为什么不需要单独引入HttpClient的dependency&#xff1f; 因为我们在sky-common的pom.xml中已经引入了aliyun-sdk-oss的依赖&#xff0c;而这个依赖低层就引入了httpclinet的依赖&#xff0c;根据依…

C语言从头学05——头文件及库文件

我们写"Hello World!"程序时&#xff0c;一上来先写了一句代码&#xff1a; #include <stdio.h> 我们看上面这条代码&#xff0c;前面有个"#"号后边没有";"号&#xff0c;这样的代码不是普通的C语言代码&#xff0c;它属于预处理命令。这…

代码随想录算法训练营第六天| 242. 有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和

哈希表理论基础 [LeetCode] 242. 有效的字母异位词 [LeetCode] 242. 有效的字母异位词 文章解释 [LeetCode] 242. 有效的字母异位词 视频解释 题目: 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出…

JavaEE技术之SpringCloud(Nacos注册中心、Nacos配置中心、Sentinel实现熔断与限流)

文章目录 SpringCloud Alibaba1、简介1.1 背景1.2 Nacos主要功能1.3 Nacos和SpringBoot、SpringCloud版本选择 2、Nacos注册中心2.1 案例准备2.2 Nacos注册中心下载启动2.2.1 下载2.2.2 解压启动2.2.3 nacos-server访问测试 2.3 nacos注册中心客户端整合2.3.1 订单服务整合naco…

YTU 3166 共享单车 DFS 记忆化搜索

问题 D: 共享单车 题目描述 共享单车走进烟台&#xff0c;小明决定尝试。小明启动共享单车 App&#xff0c;轻松地找到附近的单车。那么问题来了&#xff0c;到最近的那辆单车&#xff0c;小明大约要走多少米呢&#xff1f; 现在简化问题。将地图设定成一个由 100100 米的像…

【UE】仿原神实现无限道路延伸的开场效果

目录 效果 步骤 一、无限生成砖块 二、制作门 三、停止移动并生成门 四、进入门 效果 步骤 一、无限生成砖块 1. 新建一个Basic关卡&#xff0c;再新建一个Pawn类&#xff0c;这里命名为“BP_MyPawn” 打开“BP_MyPawn”&#xff0c;添加一个胶囊体碰撞组件和一个摄像…

工器具管理(基于若依)

文章目录 前言一、工器具管理项目总览 二、入库功能1. 前端1.1 界面展示1.2 具体操作实现1.3 js文件 2. 后端2.1 工器具信息回显2.2 工器具入库 三、领用功能1. 前端1.1 界面展示1.2 具体实现操作1.3 js文件 2. 后端2.1 工器具信息回显2.2 工器具领用 遇到的问题1. 同一页面展示…

pat乙1033-旧键盘打字

1测试点2&#xff1a; 输入的字符串如果为空&#xff0c;要用getline(cin,s)&#xff0c;而不是cin>>s&#xff0c;否则程序做不了 2题目说的如果上键坏了那大写字母打印不了&#xff0c;不是大写转小写打印啦&#xff0c;认真读题 3两个for循环长这样&#xff0c;break…

基于springboot+vue的自习室管理和预约系统(全套)

一、系统架构 前端&#xff1a;vue | element-ui | html 后端&#xff1a;springboot | mybatis-plus 环境&#xff1a;jdk1.8 | mysql | maven | nodejs 二、代码及数据库 三、功能介绍 01. web端-首页1 02. web端-首页2 03. web端-注册 04. web端-登录 05. w…

牛客Linux高并发服务器开发学习第六天

目录相关函数 学习进度&#xff1a; Linux系统编程入门 06&#xff1a;59&#xff1a;42

Apollo9.0 Control模块算法源码学习

参考资料 Apollo控制算法_哔哩哔哩_bilibili

Python自动化测试 | 如何使用Robot Framework进行自动化测试?

你还在手动测试&#xff1f;不妨了解一下更高效、准确且简单的测试方法——使用Python的Robot Framework进行自动化测试。 什么是Robot Framework&#xff1f; Robot Framework是一款开源的Python自动化测试框架&#xff0c;它基于关键字驱动的思想&#xff0c;具有易读、易扩…

每日一题 城市群的数量

题目解析 城市群数量_牛客题霸_牛客网 当解决这个问题时&#xff0c;首先需要理解题目要求。题目中给出了一个城市之间的邻接矩阵&#xff0c;矩阵中的元素表示城市之间是否直接相连。如果两个城市直接相连&#xff0c;或者通过其他城市间接相连&#xff0c;它们就属于同一个城…