前端使用vue+elementui+axios

进入可视化界面创建新项目

在这里插入图片描述

开始工作

注意:
(1) App.vue中必须要加路由渲染

(2)在index.js中配置login.vue的路径

 {
    path: '/login',
    name: 'Login',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/login.vue')
  }

新建login.vue的js

export default 只能导出一个默认模块

<script>
    export default {
        name: "login"
    }
</script>

注意:如果从git上下载项目,就要执行以下命令
在这里插入图片描述

设计登录页面

(1)login.vue组件页面

<template>
<!--    登录的容器-->
    <div class="login_container">
登录页面
    </div>
</template>

(2) 设置登陆界面的css样式

<style scoped>
.login_container{
    background-color: #e2a5b9;
    height: 100%;
}
</style>

(3)发现.login_container高度无效。 在assets下创建一个全局的css

body,html,#app{
    height: 100%;
    padding: 0;
    margin: 0;
    font-size: 12px;
}

在这里插入图片描述
(4)注意:一定要把全局css导入main.js 使用需要的插件,作用:加载公共组件

//导入全局css
import './assets/css/global.css'

(5)登陆框和登陆框的头像设计布局

<template>
    <!--    登录的容器-->
    <div class="login_container">
        <!-- 登录盒子  -->
        <div class="login_box">
            <!-- 头像 -->
            <div class="avatar_box">
                <img src="../assets/dog.png" alt="">
            </div>
            <!-- 登录表单 -->

        </div>
    </div>
</template>

(6)css样式

<style scoped>

   .login_container{
        background-color: #2b5b6b;
        height: 100%;
   }

   .login_box {
        width: 450px;
        height: 300px;
        background: #fff;
        border-radius: 3px;
        /**绝对定位*/
        position: absolute;
        /**左偏移*/
        left: 50%;
        /**上偏移*/
        top: 50%;
        /**减去容器自身的宽高*/
        transform: translate(-50%, -50%);
   }
     .login_box>.avatar_box{
          height: 130px;
          width: 130px;
          border: 1px solid #eee;
          border-radius: 50%;
          padding: 5px;
          /**阴影*/
          box-shadow: 0 0 10px #ddd;
          /**绝对定位*/
          position: absolute;
          left: 50%;
          transform: translate(-50%, -50%);
          background-color: pink;

 }
     .login_box>.avatar_box>img {
          width: 100%;
          height: 100%;
          border-radius: 50%;
          background-color: #eee;
     }

</style>

(7)设计表单输入框

<template>
    <!--    登录的容器-->
    <div class="login_container">
        <!-- 登录盒子  -->
        <div class="login_box">
            <!-- 头像 -->
            <div class="avatar_box">
                <img src="../assets/dog.png" alt="">
            </div>
            <!-- 登录表单 -->
            <div style="margin: 20px;"></div>
            <el-form class="login_form"  >
                <el-form-item >
                    <el-input prefix-icon="el-icon-user" placeholder="请输入用户名" v-model="loginFormData.loginName" ></el-input>
                </el-form-item>
                <el-form-item >
                    <el-input prefix-icon="el-icon-lock" placeholder="请输入密码" v-model="loginFormData.password"></el-input>
                </el-form-item>
                <el-form-item class="btns">
                    <el-button type="primary" >提交</el-button>
                    <el-button>重置</el-button>
                </el-form-item>
            </el-form>

        </div>
    </div>
</template>

(8)表单的css

    /*表单的设计*/
    .login_form {
        position: absolute;
        bottom: 0;
        width: 100%;
        padding: 0 20px;
        box-sizing: border-box;
    }
    .btns {
        display: flex;
        justify-content: flex-end;
    }

登陆时的表单校验

(1)在表单元素上添加 :rules=“myRules”
(2)在data中定义校验规则:
data(){
return {
myRules:{
}
}
}

(3)在表单元素上使用指定的校验规则

注意:如果vue表单输入的有数据,但是提示不能为空,检查以下
第一个地方是el-form标签是否绑定了值,也就是:model=""
第二个地方是el-form标签是否绑定了表单验证规则,也就是:rules=""
第三个地方是el-form-item标签是否有prop,并且prop的值是否和rules的值对应
注意:如果表单输入不上值,一定要把表单中的:model属性改为v-model
在这里插入图片描述
在这里插入图片描述

表单校验的代码

 <!-- 登录表单   -->
            <div style="margin: 20px;"></div>
<!--            添加校验规则第一步添加:rules="loginRules"  表单的校验规则-->
            <el-form class="login_form" :model="loginFormData" :rules="LoginRules">
<!--                第二步加上prop这个属性 表示使用表单中的那个校验规则-->
                <el-form-item  prop="loginName">
<!--                第三步注意prop的属性值是否与每个输入框里面的v-model的属性是否对照-->
                    <el-input prefix-icon="el-icon-user" placeholder="请输入用户名" v-model="loginFormData.loginName" ></el-input>
                </el-form-item>
                <el-form-item prop="password">
                    <el-input prefix-icon="el-icon-lock" placeholder="请输入密码" v-model="loginFormData.password"></el-input>
                </el-form-item>
                <el-form-item class="btns">
                    <el-button type="primary" >提交</el-button>
                    <el-button>重置</el-button>
                </el-form-item>
            </el-form>

校验的代码

<script>
    export default {
        name: "login",
        data(){
            return {
                loginFormData:{
                    loginName:"",
                    password:"",
                },
                //定义登录的校验规则
                LoginRules:{
                    loginName:[
                        { required: true, message: '用户名不能为空', trigger: 'blur' },
                        { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
                    ],
                    password:[
                        { required: true, message: '密码不能为空', trigger: 'blur' },
                        { min: 6, max: 8, message: '长度在 6到 8 个字符', trigger: 'blur' }
                    ]
                }
            }
        }
    }
</script>

重置表单

(1)为表单添加一个属性 ref=“名称”
在这里插入图片描述
(2)重置的语句 loginFormRef是指ref的值
this.$refs.loginFormRef.resetFields();
重置的方法:

       methods: {
            //重置方法
            loginReset() {
                console.log(this);
                this.$refs.loginFormRef.resetFields();
            },
       }

登陆后台接口

(1)依赖

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zz</groupId>
    <artifactId>springboot-vue</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-vue</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
<!--        mybatis-plus/依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
<!--        mybatis-plus-generator代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
<!--        模板引擎 依赖,-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.11</version>
        </dependency>
<!--        druid数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>
<!--        swagger2-->
        <dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>1.9.1.RELEASE</version>
        </dependency>
<!--        swagger  好看的ui-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.6</version>
        </dependency>
<!--        引入shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.7.0</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

(2)mp代码生成器

package com.zz.springbootvue01;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
//        获取工程的根目录
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");//
        gc.setAuthor("张峥");
        gc.setOpen(false);//是否生成代码后打开本地目录
        gc.setSwagger2(true); //是否生存实体属性 Swagger2 注解
        gc.setServiceName("%sService"); //service命名
        gc.setMapperName("%sDao"); //Dao命名
//
        mpg.setGlobalConfig(gc);//是否设置全局配置

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/springboot_vue?serverTimezone=Asia/Shanghai&useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);


        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("aaa");//模块名
        pc.setParent("com.zz");//设置父包  com.zz.aaa.controller  dao  service entity
//        设置dao
        pc.setMapper("dao");
        mpg.setPackageInfo(pc);


        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 velocity
        String templatePath = "/templates/mapper.xml.vm";
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
//        预计目录   mapper/aaa/UserMapper.xml
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        //关闭系统模板引擎
        templateConfig.setXml(null);
        //放入空的模板引擎替换掉默认的模板引擎
        mpg.setTemplate(templateConfig);
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);


        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);//是否采用驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//列是否要驼峰命名
        strategy.setEntityLombokModel(true);//        是否要lombok
        //        是否要前缀
        strategy.setTablePrefix("acl_");
        strategy.setRestControllerStyle(true);//controller是否使用restful风格

        mpg.setStrategy(strategy);
        mpg.execute();
    }
}

(3)swagger2的配置,注意要更改在哪个包下生产api文档
.apis(RequestHandlerSelectors.basePackage(“com.zz.aaa.controller”))

@Configuration//配置类
public class SwaggerConfig {

//    swagger2的是实例对象Docket

    @Bean
    public Docket getDocket() {
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName("Qy129")
                .apiInfo(apiInfo())
                .select()
//                设置哪些包下的类生产api文档
                .apis(RequestHandlerSelectors.basePackage("com.zz.aaa.controller"))
//              设置哪些请求路径生产接口文档
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

    private ApiInfo apiInfo() {
        Contact DEFAULT_CONTACT = new Contact("张峥", "http://www.jd.com", "2300326070@qq.com");
        ApiInfo apiInfo = new ApiInfo("管理系统api接口", "管理系统api接口", "2.0", "http://www.baidu.com",
                DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<VendorExtension>());
        return apiInfo;
    }

}

(4)主启动类

package com.zz.springbootvue01;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication
@EnableSwagger2//开始awagger2注解
@MapperScan("com.zz.aaa.dao")//扫描dao层
public class SpringbootVue01Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootVue01Application.class, args);
    }
}

注意:在主实现类上开启swagger2的注解和包扫描的注解
@EnableSwagger2//开启swagger2的注解
@MapperScan(“com.zz.aaa.dao”)//扫描dao层
(5)application.yml的配置信息

spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/springboot_vue?serverTimezone=Asia/Shanghai
      username: root
      password: root

server:
  port: 8081

logging:
  level:
    com.zz.aaa.dao: debug

(6)LoginController层登录接口代码

package com.zz.aaa.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zz.aaa.entity.User;
import com.zz.aaa.vo.CommonResult;
import com.zz.aaa.vo.LoginVo;
import com.zz.aaa.service.UserService;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author : 小峥
 * @date : 2021/5/2 22:28
 * @description:
 */
@RestController
@Api("登录接口API")
@RequestMapping("/aaa/login")
public class LoginController {
    @Resource
    private UserService userService;

    /**
     * 登录的接口
     * @return
     */
    @PostMapping("login")//LoginVo用来接收登陆者的信息
    public CommonResult login(LoginVo loginVo) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", loginVo.getLoginName());
        wrapper.eq("password", loginVo.getPassword());
        User user = userService.getOne(wrapper);
        if (user != null) {
            return new CommonResult(2000, "登录成功", user);
        } else {
            return new CommonResult(5000, "登录失败", null);
        }
    }
}

CommonResult 实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("响应的对象")
public class CommonResult {
    @ApiModelProperty("响应的状态码")
    private Integer code;
    @ApiModelProperty("响应的信息")
    private String msg;
    @ApiModelProperty("响应的数据")
    private Object result;
}

接收登陆者的信息LoginVo实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("登录的对象")
public class LoginVo {
    @ApiModelProperty(value = "账户")
    private String loginName;
    @ApiModelProperty(value = "密码")
    private String password;
}

前端调用后端接口

(1)在main.js中引入axios并挂载到Vue对象上

import axios from 'axios'

//把axios挂载到vue对象
Vue.prototype.$http=axios;

(2)登录页面ajax请求登录成功跳转home.vue页面,这里一定要先表单验证成功后在进行ajax操作
这里的官网上的foemName是指表单上ref="名称"的值
在这里插入图片描述

          //登录的ajax
            submit() {
                var that=this;
                this.$refs.loginFormRef.validate((valid) => {
                    //表单验证成功后
                    if (valid) {
                        this.$http.post("http://localhost:8081/aaa/login/login", this.loginFormData).then(function (dataInfo) {
                            // console.log(dataInfo)
                            if (dataInfo.data.code === 2000) {
                                //显示登录成功
                                that.$message.success(dataInfo.data.msg);
                            //    并且跳转页面
                                that.$router.push("/home");
                            } else {
                                console.log(dataInfo.data.msg)
                                that.$message.error(dataInfo.data.msg);
                            }
                        })
                    }
                });

            }

(3)以下表示出现跨域问题
什么情况下会出现跨域问题?
(1)必须是ajax请求
(2)从一个区域请求另一个区域,协议不同或者ip不同或者端口号不同。
解决:只需要在后台LoginController页面加上注解**@CrossOrigin**
在这里插入图片描述
在这里插入图片描述

(4)注入home.vue组件
①创建home.vue组件
②在index.js中注册home.vue组件

  {
    path: '/home',
    name: 'Home',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/home.vue')
  }

完成在密码框回车即登录

(1)在表单上添加 @submit.native.prevent
(2)在密码框上添加 @keyup.enter.native.prevent=“submit()”
添加回车触发登录的方法 这里的submit()表示登录的方法。
在这里插入图片描述

完善登陆功能

(1)登录成功后,前端要保存登录这的信息
保存在sessionStorage.setItem(key,value)
sessionStorage在当前窗口有效
localStorage再新开一个窗口也有效,关闭浏览器无效
(2)登录成功后,后端也会保存登录者的信息
保存在redis中,并且会随机产生一个key,value为用户的信息,并且把key相应给前端。
注意要先把redis的配置添加到application.yml

spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql:///springboot_vue?serverTimezone=Asia/Shanghai
      username: root
      password: root
  redis:
    host: 127.0.0.1
    port: 6379

server:
  port: 8081

logging:
  level:
    com.zz.dao: debug

添加RedisConfig 配置类,自定义序列化方式

package com.zz.aaa.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.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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;

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    //比如验证码
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> 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序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> 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;
    }
}

后端LoginController接口方法改动

@PostMapping("login")
    private CommonResult login(@ApiParam("登录者信息") @RequestBody LoginVo loginVo) {
        return userService.findByNameAndPassword(loginVo);
    }

后端UserServiceImpl方法改动

@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {
    @Resource
    private UserDao userDao;
    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public CommonResult findByNameAndPassword(LoginVo loginVo) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", loginVo.getLoginName());
        wrapper.eq("password", loginVo.getPassword());
        User user = userDao.selectOne(wrapper);
        if (user!= null) {
//            登录成功就把用户的信息保存到redis
//            随机产生一个key
            String key = UUID.randomUUID().toString().replace("-", "");
//            把随机产生的key作为key,用户的信息当做value
            redisTemplate.opsForValue().set(key,user,24, TimeUnit.HOURS);
            return new CommonResult(2000, "登录成功", key);
        } else {
            return new CommonResult(5000, "登录失败", null);

        }
    }
}

前台登录的方法改动

        //登录的ajax
            submit() {
                var that=this;
                this.$refs.loginFormRef.validate((valid) => {
                    //表单验证成功后
                    if (valid) {
                        this.$http.post("http://localhost:8081/aaa/login/login", this.loginFormData).then(function (dataInfo) {
                            if (dataInfo.data.code === 2000) {
                                //显示登录成功
                                that.$message.success(dataInfo.data.msg);
                                //    把用户的key保存起来  sessionStorage  localStorage 本地
                                // console.log(dataInfo.data.result)
                                sessionStorage.setItem("token",dataInfo.data.result);
                            //    并且跳转页面
                                that.$router.push("/home");
                            } else {
                                console.log(dataInfo.data.msg)
                                that.$message.error(dataInfo.data.msg);
                            }
                        })
                    }
                });

            }

路由守卫(只放行login)

在这里插入图片描述

在main.js中添加路由守卫的代码:

//to:路由跳转的路径
//from:从哪跳转来的
//next:执行
router.beforeEach((to,from,next)=>{
  var path=to.path;
  if (path==="/login"){//如果跳转路径为login则放行
    return next();
  }
//  判断是否携带token值
  var token=sessionStorage.getItem("token");
  if (token){
    return next();//已经登录则放行
  }
  return next("/login");
})

在这里插入图片描述

设置请求头的值----请求拦截器

在main.js中添加,要放在挂载vue对象的上面

//axios请求拦截器
axios.interceptors.request.use(config=>{
  var token = sessionStorage.getItem("token");
  if (token){
 config.headers.token=token;
  }
  return config;
})

在这里插入图片描述

引入shiro安全框架

(1)引入依赖

<!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.7.1</version>
        </dependency>

(2)引入ShiroConfig配置类,注意:这里还需要放行swagger的相关路径
如果加了@CrossOrigin跨域注解,前台访问还是报错跨域问题,则查看访问的路径是否放行,或是否有权限 等原因都会导致跨域
一定要放行登录的路径退出的路径

package com.zz.aaa.config;

import com.zz.aaa.realm.MyRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.Filter;
import java.util.HashMap;

@Configuration
public class ShiroConfig {

    @Bean("securityManager")
    public DefaultWebSecurityManager securityManager(Realm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        return securityManager;
    }


    //    IOC控制反转  DI依赖注入
    @Bean(value = "myRealm")
    public Realm getRealm(CredentialsMatcher credentialsMatcher) {
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(credentialsMatcher);
        return myRealm;
    }

    @Bean(value = "credentialsMatcher")
    public CredentialsMatcher getCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashIterations(1024);
        credentialsMatcher.setHashAlgorithmName("MD5");
        return credentialsMatcher;
    }

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //重点  shiro 不配置登录页面  会默认跳转到login.jsp
        HashMap<String, String> map = new HashMap<>();
        map.put("/aaa/login/login", "anon");
        map.put("/aaa/login/exit", "anon");
//        这里还需要放行swagger的相关路径
        map.put("/doc.html","anon"); //anon表示放行
        map.put("/webjars/**","anon"); //anon表示放行
        map.put("/swagger-resources/**","anon"); //anon表示放行
        map.put("/v2/**","anon"); //anon表示放行
        map.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }


    //    注册filter组件
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setName("shiroFilter");
        filterRegistrationBean.setFilter(new DelegatingFilterProxy());
        filterRegistrationBean.addUrlPatterns("/*");

        return filterRegistrationBean;
    }
}

(3)引入MyRealm

package com.zz.aaa.realm;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zz.aaa.entity.User;
import com.zz.aaa.service.UserService;
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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import javax.annotation.Resource;

public class MyRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//        User user = (User) principalCollection.getPrimaryPrincipal();
//        List<String> permissions = userService.findPermissionByUserid(user.getUserid());
//        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        if (permissions.size()>0){
//            info.addStringPermissions(permissions);
//        }
//        return info;
        return null;
    }
    
    /**
     * 登录认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//        获取用户的用户名
        String username = authenticationToken.getPrincipal().toString();
//        根据用户名获取用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        User user = userService.getOne(wrapper);
        if (user!=null){
//            获得盐
            ByteSource salt = ByteSource.Util.bytes(user.getSalt());
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), salt, this.getName());
            return info;
        }
        return null;
    }

}

(4)UserServiceImpl类,service的代码

@Service
@Slf4j//打印日志
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {
    @Resource
    private UserDao userDao;
    @Resource
    private RedisTemplate redisTemplate;

        @Override
    public CommonResult findByNameAndPassword(LoginVo loginVo) {

            try {
                Subject subject = SecurityUtils.getSubject();
                UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getLoginName(),loginVo.getPassword());
                subject.login(token);
//                获得登录的信息
                Object user = subject.getPrincipal();
                //  登录成功就把用户的信息保存到redis
//            随机产生一个key
                String key = UUID.randomUUID().toString().replace("-", "");
//            把随机产生的key作为key,用户的信息当做value
                redisTemplate.opsForValue().set(key,user,24,TimeUnit.HOURS);
                return new CommonResult(2000, "登录成功", key);
            } catch (AuthenticationException e) {
                e.printStackTrace();
                return new CommonResult(5000, "登录失败", null);
            }
    }
}

测试接口的swagger2路径

http://localhost:8081/doc.html

登录成功页面的布局

<template>
    <el-container class="home-container">
        <el-header>
            <div><img src="../assets/dog.png" width="60" alt="">
                <span>进入首页</span>
                <el-button type="danger" @click="exit">危险按钮</el-button>
            </div>
        </el-header>
        <el-container>
            <el-aside width="200px">
                <el-menu
                        default-active="2"
                        class="el-menu-vertical-demo">
                    <el-submenu index="1">
                        <template slot="title">
                            <i class="el-icon-location"></i>
                            <span>导航一</span>
                        </template>
                        <el-menu-item-group>
                            <template slot="title">分组一</template>
                            <el-menu-item index="1-1">选项1</el-menu-item>
                            <el-menu-item index="1-2">选项2</el-menu-item>
                        </el-menu-item-group>
                        <el-menu-item-group title="分组2">
                            <el-menu-item index="1-3">选项3</el-menu-item>
                        </el-menu-item-group>
                        <el-submenu index="1-4">
                            <template slot="title">选项4</template>
                            <el-menu-item index="1-4-1">选项1</el-menu-item>
                        </el-submenu>
                    </el-submenu>
                    <el-menu-item index="2">
                        <i class="el-icon-menu"></i>
                        <span slot="title">导航二</span>
                    </el-menu-item>
                    <el-menu-item index="3" disabled>
                        <i class="el-icon-document"></i>
                        <span slot="title">导航三</span>
                    </el-menu-item>
                    <el-menu-item index="4">
                        <i class="el-icon-setting"></i>
                        <span slot="title">导航四</span>
                    </el-menu-item>
                </el-menu>
            </el-aside>
            <el-main>Main</el-main>
        </el-container>
    </el-container>
</template>

退出的js方法

<script>
    export default {
        name: "home",
        methods: {
            //退出的方法
            exit() {
                var that = this;
                this.$http.get("http://localhost:8081/aaa/login/exit").then(function (dataInfo) {
                //清除sessionStorage
                    sessionStorage.clear();
                    that.$router.push("/login");
                })
            }
        }
    }
</script>

解决浏览器回退按钮清空token

// 解决浏览器回退按钮清空token
    var guanzhu ='http://www.baidu.com';
    window.onhashchange = function () {
        var that=this;
        /*  this.$http.get("http://localhost:8888/sys/login/logout").then(function(resp){*/
        this.axios.get("http://localhost:8081/aaa/login/exit").then(function(resp){
            sessionStorage.clear();
            that.$router.push("/login")
        });
        // location.href = guanzhu+"?s=mRygKs" + (parseInt((parseInt(new Date().getTime() / (100 * 5 * 1)) + '').substring(2)) + 5000);
    };

UserServiceImpl退出代码

    @Override
    public CommonResult exit() {
        //        获取request对象
        HttpServletRequest request = WebUtil.getRequest();
        String token = request.getHeader("token");
        if ("".equals(token)){
            redisTemplate.delete(token);
        return new CommonResult(2000,"退出成功",null);
        }
        return new CommonResult(5000,"退出失败",null);
    }

后续有登录综合案例02,退出完善,菜单权限,以及遇到的一些问题,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2

Logo

前往低代码交流专区

更多推荐