赞
踩
之前都是使用若依框架来实现的动态权限和菜单功能,但是一直想尝试自己来实现动态权限。所以这两天准备整合一下自己的所学知识,依据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就是调用UserDetailsService的loadUserByUsername来查询用户信息以及权限列表的。
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。