登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2

登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2

  • 前端使用vue+elementui+axios
    • 进入可视化界面创建新项目
    • 开始工作
    • 新建login.vue的js
  • 设计登录页面
  • 登陆时的表单校验
    • 表单校验的代码
    • 校验的代码
  • 重置表单
  • 登陆后台接口
  • 前端调用后端接口
    • 完成在密码框回车即登录
  • 完善登陆功能
    • 后端LoginController接口方法改动
    • 后端UserServiceImpl方法改动
    • 前台登录的方法改动
  • 路由守卫(只放行login)
    • 在main.js中添加路由守卫的代码:
  • 设置请求头的值----请求拦截器
  • 引入shiro安全框架
    • 测试接口的swagger2路径
  • 登录成功页面的布局
    • 退出的js方法
    • 解决浏览器回退按钮清空token
    • UserServiceImpl退出代码

前端使用vue+elementui+axios

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

登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2_第1张图片

开始工作

注意:
(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;
}

登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2_第2张图片
(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样式


(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
登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2_第3张图片
登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2_第4张图片

表单校验的代码

 
            <div style="margin: 20px;">div>

            <el-form class="login_form" :model="loginFormData" :rules="LoginRules">

                <el-form-item  prop="loginName">

                    <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)依赖


<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.4.5version>
        <relativePath/> 
    parent>
    <groupId>com.zzgroupId>
    <artifactId>springboot-vueartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>springboot-vuename>
    <description>Demo project for Spring Bootdescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-configuration-processorartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.4.1version>
        dependency>

        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-generatorartifactId>
            <version>3.4.1version>
        dependency>

        <dependency>
            <groupId>org.apache.velocitygroupId>
            <artifactId>velocity-engine-coreartifactId>
            <version>2.2version>
        dependency>
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
            <version>3.11version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.21version>
        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.apache.shirogroupId>
            <artifactId>shiro-spring-boot-starterartifactId>
            <version>1.7.0version>
        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)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 {
     

    /**
     * 

* 读取控制台内容 *

*/
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", "[email protected]");
        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="名称"的值
登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2_第5张图片

          //登录的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**
在这里插入图片描述
登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2_第6张图片

(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()表示登录的方法。
登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2_第7张图片

完善登陆功能

(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

后端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");
})

登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2_第8张图片

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

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

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

登录综合案例,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2_第9张图片

引入shiro安全框架

(1)引入依赖


        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-spring-boot-starterartifactId>
            <version>1.7.1version>
        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 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">选项1el-menu-item>
                            <el-menu-item index="1-2">选项2el-menu-item>
                        el-menu-item-group>
                        <el-menu-item-group title="分组2">
                            <el-menu-item index="1-3">选项3el-menu-item>
                        el-menu-item-group>
                        <el-submenu index="1-4">
                            <template slot="title">选项4template>
                            <el-menu-item index="1-4-1">选项1el-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>Mainel-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);
    }

你可能感兴趣的:(vue,springboot,shiro,redis)