权限系统--前后端分离

目录

1.前端使用:

2.后端使用:

3.前端页面--登录页面

3.1.在views下新建一个视图,作为登录的视图

3.2.在登录试图视图中,写入登录页面需要的代码

3.2.1.登录页面

3.2.2.登录页面的数据和方法

3.2.3.登录页面的样式

3.2.4.渲染主件

3.2.5.设置路由

3.2.6.跳转页面

3.2.7.运行之后的样子

3.4.8.登录按钮事件

4.后端登录接口

4.1.新建一个springboot工程

4.2.引入相关的依赖

4.3.mp的代码生成器

4.4.运行代码生成器

4.5.配置application文件

4.6.配置swagger的工具类

4.7.创建同一返回的数据信息加swagger的注解

4.7.创建登录参数的实体类加swagger注解

4.8.后端登录接口加swagger的注解

5.登录的bug

5.1.修改登录接口

5.1.1.如果在数据库查询到了用户信息,然后将用户信息保存

5.1.2.将保存的信息给前端

5.1.3.axios得请求拦截器、

6.前置路由守卫

6.1.什么是前置路由守卫

6.2.前置路由守卫的语法

6.3.使用前置路由守卫 --在main.js

7.整合shiro

7.1.添加shiro依赖

7.2.shiro得配置类

7.3.增加一个realm类对象

7.4.新增一个拦截器

7.5.修改controller代码

8.主页得布局

8.1.前端

9.退出登录

9.1.前端

9.2.后端

10.查询左侧菜单

10.2.请求路径

10.3.controller层

10.4.service层

10.5.mapper层

10.6.seriveimpl业务代码

10.7.连表



1.前端使用:

vue + elementui + axios + css + html

2.后端使用:

springboot+mybatis-plus +mybatis+druid+shiro+swagger2+redis

3.前端页面--登录页面

3.1.在views下新建一个视图,作为登录的视图

3.2.在登录试图视图中,写入登录页面需要的代码

3.2.1.登录页面

<template>
    <!--这里必须使用一个双标签-->
    <div id="login_box">
        <div class="img_position">
            <el-avatar :size="140" :src="imgUrl"></el-avatar>
        </div>
        <el-card class="box-card">
            <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
                <el-form-item label="用户名" prop="name">
                    <el-input type="text" v-model="ruleForm.name" autocomplete="off"></el-input>
                </el-form-item>
                <el-form-item label="密码" prop="password">
                    <el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input>
                </el-form-item>
                <el-form-item>
    <!--登录按钮-->
                    <el-button type="primary"
                               size="mini"
                               :plain="true"
                               @click="login"
                               style="margin-left: 100px;width: 100px">登录</el-button>
                </el-form-item>
            </el-form>
        </el-card>
    </div>

</template>

3.2.2.登录页面的数据和方法

<script>
    export default {
        name: "Login",
        data(){
            return{
                ruleForm: {
                    name: '',
                    password: ''
                },
                rules: {
                    name: [
                        {required: true, message:'用户名不能为空', trigger: 'blur'},
                    ],
                    password: [
                        {required: true, message: '密码不能为空', trigger: 'blur'},
                    ]
                },
                imgUrl:'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png'
            }
            },
        methods:{
            login(){
                this.$refs['ruleForm'].validate((valid) => {
                    if(valid) {

                         this.$message.success("登录成功")
                    } else {
                         this.$message.error("登录失败")
                     }
                })
            }
        }


    }

3.2.3.登录页面的样式

<style>
    #login_box{
        position: relative;
        width: 500px;
        margin: 250px auto;
    }
    #login_box div.img_position{
        position: absolute;
        left: 200px;
        top: -70px;
    }
    .text {
        font-size: 14px;
    }

    .item {
        padding: 18px 0;
    }

    .box-card {
        padding: 100px 50px 0 0;
        width: 480px;
    }
</style>

3.2.4.渲染主件

<!--渲染主件-->
<router-view/>   这个是在app.vue 下渲染的,也就是打开网页默认的页面

 3.2.5.设置路由

//不管这个路径有没有被访问,一定会导入的网页
import Login from "../views/Login";
{
  path:'/login', --路径
  name:'Login', --views下的视图
 component:Login --组件的名称
}

打开网页之后输入login或者/login,就会去找路由下的index .js  在里面去找有没有/login的路径,找到之后根据import Login from "../views/Login";这句话,去找views下的视图页面,然后再网页上显示

3.2.6.跳转页面

{
  path: "/",
  redirect: "/login"
}

刚打开网页的时候上面的网址是http://localhost:8080/#/,显示一片空白,想要登录页面还得输入login或者/login,有点麻烦,而且一打开页面应该是登录页面,不是空白的页面,上面的代码是,一打开页面重定向的login

3.2.7.运行之后的样子

 3.4.8.登录按钮事件

我想登录成功之后,跳转到主页面,这个就用到了axios

[1]//导入axios --在main.js中
import axios from "axios";


[2]//把axios挂载到vue对象中,以后在vue中如果使用axios直接可以用$http名称
Vue.prototype.axios=axios

方法:

 methods:{
            login(){
                //表单校验
                this.$refs['ruleForm'].validate((valid) => {
                        if(valid){
                             //url:后端登录接口的路径 this.ruleForm是表单
                             this.$http.post("http://localhost:8808/user/login",this.ruleForm).then(result=>{
                                    //登录成功之后跳转到主页面--路由跳转
                                this.$router.push("/home")

                             });
                        }
                })
            }
        }

路由设置

 {
    path: '/home',
    name: '/Home',
    component: ()=>import("../views/Home")
  }

4.后端登录接口

4.1.新建一个springboot工程

 注意:选择版本,要不然会报错--8的版本

4.2.引入相关的依赖

<?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.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.guan</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatis-plus-vue</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>1.9.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </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>
    </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>

4.3.mp的代码生成器

package com.guan.demo;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.Collections;

/**
 * TODO
 *
 * @author lenovo
 * @version 1.0
 * @since 2022-08-08  10:02:23
 */
public class Generator {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/day?serverTimezone=Asia/Shanghai", "root", "grt081141" +
                "")
                .globalConfig(builder -> {
                    builder.author("guan") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir(".\\src\\main\\java\\"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.guan.demo") // 设置父包名
                            .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();

    }
}

4.4.运行代码生成器

4.5.配置application文件

#设置端口号--一定要设置端口号,不设置的话,端口号会冲突
server.port=8888
spring.datasource.druid.url=jdbc:mysql://localhost:3306/day?serverTimezone=Asia/Shanghai 
spring.datasource.druid.username=root
spring.datasource.druid.password=grt081141
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver

#日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

4.6.配置swagger的工具类

@Configuration    //该注解为配置类相当于xml
public class SwaggerConfig {
    @Bean
    //swagger中所有的功能都封装到了Docket类中
    public Docket docket(){
        Docket docket=new Docket(DocumentationType.SWAGGER_2)
                .groupName("接口文档")
                设置api文档信息
                .apiInfo(apiInfo())
                //为那个包下的类生成接口文档
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.guan.demo.system.controller"))
                .build();

        return docket;
    }
    //定义自己接口文档信息

    public ApiInfo apiInfo(){
        //description 描述   description 团队地址  license  企业的名字
        Contact DEFAULT_CONTACT = new Contact("哈哈哈", "http://www.bidu.com", "[email protected]");
        ApiInfo apiInfo=new ApiInfo("亿个小项目", "针对小孩子的", "1.0", "http://www.jd.com", DEFAULT_CONTACT, "牛家企业", "http://www.taobao.com", new ArrayList<VendorExtension>());
        return apiInfo;
    }
}

 4.7.创建同一返回的数据信息加swagger的注解

@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("返回同一的信息")
public class CommonResult {
    @ApiModelProperty("状态码 2000成功,5000失败")
    private int code;
    @ApiModelProperty("信息")
    private String msg;
    @ApiModelProperty("数据")
    private Object data;
}

4.7.创建登录参数的实体类加swagger注解

@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("登录的参数的实体类")
public class VoLogin {
    //这里面的参数必须和你前端请求的参数相同
    @ApiModelProperty("账号")
    private String name;
    @ApiModelProperty("密码")
    private String password;
}

4.8.后端登录接口加swagger的注解

@RestController
@RequestMapping("/system")
@Api(tags = "登录的类")
public class LoginController {
    @Autowired
    private IUserService userService;
    @Autowired
    private RedisTemplate redisTemplate;
    @PostMapping("/user")
    @ApiOperation(value = "登录的接口")
    /*
    *   origins: 允许哪些域可以跨域访问我这个接口
        allowedHeaders:允许哪些请求头信息跨域
        methods: 允许哪些请求方式跨域
    * */
   // @CrossOrigin  //默认不写是*--解决跨域的问题
    public CommonResult login(@RequestBody VoLogin voLogin){
        QueryWrapper<User> wrapper=new QueryWrapper<>();
        wrapper.eq("username",voLogin.getName());
        wrapper.eq("password",voLogin.getPassword());
        wrapper.eq("is_deleted",0);
        User one = userService.getOne(wrapper);
        if (one!= null) {
            
            return new CommonResult(2000,"登录成功",null);
        }else {
            return new CommonResult(5000,"登录失败",null);
        }

    }

}

5.登录的bug

5.1.修改登录接口

5.1.1.如果在数据库查询到了用户信息,然后将用户信息保存

@RestController
@RequestMapping("/system")
@Api(tags = "登录的类")
public class LoginController {
    @Autowired
    private IUserService userService;
    @Autowired
    private RedisTemplate redisTemplate;
    @PostMapping("/user")
    @ApiOperation(value = "登录的接口")
    /*
    *   origins: 允许哪些域可以跨域访问我这个接口
        allowedHeaders:允许哪些请求头信息跨域
        methods: 允许哪些请求方式跨域
    * */
   // @CrossOrigin  //默认不写是*--解决跨域的问题
    public CommonResult login(@RequestBody VoLogin voLogin){
        QueryWrapper<User> wrapper=new QueryWrapper<>();
        wrapper.eq("username",voLogin.getName());
        wrapper.eq("password",voLogin.getPassword());
        wrapper.eq("is_deleted",0);
        User one = userService.getOne(wrapper);
        if (one!= null) {
            //登录成功之后随机生成唯一的字符串
            String token = UUID.randomUUID().toString();
          //  System.out.println(token+"---------");
            //把token作为key,user的信息是value
            ValueOperations forValue = redisTemplate.opsForValue();
            forValue.set(token,one,24,TimeUnit.HOURS);
            //把token返回给前端,作为主体
            return new CommonResult(2000,"登录成功",token);
        }else {
            return new CommonResult(5000,"登录失败",null);
        }

    }

5.1.2.将保存的信息给前端

 methods:{
            login(){
                this.$refs['ruleForm'].validate((valid) => {
                    if(valid) {
                        //  url:后端登录接口的路径
                        this.axios.post("http://localhost:8888/system/user", this.ruleForm).then(result => {
                            if(result.data.code===2000){
                                //登录成功,获取token
                                var token = result.data.data;
                               // console.log(result.data.data)
                                //把token保存
                                sessionStorage.setItem("token",token);
                                //登录成功之后跳转到主页面--路由跳转
                                this.$router.push("/home")
                            }

                        });
                        
                    }

由于每次前端都往后端请求都得要人为添加参数token. 我们可以使用axios得请求拦截器。

5.1.3.axios得请求拦截器、

  //使用axios的请求拦截器,--在请求头上添加token
  //interceptors--拦截器  request--请求方法 use--用
axios.interceptors.request.use(config=>{
  //获取token
  var token = sessionStorage.getItem("token");
  //判断toke里面是不是有值
  if (token){
    //请求头中会携带token
    config.headers.token=token;
  }
  return config;

})

6.前置路由守卫

6.1.什么是前置路由守卫

路由跳转之前, 会触发的一个函数 叫前置路由守卫

6.2.前置路由守卫的语法

router.beforeEach((to, from, next) => {})

to : 到哪里去

from : 从哪里来

next : 放行函数 next():放行 , next(false):不放行

6.3.使用前置路由守卫 --在main.js


router.beforeEach((to, from, next) =>{
  var path = to.path;
  //判断是不是登录页面
  if (path==="/login"){
    //放行
      return next();
  }
  //判断其他页面是否登录,登录放行
  var token = sessionStorage.getItem("token");
  if(token){
    return next();
  }
  //没有登录的话,跳转到登录页面
  return next("/login");

} )

7.整合shiro

7.1.添加shiro依赖

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

7.2.shiro得配置类

这里面注入RedisTemplate的原因,请看跨域那一篇的笔记

package com.guan.demo.system.config;

import com.guan.demo.system.filter.LoginFilter;
import com.guan.demo.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.DelegatingFilterProxy;

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

/**
 * TODO
 *
 * @author lenovo
 * @version 1.0
 * @since 2022-08-05  20:05:38
 */
@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 hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //加密的次数
        hashedCredentialsMatcher.setHashIterations(1024);
        //应那种加密方式加密
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        return hashedCredentialsMatcher;
    }
    @Autowired
   private RedisTemplate redisTemplate;
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean filterFactoryBean(){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());

        //设置shiro过滤规则
        Map<String, String> map = new HashMap<>();
        //那些允许放行
        map.put("/system/login","anon");
        //拦截所有
        map.put("/**","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);


        //设置未登录过滤器
        Map<String, Filter> filters = new HashMap<>();
        filters.put("authc",new LoginFilter(redisTemplate));
        shiroFilterFactoryBean.setFilters(filters);

        return shiroFilterFactoryBean;
    }


    @Bean
    public FilterRegistrationBean<Filter> filterProxy(){
        FilterRegistrationBean<Filter> filterRegistrationBean=new FilterRegistrationBean<>();
        //设置过滤器
        filterRegistrationBean.setFilter(new DelegatingFilterProxy());
        //那个路径过滤
        filterRegistrationBean.setName("shiroFilter");
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }
}

7.3.增加一个realm类对象

package com.guan.demo.system.realm;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.guan.demo.system.entity.User;
import com.guan.demo.system.service.IUserService;
import com.guan.demo.system.vo.VoLogin;
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;

/**
 * TODO
 *
 * @author lenovo
 * @version 1.0
 * @since 2022-08-05  20:07:04
 */

public class MyRealm extends AuthorizingRealm {

    @Autowired
    private IUserService userService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //根据token拿到账号
        String username = (String) token.getPrincipal();
        //根据账号查询信息
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        wrapper.eq("is_deleted",0);
        User user = userService.getOne(wrapper);
        //如果user里面查询到了,有值不为空,从数据库中获取密码
        if(user!=null){
            //从数据库中获取的密码
            //第一个参数是唯一标识,第二个参我们获取到user的密码,第三个参数就是获取到当前的名字
            ByteSource bytes = ByteSource.Util.bytes(user.getSalt());
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),bytes,this.getName());
            return info;
        }
        return null;
    }
}

7.4.新增一个拦截器

//如果类没有交于spring容器来管理 那么该类中得属性也不能让spring帮你注入
public class LoginFilter extends FormAuthenticationFilter {


    private RedisTemplate redisTemplate;  //没有自动注入,原因该类没有交于spring管理LoginFilter必须交于spring容器来管理。
    //构造方法
    public LoginFilter(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    //当登录成功后执行得方法,如果该方法返回false,则执行onAccessDenied方法,true则不会执行onAccessDenied方法
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        System.out.println(redisTemplate);
        HttpServletRequest req = (HttpServletRequest) request;
        //1.请求方式是否为OPTIONS
        String method = req.getMethod();
        //如果请求方式不为空,并且请求方式是OPTIONS,则不执行onAccessDenied方法
        if(method!=null && method.equals("OPTIONS")){
            return true;
        }

        //2.判断请求头是否有token值
        String token = req.getHeader("token");
        if(token!=null && redisTemplate.hasKey(token)){
            return true;
        }
        return false;
    }
    //当没有登录时会经过该方法。如果想让他返回json数据那么必须重写方法
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {

        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        CommonResult commonResult = new CommonResult(4001, "未登录", null);
        ObjectMapper objectMapper=new ObjectMapper();
        String json = objectMapper.writeValueAsString(commonResult);
        writer.print(json); //响应给客户json数据
        writer.flush();
        writer.close();
        return false;
    }
}

7.5.修改controller代码

 //登录
    @PostMapping("/login")
    @ApiOperation(value = "登录的接口")
    /*
    *   origins: 允许哪些域可以跨域访问我这个接口
        allowedHeaders:允许哪些请求头信息跨域
        methods: 允许哪些请求方式跨域
    * */
   // @CrossOrigin  //默认不写是*--解决跨域的问题
    public CommonResult login(@RequestBody VoLogin voLogin){

        try {
            //获取subject对象
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(voLogin.getName(),voLogin.getPassword());
            //登录
            subject.login(usernamePasswordToken);
            //获取主体
            Object one = subject.getPrincipal();

            //登录成功之后随机生成唯一的字符串
            String token = UUID.randomUUID().toString();
          //  System.out.println(token+"---------");
            //把token作为key,user的信息是value
            ValueOperations forValue = redisTemplate.opsForValue();
            forValue.set(token,one,24,TimeUnit.HOURS);
            //把token返回给前端,作为主体
            return new CommonResult(2000,"登录成功",token);
        }catch (Exception e){
            return new CommonResult(5000,"登录失败",null);
        }

    }

8.主页得布局

8.1.前端

<template>
    <el-container>

        <el-header>
             <span id="logo" style="display: inline-block;width: 50%;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: -9px">
                            <el-dropdown-item command="personal">个人信息</el-dropdown-item>
                            <el-dropdown-item command="loginOut">退出登录</el-dropdown-item>
                        </el-dropdown-menu>
                 </el-dropdown>
            </span>

        </el-header>

        <!--左侧菜单-->
        <el-container height="600px">
           
    </el-container>
</template>

 样式

<style>
    body{
        margin: 0px;
        padding: 0px;
    }
    .el-menu{
        border:0px;
        background-color:#C0C4CC;

    }
    .el-header, .el-footer {
        background-color: #818181;
        color: #333;
        height: 60px;
        line-height: 60px;
    }

    .el-aside {
        background-color: #C0C4CC;
        color: #333;
        height: 600px;
        line-height: 600px;
        /*overflow: hidden;*/
    }
    .el-aside::-webkit-scrollbar {
        display: none;
    }

    .el-main {
        background-color: #E9EEF3;
        color: #333;
        text-align: center;
        height: 600px;
        line-height: 600px;
        overflow: hidden;
    }

    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;
    }

    .el-dropdown {
        vertical-align: top;
    }
    .el-dropdown + .el-dropdown {
        margin-left: 15px;
    }
    .el-icon-arrow-down {
        font-size: 12px;
    }
    /*设置标签页第一个不可删除*/
    .el-tabs__nav .el-tabs__item:nth-child(1) span{
        display: none;
    }

</style>

9.退出登录

9.1.前端

            //退出
            handleCommand(command){
                if (command==='loginOut'){
                    this.axios.post("/system/loginOut").then(result=>{
                        if (result.data.code===2000){
                            //删除缓存的信息
                            sessionStorage.getItem("token");
                            //跳转到登录页面
                            this.$router.push("/login")
                        }
                    })
                }
            }

9.2.后端

  @Autowired
    private RedisTemplate redisTemplate;
    //退出
    @PostMapping("/loginOut")
    public CommonResult loginOut(HttpServletRequest request){
        String token = request.getHeader("token");

            redisTemplate.delete(token);
            return new CommonResult(2000,"退出成功",null);
    }

10.查询左侧菜单

10.1.前端

 <!--左侧菜单-->
        <el-container height="600px">
            <el-aside width="200px">

                    <el-menu
                            default-active="2"
                            class="el-menu-vertical-demo"
                            background-color="#C0C4CC"
                            text-color="#000"
                            active-text-color="#ffd04b">
                        <el-submenu :index="menu.id+''" v-for="menu in leftMenus">
                            <template slot="title">
                                <i class="el-icon-location"></i>
                                <span>{
   
   {menu.name}}</span>
                            </template>
                            <el-menu-item :index="second.id+''" v-for="second in menu.children">
                                <i :class="el-icon-menu"></i>
                                <span slot="title">
                                    <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>

 10.2.请求路径

data(){
            return{
                leftMenus:[],
            }
        },
        created() {
            this.initLeftMenu();
        },
        methods:{
            initLeftMenu(){
                this.axios.post("/system/permission/leftMenu").then(result=>{
                    if(result.data.code===2000){
                        this.leftMenus=result.data.data;
                    }
                })
            },

10.3.controller层

@RestController
@RequestMapping("/system/permission")
public class PermissionController {
    @Autowired
    private IPermissionService permissionService;

    @PostMapping("leftMenu")
    public CommonResult leftMenu(HttpServletRequest request){
        //获取登录者的信息
        String token = request.getHeader("token");
        return permissionService.findPermissionByUserId(token);
    }
}

10.4.service层

public interface IPermissionService extends IService<Permission> {

    CommonResult findPermissionByUserId(String token);
}

 10.5.mapper层

public interface PermissionMapper extends BaseMapper<Permission> {

    List<Permission> selectByUserId(String id);
}

10.6.seriveimpl业务代码

   @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public CommonResult findPermissionByUserId(String token) {
        //根据token获取用户信息
        ValueOperations forValue = redisTemplate.opsForValue();
        User o = (User) forValue.get(token);
        String id = o.getId();
        //根据用户id查询该用户具有得权限。
        List<Permission> permissionList = permissionMapper.selectByUserId(id);
        //设置层级关系
        List<Permission> firstMenus = new ArrayList<>();
        for (Permission first : permissionList) {
            //找一级菜单
            if (first.getPid().equals("1")) {
                firstMenus.add(first);
            }
        }


        //为一级菜单设置二级菜单
        for (Permission first : firstMenus) {
            //根据一级菜单id 查询 该菜单得二级菜单。如果出现不确定有几级菜单 那么我们可以使用方法得递归调用
            first.setChildren(findChildren(permissionList, first.getId()));

        }

        return new CommonResult(2000,"查询成功",firstMenus);
    }

    //方法递归
    private List<Permission> findChildren(List<Permission> permissionList, String id) {
        List<Permission> children = new ArrayList<>();
        for (Permission p : permissionList) {
            if (p.getPid().equals(id)) {
                children.add(p);
            }
        }


        for (Permission child : children) {
            child.setChildren(findChildren(permissionList, child.getId()));
        }

        return children;

    }
}

10.7.连表

<select id="selectByUserId" resultType="com.guan.demo.system.entity.Permission">
    select distinct p.* from acl_user_role ur join acl_role_permission rp on ur.role_id=rp.role_id join acl_permission
    p on rp.permission_id=p.id where ur.user_id=#{userid} and type=1
</select>

猜你喜欢

转载自blog.csdn.net/ne_123456/article/details/126235787