1. Vue通过脚手架创建Vue工程。
1.组件【网页】--->组件【父组件】可以引用另一个组件[子组件].父组件怎么传参给子组件
2.路由:
[1]路由跳转
() this.$router.push("/路由路径")
路由配置: {path:"",component:""}
路由渲染:理解:
权限系统:
1.前端使用: vue + elementui + axios + css + html
2.后端使用: springboot+mybatis-plus +mybatis+druid+shiro+swagger2+redis

(1)Login.vue组件
- <template>
-
- <div id="building">
- <div class="login-container">
- <el-form :model="ruleForm" :rules="rules"
- status-icon
- width="200px"
- ref="ruleForm"
- class="demo-ruleForm login-page">
- <div class="block"><el-avatar :size="100" :src="circleUrl">el-avatar>div>
- <p class="denglu" style="font-family: 幼圆;font-size: 30px ">用户登录p>
- <hr style="background-color: mintcream; overflow:hidden; "/>
- <p style="font-family: 幼圆;font-size: 16px; color:white">User loginp>
- <el-form-item label="" prop="username">
- <el-input type="text"
- v-model="ruleForm.username"
- auto-complete="off"
- placeholder="请输入账号"
- >el-input>
- el-form-item>
- <el-form-item label="" prop="password">
- <el-input type="password"
- v-model="ruleForm.password"
- auto-complete="off"
- placeholder="请输入密码"
- @keyup.enter.native="handleSubmit"
- >el-input>
- el-form-item>
-
- <el-form-item style="width:100%;">
- <el-button type="primary" style="width:100px;background-color: #348C77" @click="handleSubmit" :loading="logining">登录el-button>
- el-form-item>
- el-form>
- div>
- div>
- template>
-
- <script>
- export default {
- name: "Login",
- data(){
- return{
- logining: false,
- //绑定表单数据
- ruleForm: {
- username:'',
- },
- //表单验证规则
- rules: {
- username: [
- {required: true, message: '请输入账号,账号不能为空', trigger: 'blur'},
- //{ type: 'number', message: '账号必须为数字值'},
- ],
- password: [
- {required: true, message: '请输入密码,密码不能为空', trigger: 'blur'},
- {min: 6, max: 12, message: "密码的长度必须{6~12}", trigger: "blur"},
- ]
- },
- checked: false,
- //头像绑定
- circleUrl:'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
- }
- },
- methods:{
- handleSubmit(){
- //表单校验
- this.$refs['ruleForm'].validate((valid) => {
- if (valid) {
- this.$router.push("/home")
- }
- })
- }
- }
- }
- script>
-
- <style>
- body{
- padding:0px;
- margin:0px;
- }
- /* 登录文字 */
- .denglu {
- color: white;
- font-size: 20px;
- }
- .login-container {
- width: 450px;
- height: 200px;
- margin:auto auto;
- }
- .login-page {
- /* 背景图片 */
- /*background-image: url(/imgs/);*/
- background-size: 100% 100%;
-
-
- /*background-color: #3CA993; */
-
- margin-top: 10rem;
- border-radius: 12px;
- /* 内边距 */
- padding: 20px;
- text-align: center;
- /*box-shadow: 0 8px 5px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);*/
- }
- label.el-checkbox.rememberme {
- margin: 0px 0px 25px;
- text-align: left;
- }
- #building {
- background: url("../assets/login.jpg");
- width: 100%;
- height: 100%;
- position: fixed;
- background-size: 100% 100%;
- }
- style>
(2)配置路由
- import Vue from 'vue'
- import VueRouter from 'vue-router'
- import HomeView from '../views/HomeView.vue'
- import Login from '../views/Login'
- Vue.use(VueRouter)
-
- const routes = [
- {
- /*路由路径为/则重定向到/login路由*/
- path: '/',
- redirect: '/login'
- },
- {
- path: '/login',
- name: 'Login',
- component: Login
- //component: ()=>import("../views/Login.vue")
- },
- ]
-
- const router = new VueRouter({
- //删除地址栏的#
- mode: 'history',
- routes
- })
-
- export default router

(3)登录按钮事件
如果想在vue工程中使用axios进行异步请求,则需要在main.js中导入axios
[1]//导入axiosimport axios from "axios";[2]//把axios挂载到vue对象中,以后在vue中如果使用axios直接可以用$http名称,可以随意起名
Vue.prototype.$http=axios
- methods:{
- handleSubmit(){
- //表单校验
- this.$refs['ruleForm'].validate((valid) => {
- if (valid) {
- //this.$http就是自己刚刚定义的axios
- //url:后端登录接口的路径 //es=>函数
- this.$http.post("http://localhost:8081/system/login",this.ruleForm).then(result=>{
-
- })
- }
- })
- }
- }
(1) 创建springboot项目

pox.xml文件依赖
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0modelVersion>
- <parent>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-parentartifactId>
- <version>2.3.12.RELEASEversion>
- <relativePath/>
- parent>
- <groupId>com.wzhgroupId>
- <artifactId>springboot-vue0808artifactId>
- <version>0.0.1-SNAPSHOTversion>
- <name>springboot-vue0808name>
- <description>Demo project for Spring Bootdescription>
- <properties>
- <java.version>1.8java.version>
- properties>
- <dependencies>
- <dependency>
- <groupId>com.baomidougroupId>
- <artifactId>mybatis-plus-generatorartifactId>
- <version>3.5.2version>
- dependency>
- <dependency>
- <groupId>org.freemarkergroupId>
- <artifactId>freemarkerartifactId>
- <version>2.3.31version>
- dependency>
-
- <dependency>
- <groupId>com.baomidougroupId>
- <artifactId>mybatis-plus-boot-starterartifactId>
- <version>3.5.2version>
- dependency>
- <dependency>
- <groupId>com.alibabagroupId>
- <artifactId>druid-spring-boot-starterartifactId>
- <version>1.2.8version>
- dependency>
- <dependency>
- <groupId>com.spring4allgroupId>
- <artifactId>swagger-spring-boot-starterartifactId>
- <version>1.9.1.RELEASEversion>
- dependency>
-
- <dependency>
- <groupId>com.github.xiaoymingroupId>
- <artifactId>swagger-bootstrap-uiartifactId>
- <version>1.9.6version>
- dependency>
-
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
- <dependency>
- <groupId>org.mybatis.spring.bootgroupId>
- <artifactId>mybatis-spring-boot-starterartifactId>
- <version>2.2.2version>
- dependency>
-
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- <scope>runtimescope>
- dependency>
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- <optional>trueoptional>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-testartifactId>
- <scope>testscope>
- dependency>
- dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-maven-pluginartifactId>
- <configuration>
- <excludes>
- <exclude>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- exclude>
- excludes>
- configuration>
- plugin>
- plugins>
- build>
-
- project>
(2) 使用mybatis-plus的代码生成器
- package com.wzh;
-
- import com.baomidou.mybatisplus.generator.FastAutoGenerator;
- import com.baomidou.mybatisplus.generator.config.OutputFile;
- import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
-
- import java.util.Collections;
-
- /**
- * @ProjectName: springboot-vue0808
- * @Package: com.wzh
- * @ClassName: Generator
- * @Author: 王振华
- * @Description: mp代码生成器
- * @Date: 2022/8/8 18:33
- * @Version: 1.0
- */
- public class Generator {
- public static void main(String[] args) {
- FastAutoGenerator.create("jdbc:mysql://localhost:3306/shiro_permission?serverTimezone=Asia/Shanghai", "root", "123456")
- .globalConfig(builder -> {
- builder.author("王振华") // 设置作者
- .enableSwagger() // 开启 swagger 模式
- .fileOverride() // 覆盖已生成文件
- .outputDir(".\\src\\main\\java\\"); // 指定输出目录
- })
- .packageConfig(builder -> {
- builder.parent("com.wzh") // 设置父包名
- .moduleName("system") // 设置父包模块名
- .pathInfo(Collections.singletonMap(OutputFile.xml, "src\\main\\resources\\mapper\\")); // 设置mapperXml生成路径
- })
- .strategyConfig(builder -> {
- builder.addInclude("acl_user","acl_role","acl_permission")// 设置需要生成的表名
- .addTablePrefix("acl_"); // 设置过滤表前缀
- })
- .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
- .execute();
-
- }
- }
(3)配置application文件
- #端口号
- server.port=8081
-
- #druid数据源
- spring.datasource.druid.url=jdbc:mysql://localhost:3306/shiro_permission?serverTimezone=Asia/Shanghai
- spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
- spring.datasource.druid.username=root
- spring.datasource.druid.password=123456
- #初始化的个数
- spring.datasource.druid.initial-size=5
- # 最大活跃数
- spring.datasource.druid.max-active=10
- # 最大等待时间
- spring.datasource.druid.max-wait=3000
- # 最小的闲置个数
- spring.datasource.druid.min-idle=5
-
- #mybatis-plus日志
- mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(4)登录接口
- package com.wzh.system.controller;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.wzh.system.entity.User;
- import com.wzh.system.service.IUserService;
- import com.wzh.system.vo.CommonResult;
- import com.wzh.system.vo.LoginVo;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.*;
-
- /**
- * @ProjectName: springboot-vue0808
- * @Package: com.wzh.system.controller
- * @ClassName: LoginController
- * @Author: 王振华
- * @Description:登录接口
- * @Date: 2022/8/8 18:47
- * @Version: 1.0
- */
- @RestController
- @RequestMapping("system")
- @Api(tags = "登录的接口类")
- //@CrossOrigin //解决跨域问题
- public class LoginController {
- @Autowired
- private IUserService userService;
-
- @PostMapping("/login")
- @ApiOperation(value = "登录接口")
- public CommonResult login(@RequestBody LoginVo loginVo){
- QueryWrapper
wrapper = new QueryWrapper<>(); - wrapper.eq("username",loginVo.getUsername());
- wrapper.eq("password",loginVo.getPassword());
- wrapper.eq("is_deleted",0);
- User user = userService.getOne(wrapper);
- if(user!=null){
- return new CommonResult(2000,"登录成功",null);
- }else{
- return CommonResult.LOGIN_ERROR;
- }
- }
- }
vo包:它也是实体类的一种; view obeject 视图对象。 作用:接受和响应网页的对象,
如果用map接收的话,无法使用swagger注解

前端调用后端登录接口时出现如下的错误

当使用异步请求从一个网址访问另一个网址时可能会出现跨域问题。
前提:
1. 必须为异步请求
2. 当端口号或协议或ip不同时则会出现跨域

出现两个请求: 有一个请求的方式为: OPTIONS 和真实的请求方式
理解: OPTIONS先头部队。---探视后台有没有解决跨域。
如何解决跨域:
1.前端解决
2.后端解决---->这里也有几种方式:
【1】可以借助nginx.
【2】在代码中解决 (重点)
在控制层接口上添加@CrossOrigin

@CrossOrigin(origins = {"192.168.1.14:8081","192.168.2.34:8080"},allowedHeaders="运行哪些请求头跨域",methods={RequestMethod.GET,RequestMethod.POST})
(origins = {"192.168.0.111:8080","192.168.0.120:8081"},allowedHeaders="运行哪些请求头跨域",methods={"GET","POST"})
origins: 允许哪些域可以跨域访问我这个接口
allowedHeaders:允许哪些请求头信息跨域
methods: 允许哪些请求方式跨域
上面在控制层接口处加上注解的方式解决跨域,麻烦的地方就需要对每个控制类都加该注解。 设置一个全局跨域配置类。
- package com.wzh.system.config;
-
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.cors.CorsConfiguration;
- import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
- import org.springframework.web.filter.CorsFilter;
-
- @Configuration
- public class CorsConfig {
-
- // 当前跨域请求最大有效时长。这里默认1天
- private static final long MAX_AGE = 24 * 60 * 60;
-
- @Bean
- public CorsFilter corsFilter() {
- UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
- CorsConfiguration corsConfiguration = new CorsConfiguration();
- corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
- corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
- corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
- corsConfiguration.setMaxAge(MAX_AGE);
- source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
- return new CorsFilter(source);
- }
- }
(5)加入swagger配置类
- package com.wzh.system.config;
-
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import springfox.documentation.builders.PathSelectors;
- import springfox.documentation.builders.RequestHandlerSelectors;
- import springfox.documentation.service.ApiInfo;
- import springfox.documentation.service.Contact;
- import springfox.documentation.service.VendorExtension;
- import springfox.documentation.spi.DocumentationType;
- import springfox.documentation.spring.web.plugins.Docket;
-
- import java.util.ArrayList;
-
- /**
- * @Author 闫克起
- * @Date 2021/4/29 16:37
- * @Version 1.0
- */
- @Configuration
- public class SwaggerConfig {
-
- //获取swagger2的实例对象docket
- @Bean
- public Docket getDocket() {
- Docket docket = new Docket(DocumentationType.SWAGGER_2)
- .groupName("QY151")
- .apiInfo(apiInfo())
- .select()//设置哪些包下的类生产api接口文档
- .apis(RequestHandlerSelectors.basePackage("com.wzh.system.controller"))
- //设置哪些请求路径生产接口文档
- .paths(PathSelectors.any())
- .build();
- return docket;
- }
-
- private ApiInfo apiInfo() {
- Contact DEFAULT_CONTACT = new Contact("王振华", "http://www.bing.com", "1430930278@qq.com");
- ApiInfo apiInfo = new ApiInfo("员工管理系统API接口文档", "员工管理系统API接口文档", "1.0", "http://www.bing.com",
- DEFAULT_CONTACT, "Apache 2.0",
- "http://www.apache.org/licenses/LICENSE-2.0", new
- ArrayList
()); - return apiInfo;
- }
- }

(6)登录成功后前端路由跳转

上面咱们写的登录,后端没有保存数据 前端也没有拿到数据进行保存

要用redis的原因是因为如果高并发访问数据库数据库会崩溃,造成系统瘫痪
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-data-redisartifactId>
- dependency>
application配置文件
- #redis的配置
- spring.redis.host=localhost
- spring.redis.port=6379
- spring.redis.jedis.pool.max-active=20
- spring.redis.jedis.pool.max-wait=20000
- spring.redis.jedis.pool.max-idle=10
- spring.redis.jedis.pool.min-idle=5
(1)修改登录的接口
- package com.wzh.system.controller;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.wzh.system.entity.User;
- import com.wzh.system.service.IUserService;
- import com.wzh.system.vo.CommonResult;
- import com.wzh.system.vo.LoginVo;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.web.bind.annotation.*;
-
- import java.util.UUID;
- import java.util.concurrent.TimeUnit;
-
- /**
- * @ProjectName: springboot-vue0808
- * @Package: com.wzh.system.controller
- * @ClassName: LoginController
- * @Author: 王振华
- * @Description:登录接口
- * @Date: 2022/8/8 18:47
- * @Version: 1.0
- */
- @RestController
- @RequestMapping("system")
- @Api(tags = "登录的接口类")
- //@CrossOrigin(origins = {"192.168.1.14:8081","192.168.2.34:8080"},allowedHeaders="运行哪些请求头跨域",methods={RequestMethod.GET,RequestMethod.POST}) //解决跨域问题
- public class LoginController {
- @Autowired
- private IUserService userService;
-
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- @PostMapping("/login")
- @ApiOperation(value = "登录接口")
- public CommonResult login(@RequestBody LoginVo loginVo){
- QueryWrapper
wrapper = new QueryWrapper<>(); - wrapper.eq("username",loginVo.getUsername());
- wrapper.eq("password",loginVo.getPassword());
- wrapper.eq("is_deleted",0);
- User user = userService.getOne(wrapper);
- if(user!=null){
- //随机生成一个唯一字符串。
- String token = UUID.randomUUID().toString();
- //把该token作为redis的key value为当前登录用户信息
- ValueOperations forValue = redisTemplate.opsForValue();
- forValue.set(token,user,24, TimeUnit.HOURS);
- return new CommonResult(2000,"登录成功",token);
- }else{
- return CommonResult.LOGIN_ERROR;
- }
- }
- }
RedisTemplate类需要序列化,加入一个配置类
- package com.wzh.system.config;
-
- import com.fasterxml.jackson.annotation.JsonAutoDetect;
- import com.fasterxml.jackson.annotation.PropertyAccessor;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import org.springframework.cache.CacheManager;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.data.redis.cache.RedisCacheConfiguration;
- import org.springframework.data.redis.cache.RedisCacheManager;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.RedisSerializationContext;
- import org.springframework.data.redis.serializer.RedisSerializer;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
-
- import java.time.Duration;
-
- /**
- * @program: qy151-redis-springboot
- * @description:
- * @author: 王振华
- * @create: 2022-08-08 15:16
- **/
- @Configuration
- public class RedisConfig {
- //这个是用来自己创建的RedisTemplate
- /* @Bean
- public CacheManager cacheManager(RedisConnectionFactory factory) {
- RedisSerializer
redisSerializer = new StringRedisSerializer(); - Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
- //解决查询缓存转换异常的问题
- ObjectMapper om = new ObjectMapper();
- om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
- jackson2JsonRedisSerializer.setObjectMapper(om);
- // 配置序列化(解决乱码的问题),过期时间600秒
- RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
- .entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。
- .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式
- .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化
- .disableCachingNullValues();
- RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
- .cacheDefaults(config)
- .build();
- return cacheManager;
- }*/
-
- @Bean
- public RedisTemplate
redisTemplate(RedisConnectionFactory factory) { - RedisTemplate
template = new RedisTemplate<>(); - RedisSerializer
redisSerializer = new StringRedisSerializer(); - Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
- ObjectMapper om = new ObjectMapper();
- om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
- jackson2JsonRedisSerializer.setObjectMapper(om);
- template.setConnectionFactory(factory);
- //key序列化方式
- template.setKeySerializer(redisSerializer);
- //value序列化
- template.setValueSerializer(jackson2JsonRedisSerializer);
- //value hashmap序列化 filed value
- template.setHashValueSerializer(jackson2JsonRedisSerializer);
- template.setHashKeySerializer(redisSerializer);
- return template;
- }
- }
(2)修改前端登录方法

后面每次请求都可以携带该token,

每次请求都得要人为添加参数token. 我们可以使用axios的请求拦截器。

验证token有没有被使用
- <template>
- <div>
- <el-button type="primary" @click="getInfo">获取用户信息el-button>
- div>
- template>
-
- <script>
- export default {
- name: "User",
- methods:{
- getInfo(){
- this.$http.get("http://localhost:8081/system/user/getInfo").then(result=>{
- console.log(result)
- })
- }
- }
- }
- script>
-
- <style scoped>
-
- style>
接口:
- package com.wzh.system.controller;
-
- import com.wzh.system.entity.User;
- import com.wzh.system.vo.CommonResult;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.servlet.http.HttpServletRequest;
-
- /**
- *
- * 用户表 前端控制器
- *
- *
- * @author 王振华
- * @since 2022-08-08
- */
- @RestController
- @RequestMapping("/system/user")
- public class UserController {
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- @GetMapping("getInfo")
- public CommonResult getInfo(HttpServletRequest request) {
- String token = request.getHeader("token"); //获取请求头
- System.out.println(token);
- //根据token从redis中获取用户信息
- ValueOperations forValue = redisTemplate.opsForValue();
- User o = (User) forValue.get(token);
- return new CommonResult(2000, "获取用户信息成功", o);
- }
- }
这时发现报500 原因我们的实体类有两个时间类型


解决办法:
-
- <dependency>
- <groupId>com.fasterxml.jackson.datatypegroupId>
- <artifactId>jackson-datatype-jsr310artifactId>
- <version>2.9.3version>
- dependency>
类属性上加入注解
- @JsonDeserialize(using = LocalDateTimeDeserializer.class)
- @JsonSerialize(using = LocalDateTimeSerializer.class)

运行程序成功

前置路由守卫:就是在路由跳转前加上自己得一些业务代码,在main.js中配置。类似于拦截器
- //设置前置路由守卫 to:到哪个路由 from:从哪个路由来 next():放行到指定路由
- router.beforeEach((to,from,next)=>{
- //获取跳转得路径
- var path = to.path;
- //判断是否为登录路由路径
- if(path==="/login"){
- console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
- //放行
- return next();
- }
- //其他路由路径 判断是否登录过
- var token = sessionStorage.getItem("token");
- if(token){
- return next();
- }
- //跳转登录
- return next("/login");
- })
(1)添加依赖
- <dependency>
- <groupId>org.apache.shirogroupId>
- <artifactId>shiro-spring-boot-starterartifactId>
- <version>1.7.0version>
- dependency>
(2)shiro的配置类
- package com.wzh.system.config;
-
-
- import com.wzh.system.filter.LoginFilter;
- import com.wzh.system.realm.MyRealm;
- import org.apache.shiro.authc.credential.CredentialsMatcher;
- import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
- import org.apache.shiro.realm.Realm;
- import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
- import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
- import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
- import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.web.servlet.FilterRegistrationBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.web.filter.CharacterEncodingFilter;
- import org.springframework.web.filter.DelegatingFilterProxy;
-
- import javax.servlet.Filter;
- import java.util.HashMap;
-
- /**
- * @ProjectName: springboot-shiro-swagger
- * @Package: com.wzh.config
- * @ClassName: ShiroConfig
- * @Author: 王振华
- * @Description:
- * @Date: 2022/8/5 17:32
- * @Version: 1.0
- */
- @Configuration
- public class ShiroConfig {
- @Bean
- public DefaultWebSecurityManager securityManager() {
- DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
- securityManager.setRealm(realm());
- return securityManager;
- }
-
- @Bean
- public Realm realm() {
- MyRealm myRealm = new MyRealm();
- myRealm.setCredentialsMatcher(credentialsMatcher());
- return myRealm;
- }
-
- @Bean
- public CredentialsMatcher credentialsMatcher() {
- HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
- credentialsMatcher.setHashAlgorithmName("MD5");
- credentialsMatcher.setHashIterations(1024);
- return credentialsMatcher;
- }
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- @Bean(value = "shiroFilter")
- public ShiroFilterFactoryBean filterFactoryBean() {
- ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
- filterFactoryBean.setSecurityManager(securityManager());
-
- //设置拦截规则
- HashMap
map = new HashMap<>(); - //放行路径
- map.put("/system/login", "anon");
- map.put("/doc.html", "anon");
- map.put("/swagger-ui.html", "anon");
- map.put("/swagger/**", "anon");
- map.put("/webjars/**", "anon");
- map.put("/swagger-resources/**", "anon");
- map.put("/v2/**", "anon");
- map.put("/static/**", "anon");
- //拦截路径
- map.put("/**", "authc");
- map.put("/login/logout", "logout");
- filterFactoryBean.setFilterChainDefinitionMap(map);
-
- //设置自定义认证过滤器
- HashMap
filterMap = new HashMap(); - filterMap.put("authc", new LoginFilter(redisTemplate));
- filterFactoryBean.setFilters(filterMap);
-
- return filterFactoryBean;
- }
-
- @Bean //注册filter
- public FilterRegistrationBean
filterRegistrationBean() { - FilterRegistrationBean
filterRegistrationBean = new FilterRegistrationBean<>(); - filterRegistrationBean.setName("shiroFilter");
- filterRegistrationBean.setFilter(new DelegatingFilterProxy());
- filterRegistrationBean.addUrlPatterns("/*");
- return filterRegistrationBean;
- }
-
- //开始shiro注解
- @Bean
- public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
- AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
- authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
- return authorizationAttributeSourceAdvisor;
- }
-
- @Bean
- public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
- DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
- advisorAutoProxyCreator.setProxyTargetClass(true);
- return advisorAutoProxyCreator;
- }
-
- @Bean
- public FilterRegistrationBean vv() {
- CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
- //设置字符编码
- characterEncodingFilter.setEncoding("UTF-8");
- //设置强制使用指定字符编码
- characterEncodingFilter.setForceEncoding(true);
-
-
- FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
- filterRegistrationBean.setFilter(characterEncodingFilter);
- filterRegistrationBean.addUrlPatterns("/*");
-
- return filterRegistrationBean;
-
- }
- }
(3)增加一个realm类对象
- package com.wzh.system.realm;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.wzh.system.entity.Permission;
- import com.wzh.system.entity.User;
- import com.wzh.system.service.IPermissionService;
- import com.wzh.system.service.IRoleService;
- import com.wzh.system.service.IUserService;
- import org.apache.shiro.authc.AuthenticationException;
- import org.apache.shiro.authc.AuthenticationInfo;
- import org.apache.shiro.authc.AuthenticationToken;
- import org.apache.shiro.authc.SimpleAuthenticationInfo;
- import org.apache.shiro.authz.AuthorizationInfo;
- import org.apache.shiro.authz.SimpleAuthorizationInfo;
- import org.apache.shiro.realm.AuthorizingRealm;
- import org.apache.shiro.subject.PrincipalCollection;
- import org.apache.shiro.util.ByteSource;
- import org.springframework.beans.factory.annotation.Autowired;
-
- import java.util.List;
-
- /**
- * @ProjectName: springboot-vue0808
- * @Package: com.wzh.system.realm
- * @ClassName: MyRealm
- * @Author: 王振华
- * @Description:
- * @Date: 2022/8/9 9:47
- * @Version: 1.0
- */
- public class MyRealm extends AuthorizingRealm {
- @Autowired
- private IUserService userService;
-
- @Autowired
- private IPermissionService permissionService;
-
- @Autowired
- private IRoleService roleService;
-
- //当你进行权限校验时会执行该方法
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
- /* User user = (User) principals.getPrimaryPrincipal();
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- //根据账号查找该用户具有哪些权限
- QueryWrapper
- List
list = permissionService.getOne(user.get()); - if(list!=null&&list.size()>0){
- info.addStringPermissions(list);
- }
- List
roles = roleService.findRolesById(user.getUserid()); - if(roles!=null&&roles.size()>0){
- info.addRoles(roles);
- }*/
- return null;
- }
-
- //该方法用于完成认证的功能
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
- //1.根据token获取账号
- String username = (String) token.getPrincipal();
- /**
- * 以前登陆的逻辑是 把用户和密码全部发到数据库 去匹配
- * 在shrio里面是先根据用户名把用户对象查询出来,再来做密码匹配
- */
-
- //2.根据账号查询用户信息
- QueryWrapper
wrapper = new QueryWrapper<>(); - wrapper.eq("username",username);
- wrapper.eq("is_deleted",0);
- User user = userService.getOne(wrapper);
- //表示该用户名在数据库中存在
- if(user!=null){
- /**
- * 参数说明
- * 参数1:可以传到任意对象
- * 参数2:从数据库里面查询出来的密码
- * 参数3:盐
- * 参数4:当前类名
- */
- ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
- SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),credentialsSalt,this.getName());
- return info;
- }
- return null;
- }
-
- }
(4) 修改controller代码
- package com.wzh.system.controller;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.wzh.system.entity.User;
- import com.wzh.system.service.IUserService;
- import com.wzh.system.vo.CommonResult;
- import com.wzh.system.vo.LoginVo;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.authc.UsernamePasswordToken;
- import org.apache.shiro.subject.Subject;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.web.bind.annotation.*;
-
- import javax.servlet.http.HttpServletRequest;
- import java.util.UUID;
- import java.util.concurrent.TimeUnit;
-
- /**
- * @ProjectName: springboot-vue0808
- * @Package: com.wzh.system.controller
- * @ClassName: LoginController
- * @Author: 王振华
- * @Description:登录接口
- * @Date: 2022/8/8 18:47
- * @Version: 1.0
- */
- @RestController
- @RequestMapping("system")
- @Api(tags = "登录的接口类")
- //@CrossOrigin(origins = {"192.168.1.14:8081","192.168.2.34:8080"},allowedHeaders="运行哪些请求头跨域",methods={RequestMethod.GET,RequestMethod.POST}) //解决跨域问题
- public class LoginController {
- @Autowired
- private IUserService userService;
-
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- @PostMapping("/login")
- @ApiOperation(value = "登录接口")
- public CommonResult login(@RequestBody LoginVo loginVo){
-
- try{
- Subject subject = SecurityUtils.getSubject();
- UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginVo.getUsername() ,loginVo.getPassword());
- subject.login(usernamePasswordToken);
-
- Object user = subject.getPrincipal();
- //随机生成一个唯一字符串。
- String token = UUID.randomUUID().toString();
- //把该token作为redis的key value为当前登录用户信息
- ValueOperations forValue = redisTemplate.opsForValue();
- forValue.set(token,user,24, TimeUnit.HOURS);
- return new CommonResult(2000,"登录成功",token);
- }catch (Exception e){
- e.printStackTrace();
- return CommonResult.LOGIN_ERROR;
- }
- }
- }

测试登录
登录成功后获取用户信息时出现如下得错误

被shiro的拦截器给拦截了。
- package com.wzh.system.filter;
-
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.wzh.system.vo.CommonResult;
- import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.stereotype.Component;
-
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import java.io.PrintWriter;
-
- /**
- * @ProjectName: springboot-vue0808
- * @Package: com.wzh.system.filter
- * @ClassName: LoginFilter
- * @Author: 王振华
- * @Description:
- * @Date: 2022/8/9 9:46
- * @Version: 1.0
- */
- //如果类没有交于spring容器来管理 那么该类中得属性也不能让spring帮你注入
-
- public class LoginFilter extends FormAuthenticationFilter {
-
- private RedisTemplate redisTemplate; //LoginFilter必须交于spring容器来管理。
-
- public LoginFilter(RedisTemplate redisTemplate) {
- this.redisTemplate = redisTemplate;
- }
-
- //当登录成功后执行得方法,如果该方法返回false,则执行onAccessDenied
- @Override
- protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
- HttpServletRequest req = (HttpServletRequest) request;
- //1.请求方式是否为OPTIONS
- String options = req.getMethod();
- if(options!=null && options.equals("OPTIONS")){
- return true;
- }
- String token = req.getHeader("token");
- //token可以伪造,所以也要查询redis里有没有该记录
- if(token!=null && redisTemplate.hasKey(token)){
- return true;
- }
-
- return false;
- }
- //未登录时调用该方法? 为什么进入没有登录方法:
- // --->第一个请求是OPTIONS,没有携带token 第二个因为前端和后端不是用得同一个session.默认shiro以sessionId为是否登录得标准
- @Override
- protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
- response.setContentType("application/json;charset=utf-8");
- PrintWriter writer = response.getWriter();
- CommonResult commonResult = CommonResult.UNLOGIN;
- ObjectMapper objectMapper = new ObjectMapper();
- String json = objectMapper.writeValueAsString(commonResult);
- writer.print(json);
- writer.flush();
- writer.close();
- return false;
- }
- }

- <template>
- <el-container>
- <el-header>
- <span id="logo" style="display: inline-block;width: 30%;height: 100%;float: left" >
- <a href="https://www.bilibili.com/video/BV14g41197PY/"><img src="../assets/logo.png" height="100%" width="180px">a>
- span>
- <span id="avatar" style="float: right">
- <el-dropdown @command="handleCommand">
- <span class="el-dropdown-link" style="margin-top: 10px; display: inline-block;">
- <el-avatar >el-avatar>
- span>
- <el-dropdown-menu slot="dropdown" style="margin-top: -10px">
- <el-dropdown-item command="info">个人信息el-dropdown-item>
- <el-dropdown-item command="logout">退出登录el-dropdown-item>
- el-dropdown-menu>
- el-dropdown>
- span>
- el-header>
- <el-container>
- <el-aside width="200px">
-
- el-aside>
- <el-main>
-
- el-main>
- el-container>
- <el-footer>Footerel-footer>
- el-container>
- template>
-
- <script>
- export default {
- name: "Home",
- methods:{
- getInfo(){
- this.$http.get("/system/user/getInfo").then(result=>{
- console.log(result)
- })
- },
-
- }
- }
- script>
- <style>
- html,body,#app{
- height: 100%;
- }
- body,#app{
- padding: 0px;
- margin:0px;
- }
- .el-container{
- height: 100%;
- }
- .el-header, .el-footer {
- background-color: #1F272F;
- color: #333;
- line-height: 60px;
- }
-
- .el-aside {
- background-color: #545c64;
- color: #333;
- line-height: 560px;
- }
- .el-aside>.el-menu{
- border: none;
- }
- .el-main {
- background-color: #E9EEF3;
- color: #333;
- line-height: 560px;
- }
-
- body > .el-container {
- margin-bottom: 40px;
- }
-
- .el-container:nth-child(5) .el-aside,
- .el-container:nth-child(6) .el-aside {
- line-height: 260px;
- }
-
- .el-container:nth-child(7) .el-aside {
- line-height: 320px;
- }
- style>

前端:
由于每个跳转都需要写ip和端口,我们可以在main.js中设置axios的基础路径
//设置axios基础路径 axios.defaults.baseURL="http://localhost:8082"

后端:
- @GetMapping("/logout")
- public CommonResult logout(HttpServletRequest request){
- String token = request.getHeader("token");
- if(redisTemplate.hasKey(token)){
- redisTemplate.delete(token);
- return new CommonResult(2000,"退出成功",null);
- }
- return new CommonResult(5000,"请先登录",null);
- }
(1)前端
- <template>
- <el-container>
- <el-header>
- <span id="logo" style="display: inline-block;width: 30%;height: 100%;float: left" >
- <a href="https://www.bilibili.com/video/BV14g41197PY/"><img src="../assets/logo.png" height="100%" width="180px">a>
- span>
- <span id="avatar" style="float: right">
- <el-dropdown @command="handleCommand">
- <span class="el-dropdown-link" style="margin-top: 10px; display: inline-block;">
- <el-avatar >el-avatar>
- span>
- <el-dropdown-menu slot="dropdown" style="margin-top: -10px">
- <el-dropdown-item command="info">个人信息el-dropdown-item>
- <el-dropdown-item command="logout">退出登录el-dropdown-item>
- el-dropdown-menu>
- el-dropdown>
- span>
- el-header>
- <el-container>
- <el-aside width="200px">
- <el-menu
- default-active="2"
- class="el-menu-vertical-demo"
- background-color="#545c64"
- text-color="#fff"
- active-text-color="#ffd04b">
- <el-submenu :index="menu.id+''" v-for="menu in leftMenus">
- <template slot="title">
- <i :class="menu.icon">i>
- <span>{{menu.name}}span>
- template>
- <el-menu-item :index="second.id+''" v-for="second in menu.children">
- <i :class="second.icon">i>
- <span><a style="color: white;text-decoration: none"
- >{{second.name}}a>span>
- el-menu-item>
- el-submenu>
-
- el-menu>
- el-aside>
- <el-main>
-
- el-main>
- el-container>
- <el-footer>Footerel-footer>
- el-container>
- template>
-
- <script>
- export default {
- name: "Home",
- data(){
- return{
- leftMenus:[]
- }
- },
- methods:{
- initLeftMenu(){
- this.$http.get("/system/permission/leftMenu").then(result=>{
- if(result.data.code===2000){
- this.leftMenus=result.data.data;
- }
- })
- },
- getInfo(){
- this.$http.get("/system/user/getInfo").then(result=>{
- console.log(result)
- })
- },
- //下拉的触发事件
- handleCommand(command) {
- if (command === 'logout') {
- this.$http.get("/system/logout").then(result => {
- if (result.data.code === 2000) {
- console.log("11111")
- sessionStorage.clear();
- //sessionStorage.removeItem("token");
- this.$router.push("/login")
- }
- })
- }
- }
- }
- }
- script>
- <style>
- html,body,#app{
- height: 100%;
- }
- body,#app{
- padding: 0px;
- margin:0px;
- }
- .el-container{
- height: 100%;
- }
- .el-header, .el-footer {
- background-color: #1F272F;
- color: #333;
- line-height: 60px;
- }
-
- .el-aside {
- background-color: #545c64;
- color: #333;
- line-height: 560px;
- }
- .el-aside>.el-menu{
- border: none;
- }
- .el-main {
- background-color: #E9EEF3;
- color: #333;
- line-height: 560px;
- }
-
- body > .el-container {
- margin-bottom: 40px;
- }
-
- .el-container:nth-child(5) .el-aside,
- .el-container:nth-child(6) .el-aside {
- line-height: 260px;
- }
-
- .el-container:nth-child(7) .el-aside {
- line-height: 320px;
- }
- style>
(2)后端:
- @RestController
- @RequestMapping("/system/permission")
- public class PermissionController {
-
- @Autowired
- private IPermissionService permissionService;
-
- @GetMapping("leftMenu")
- public CommonResult leftMenu(HttpServletRequest request){
- String token = request.getHeader("token");
- return permissionService.findPermissionByUserId(token);
- }
-
- }
service
- package com.wzh.system.service.impl;
-
- import com.wzh.system.entity.Permission;
- import com.wzh.system.entity.User;
- import com.wzh.system.mapper.PermissionMapper;
- import com.wzh.system.service.IPermissionService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.wzh.system.vo.CommonResult;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Service;
-
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- *
- * 权限 服务实现类
- *
- *
- * @author 王振华
- * @since 2022-08-09
- */
- @Service
- public class PermissionServiceImpl extends ServiceImpl
implements IPermissionService { -
- @Autowired
- private PermissionMapper permissionMapper;
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- @Override
- public CommonResult findPermissionByUserId(String token) {
- //根据token获取用户信息
- ValueOperations forValue = redisTemplate.opsForValue();
- User user = (User) forValue.get(token);
- //根据用户id查询该用户具有的权限。
- List
permissionList = permissionMapper.selectByUserId(user.getId()); -
- //设置层级关系
- List
firstMenus = new ArrayList<>(); - for (Permission firstMenu : permissionList){
- if(firstMenu.getPid().equals("1")){
- firstMenus.add(firstMenu);
- }
- }
-
- //为一级菜单设置二级菜单
- for (Permission first : firstMenus){
- //根据一级菜单id 查询 该菜单得二级菜单。如果出现不确定有几级菜单 那么我们可以使用方法得递归调用
- first.setChildren(findChildren(permissionList, first.getId()));
- }
- return new CommonResult(2000,"查询成功",firstMenus);
- }
- //方法递归
- private List
findChildren(List permissionList, String pid) { - List
children = new ArrayList<>(); - for (Permission p : permissionList){
- if(p.getPid().equals(pid)){
- children.add(p);
- }
- }
-
- for(Permission child : children){
- child.setChildren(findChildren(permissionList,child.getId()));
- }
-
- return children;
- }
- }
实体类添加列

推荐使用下一篇文章的,用到了递归,不管几级菜单都可以