Spring Security-动态权限控制(1)

发布时间:2024年01月18日

SpringSecurity过滤器链,图中绿色的是认证相关的,蓝色部分是异常相关的,而橙色部分是授权相关,今天我们就是要理清橙色部分授权相关的流程,以及实现动态授权。

整个动态授权的过程

accessDecision-flow.png

通过上面的授权流程分析,咱们大致清楚了SpringSecurity是怎么授权的,那么我们要实现动态授权应该怎么做?其实就是实现自定义上图中的两个类:一个是SecurityMetadataSource类用来获取当前请求所需要的权限;另一个是AccessDecisionManager类来实现授权决策

image-20201117151931194

FilterInvocationSecurityMetadataSource:通过此类,获取哪些角色可以访问该 url 。

AccessDecisionManager:通过此类,判断用户时候拥有上述中的角色。

img

因为要实现 动态 基于路径的权限管理, 因此 需要 数据库的支持

项目目录结构

1.建表

数据库 设计 共 5张表

?menu表
?DROP TABLE IF EXISTS `menu`;
?CREATE TABLE `menu` ?(
? ?`id` int(0) NOT NULL AUTO_INCREMENT,
? ?`pattern` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
? ?PRIMARY KEY (`id`) USING BTREE
?) 
??插入数据
?INSERT INTO `menu` VALUES (1, '/test/add');
?INSERT INTO `menu` VALUES (2, '/test/export');
?INSERT INTO `menu` VALUES (3, '/test/update');
?INSERT INTO `menu` VALUES (4, '/test/delete');
?INSERT INTO `menu` VALUES (5, '/test/query');
?INSERT INTO `menu` VALUES (6, '/test/toLogin');
role 角色表
?DROP TABLE IF EXISTS `role`;
?CREATE TABLE `role` ?(
? ?`id` int(0) NOT NULL AUTO_INCREMENT,
? ?`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
? ?`nameZh` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
? ?PRIMARY KEY (`id`) USING BTREE
?)
??
角色表数据
?INSERT INTO `role` VALUES (1, 'ROLE_admin', '系统管理员');
?INSERT INTO `role` VALUES (2, 'ROLE_user', '普通用户');
??
??
???role_menu ? 角色与菜单 表
?CREATE TABLE `role_menu` ?(
? ?`id` int(0) NOT NULL AUTO_INCREMENT,
? ?`mid` int(0) NULL DEFAULT NULL,
? ?`rid` int(0) NULL DEFAULT NULL,
? ?PRIMARY KEY (`id`) USING BTREE,
? ?INDEX `mid`(`mid`) USING BTREE,
? ?INDEX `rid`(`rid`) USING BTREE,
? ?CONSTRAINT `role_menu_ibfk_1` FOREIGN KEY (`mid`) REFERENCES `menu` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
? ?CONSTRAINT `role_menu_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
?)
??数据
INSERT INTO `role_menu` VALUES (1, 1, 1);
?INSERT INTO `role_menu` VALUES (2, 2, 1);
?INSERT INTO `role_menu` VALUES (3, 3, 1);
?INSERT INTO `role_menu` VALUES (4, 4, 1);
?INSERT INTO `role_menu` VALUES (5, 5, 1);
?INSERT INTO `role_menu` VALUES (6, 2, 2);
?INSERT INTO `role_menu` VALUES (7, 5, 2);
?user表  用户表
?DROP TABLE IF EXISTS `user`;
?CREATE TABLE `user` ?(
? ?`id` int(0) NOT NULL AUTO_INCREMENT,
? ?`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
? ?`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
? ?`enabled` bit(1) NULL DEFAULT NULL,
? ?`locked` bit(1) NULL DEFAULT NULL,
? ?PRIMARY KEY (`id`) USING BTREE
?) 
数据
INSERT INTO `user` VALUES (1, 'sale', '111', b'1', b'0');
?INSERT INTO `user` VALUES (2, 'admin', '111', b'1', b'0');

?user_role 用户-角色表

?DROP TABLE IF EXISTS `user_role`;
?CREATE TABLE `user_role` ?(
? ?`id` int(0) NOT NULL AUTO_INCREMENT,
? ?`uid` int(0) NULL DEFAULT NULL,
? ?`rid` int(0) NULL DEFAULT NULL,
? ?PRIMARY KEY (`id`) USING BTREE,
? ?INDEX `uid`(`uid`) USING BTREE,
? ?INDEX `rid`(`rid`) USING BTREE,
? ?CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
? ?CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
?) 
??
??
?数据
?INSERT INTO `user_role` VALUES (1, 1, 2);
?INSERT INTO `user_role` VALUES (3, 2, 1);

2.建立boot项目

本次采用 父子工程方式: 其中 springboot 版本为 2.7.3 ,

父工程的pom.xml

?
<dependencyManagement>
? ? ?<dependencies>
? ? ? ? ?<dependency>
? ? ? ? ? ? ?<groupId>org.springframework.boot</groupId>
? ? ? ? ? ? ?<artifactId>spring-boot-dependencies</artifactId>
? ? ? ? ? ? ?<version>2.7.3</version>
? ? ? ? ? ? ?<scope>import</scope>
? ? ? ? ? ? ?<type>pom</type>
? ? ? ? ?</dependency>
??
? ? ? ? ?<!--引入mysql -->
? ? ? ? ?<dependency>
? ? ? ? ? ? ?<groupId>mysql</groupId>
? ? ? ? ? ? ?<artifactId>mysql-connector-java</artifactId>
? ? ? ? ? ? ?<version>8.0.30</version>
? ? ? ? ? ? ?<scope>import</scope>
? ? ? ? ? ? ?<type>pom</type>
? ? ? ? ?</dependency>
? ? ? ? ?<!--引入 lombok -->
? ? ? ? ?<dependency>
? ? ? ? ? ? ?<groupId>org.projectlombok</groupId>
? ? ? ? ? ? ?<artifactId>lombok</artifactId>
? ? ? ? ? ? ?<version>1.18.24</version>
? ? ? ? ? ? ?<scope>import</scope>
? ? ? ? ? ? ?<type>pom</type>
? ? ? ? ?</dependency>
? ? ?</dependencies>
?</dependencyManagement>

子工程

?<dependencies>
? ? ?<dependency>
? ? ? ? ?<groupId>org.springframework.boot</groupId>
? ? ? ? ?<artifactId>spring-boot-starter-web</artifactId>
? ? ?</dependency>
? ? ?<dependency>
? ? ? ? ?<groupId>org.springframework.boot</groupId>
? ? ? ? ?<artifactId>spring-boot-starter-security</artifactId>
? ? ?</dependency>
??
? ? ?<dependency>
? ? ? ? ?<groupId>com.baomidou</groupId>
? ? ? ? ?<artifactId>mybatis-plus-boot-starter</artifactId>
? ? ? ? ?<version>3.5.2</version>
? ? ?</dependency>
? ? ?<dependency>
? ? ? ? ?<groupId>mysql</groupId>
? ? ? ? ?<artifactId>mysql-connector-java</artifactId>
? ? ?</dependency>
??
? ? ?<dependency>
? ? ? ? ?<groupId>org.projectlombok</groupId>
? ? ? ? ?<artifactId>lombok</artifactId>
? ? ?</dependency>
??
? ? ?<dependency>
? ? ? ? ?<groupId>org.springframework.boot</groupId>
? ? ? ? ?<artifactId>spring-boot-starter-thymeleaf</artifactId>
? ? ? ? ?<version>2.7.3</version>
? ? ?</dependency>
??
??
? ? ?<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
? ? ?<dependency>
? ? ? ? ?<groupId>org.thymeleaf.extras</groupId>
? ? ? ? ?<artifactId>thymeleaf-extras-springsecurity5</artifactId>
? ? ? ? ?<version>3.0.4.RELEASE</version>
? ? ?</dependency>
??
?</dependencies>

3.创建实体层

需要创建 Menu与 User ,Role 三个实体类

?@Data
?@NoArgsConstructor
?@AllArgsConstructor
?public class Menu {
? ? ?private int id;
? ? ?private String pattern;
? ? ?private List<Role> roles; ?// 菜单对应的角色
??
??
?}

?@Data
?@NoArgsConstructor
?@AllArgsConstructor
?public class Role {
??
? ? ?private Integer id;
? ? ?private String name;
? ? ?private String nameZh;
? ? ?
?}

User 需要 实现UserDetails 接口

?
package com.entity;
??
?import lombok.AllArgsConstructor;
?import lombok.Data;
?import lombok.NoArgsConstructor;
?import org.springframework.security.core.GrantedAuthority;
?import org.springframework.security.core.authority.SimpleGrantedAuthority;
?import org.springframework.security.core.userdetails.UserDetails;
??
?import java.util.ArrayList;
?import java.util.Collection;
?import java.util.List;
??
?@Data
?@NoArgsConstructor
?@AllArgsConstructor
?public class User implements UserDetails {
??
? ? ?private Integer id;
? ? ?private String username;
? ? ?private String password;
? ? ?private boolean enabled;
? ? ?private boolean locked;
? ? ?private List<Role>roles; ? //用户对应的角色
??
??
? ? ?// 返回当前用户的权限列表
? ? ?@Override
? ? ?public Collection<? extends GrantedAuthority> getAuthorities() {
? ? ? ? List<SimpleGrantedAuthority> authorities=new ArrayList<>();
? ? ? ? for(Role role:roles){
? ? ? ? ? ? authorities.add(new SimpleGrantedAuthority(role.getName()));
? ? ? ? }
? ? ? ? return authorities;
? ?  }
??
? ? ?@Override
? ? ?public String getPassword() {
? ? ? ? ?return this.password;
? ?  }
??
? ? ?@Override
? ? ?public String getUsername() {
? ? ? ? ?return this.username;
? ?  }
? ? ?//账号是否未过期,直接返回true 表示账户未过期,也可以在数据库中添加该字段
? ? ?@Override
? ? ?public boolean isAccountNonExpired() {
? ? ? ? ?return true;
? ?  }
? ? ?//账号是否被锁, 这里和数据库中的locked字段刚好相反,所有取反
? ? ?@Override
? ? ?public boolean isAccountNonLocked() {
? ? ? ? ?return !this.locked;
? ?  }
? ? ?//密码是否为过期,数据库中无该字段,直接返回true
? ? ?@Override
? ? ?public boolean isCredentialsNonExpired() {
? ? ? ? ?return true;
? ?  }
? ? ?//账户是否可用,从数据库中获取该字段
? ? ?@Override
? ? ?public boolean isEnabled() {
? ? ? ? ?return this.enabled;
? ?  }
?}

4.增加yaml配置

数据源及mybatis-plus配置

?spring:
?  datasource:
? ?  driver-class-name: com.mysql.cj.jdbc.Driver
? ?  url: jdbc:mysql://localhost:3306/security?serverTimezone=GMT%2B8
? ?  username: 自己的用户名
? ?  password: 自己的密码
?mybatis-plus:
?  mapper-locations: classpath:mapper/*Mapper.xml
?  type-aliases-package: com.entity
?  configuration:
? ?  log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

5.创建mapper层

创建 MenuMapper

?
package com.mapper;
??
?import com.baomidou.mybatisplus.core.mapper.BaseMapper;
?import com.entity.Menu;
?import java.util.List;
??
??
?public interface MenuMapper extends BaseMapper<Menu> {
? ? ? ? ?//查询所有的菜单项(权限)
? ? ? ? ?List<Menu> getAllMenus();
??
?}

MenuMapper.xml

?<?xml version="1.0" encoding="UTF-8" ?>
?<!DOCTYPE mapper
? ? ? ? ?PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
? ? ? ? ?"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
?<mapper namespace="com.mapper.MenuMapper">
??
? ? ?<resultMap id="tMap" type="menu">
? ? ? ? ?<id column="id" property="id"/>
? ? ? ? ?<result column="pattern" property="pattern"/>
? ? ? ? ?<collection property="roles" ofType="role">
? ? ? ? ? ? ?<id column="rid" property="id"/>
? ? ? ? ? ? ?<result column="rname" property="name"/>
? ? ? ? ? ? ?<result column="rnameZh" property="nameZh"/>
? ? ? ? ?</collection>
? ? ?</resultMap>
??
? ? ?<select id="getAllMenus" resultMap="tMap">
? ? ? ?  select m.*,r.id as rid,r.name as rname,r.nameZh as rnameZh
? ? ? ?  from menu m 
? ? ? ?  left join role_menu mr on m.id=mr.mid 
? ? ? ?  left join role r on mr.rid=r.id
? ? ?</select>
??
?</mapper>

UserMapper.java

?
package com.mapper;
??
?import com.baomidou.mybatisplus.core.mapper.BaseMapper;
?import com.entity.Role;
?import com.entity.User;
?import java.util.List;
??
??
?public interface UserMapper extends BaseMapper<User> {
??
? ? ? //根据用户名 查询 用户信息
? ? ? User loadUserByUsername(String username);
??
? ? ? //获取当前用户的角色
? ? ? List<Role> getUserRolesById(int uid);
??
?}

UserMapper.xml

?<?xml version="1.0" encoding="UTF-8" ?>
?<!DOCTYPE mapper
? ? ? ? ?PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
? ? ? ? ?"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
?<mapper namespace="com.mapper.UserMapper">
??
??
? ? ?<select id="loadUserByUsername" resultType="user">
? ? ? ? select * from user where username=#{username}
? ? ?</select>
??
? ? ?<select id="getUserRolesById" parameterType="int" resultType="role">
? ? ? ?  select * from role where id in(select rid from user_role where uid=#{id})
? ? ?</select>
??
?</mapper>

?

?

文章来源:https://blog.csdn.net/ly121862/article/details/135665008
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。