SpringBoot+SpringSecurity+Vue实现动态权限(一)

前言

之前都是使用若依框架来实现的动态权限和菜单功能,但是一直想尝试自己来实现动态权限。所以这两天准备整合一下自己的所学知识,依据RBAC权限模型,使用SpringBoot+SpringSecurity+Vue来自己实现一下动态权限。

数据库结构

在数据库设计方面是根据RBAC权限模型来设计的,分别有user(用户)表,role(角色)表,permission(权限)表,user_role(用户角色)表和role_permission(角色权限)表。大致字段如下:
在这里插入图片描述

技术选型

为了方便开发,选择了市面上主流的框架SpringBoot+Vue,安全框架使用SpringSecurity,ORM框架使用MyBatis-Plus。缓存使用Redis。

构建项目

构建一个普通的SpringBoot项目,在pom文件导入以下依赖:

<?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.6.8</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lyx.autoperm</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>autoperm</name>
    <description>配合SpringSecurity动态权限</description>
    <properties>
        <java.version>1.8</java.version>
        <mysql.version>8.0.28</mysql.version>
        <druid.version>1.2.6</druid.version>
        <mybatisPlus.version>3.5.1</mybatisPlus.version>
        <fastjson.version>1.2.78</fastjson.version>
        <jwt.version>0.9.1</jwt.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!-- DRUID -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatisPlus.version}</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatisPlus.version}</version>
        </dependency>

        <!-- 模板引擎 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
            <scope>compile</scope>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- Token生成与解析-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jwt.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

代码生成

使用mybatisplus生成实体类,controller和service的java文件。

@SpringBootTest
class AutopermApplicationTests {

    @Autowired
    private DruidDataSource dataSource;

    @Test
    void contextLoads() {
        FastAutoGenerator.create(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword())
                .globalConfig(builder -> {
                    builder.author("liyongxuan") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("D://"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.lyx") // 设置父包名
                            .moduleName("autoperm") // 设置父包模块名
                            .entity("entity")
                            .controller("controller")
                            .service("service")
                            .serviceImpl("impl")
                            .pathInfo(Collections.singletonMap(OutputFile.mapper, "E:\\WorkSpace\\autoperm\\src\\main\\java\\com\\lyx\\autoperm\\mapper"))
                            .pathInfo(Collections.singletonMap(OutputFile.entity, "E:\\WorkSpace\\autoperm\\src\\main\\java\\com\\lyx\\autoperm\\entity"))
                            .pathInfo(Collections.singletonMap(OutputFile.service, "E:\\WorkSpace\\autoperm\\src\\main\\java\\com\\lyx\\autoperm\\service"))
                            .pathInfo(Collections.singletonMap(OutputFile.serviceImpl, "E:\\WorkSpace\\autoperm\\src\\main\\java\\com\\lyx\\autoperm\\service\\impl"))
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "E:\\WorkSpace\\autoperm\\src\\main\\resources\\mybatis")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude("L_ROLE") // 设置需要生成的表名
                            .addInclude("L_USER")
                            .addInclude("L_PERMISSON")
                            .addInclude("L_USER_ROLE")
                            .addInclude("L_ROLE_PERMISSON")
                            .addTablePrefix("L_"); // 设置过滤表前缀
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }

}

生成实体类后将User类继承UserDetails,同时继承其方法。我们这里只是用enabled来判断状态,所以isAccountNonExpired、isAccountNonLocked、isCredentialsNonExpired三个方法直接返回true.

@TableName("L_USER")
public class User implements Serializable, UserDetails {

    private static final long serialVersionUID = 1L;

     /**
       * 用户编号
       */
    private String id;

     /**
       * 用户名
       */
    private String username;

     /**
       * 密码
       */
    private String password;

     /**
       * 真实姓名
       */
    private String name;

     /**
       * 年龄
       */
    private String age;

     /**
       * 性别
       */
    private String sex;

     /**
       * 手机号
       */
    private String phone;

     /**
       * 家庭住址
       */
    private String address;

     /**
       * 启用状态
       */
    private Integer enable;

     /**
       * 创建时间
       */
    private String createTime;

    @TableField(exist = false)
    private Set<String> permissions;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

     /**
       * 默认不适用该状态
       */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * 默认不适用该状态
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 默认不适用该状态
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enable==1?true:false;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    public void setPermissions(Set<String> permissions) {
        this.permissions = permissions;
    }

    public Set<String> getPermissions() {
        return permissions;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
    public Integer getEnable() {
        return enable;
    }

    public void setEnable(Integer enable) {
        this.enable = enable;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }
    
}

在UserServiceImpl中实现UserDetailsService,SpringSecurity就是调用UserDetailsServiceloadUserByUsername来查询用户信息以及权限列表的。

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
        // 校验用户名密码是否为空
        if(StringUtils.isEmpty(username)) throw new UserException(UserCodeEnum.USERNAME_IS_EMPTY);
        // 查询用户
        User userFromDB = userMapper.selectOne(new QueryWrapper<User>().eq("username", username));
        // 判断用户是否为空或账号不存在
        if(null == userFromDB){
            throw new UsernameNotFoundException("用户不存在");
        }
        // 查询登录用户的权限列表
        Set<String> permissions = permissionMapper.queryPermissions(userFromDB.getId());
        if(!CollectionUtils.isEmpty(permissions)){
            userFromDB.setPermissions(permissions);
        }

        return userFromDB;
    }

源码地址

https://github.com/Lyx0912/autopermission

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
SpringBoot+SpringSecurity+Vue实现动态路由的过程如下: 1. 在后端(SpringBoot)中,首先需要定义一个权限表,用于存储所有的权限信息,包括权限名称、权限标识等。 2. 在前端(Vue)中,需要定义一个路由表,用于存储所有的路由信息,包括路由路径、组件名称等。 3. 后端需要提供一个接口,用于获取当前用户的权限列表。该接口会根据用户的角色查询对应的权限,并返回给前端。 4. 前端在登录成功后,会调用后端接口获取当前用户的权限列表,并将权限列表存储到本地(如localStorage或vuex)中。 5. 前端在路由跳转时,会根据当前用户的权限列表动态生成路由。可以通过遍历权限列表,根据权限标识匹配路由表中的路由信息,将匹配到的路由添加到路由表中。 6. 前端在生成路由后,需要使用Vue Router的addRoutes方法将动态生成的路由添加到路由表中。 7. 前端在路由跳转时,会根据用户的权限判断是否有权限访问该路由。可以通过导航守卫的beforeEach方法,在路由跳转前进行权限判断。 8. 后端可以使用Spring Security的注解对接口进行权限控制。可以通过在接口上添加注解,指定需要的权限才能访问该接口。 9. 后端在接口调用时,可以通过从redis中获取当前用户的权限列表,并进行权限判断。 10. 前端和后端通过接口交互,实现动态路由的权限控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值