spring security动态配置权限

一个资源需要什么权限才能访问,资源 和 权限的表,,动态配置资源,权限
在这里插入图片描述
在这里插入图片描述
涉及到的类:

授权的接口:
AccessDecisionManager : 是一个决策器,决定此次访问是否被允许
AccessDecisionVoter: 是一个投票器,会检查用户是否具备应有的角色,进而投出,赞成,反对,或者弃权票
AccessDecisionManager会挨个遍历 AccessDecisionVoter ,进而决定是否允许用户访问,关系类似于AuthenticationProvider 和 ProviderManager 的关系

ConfigAttribute对象 : 用户请求一个资源,,所需要的角色就会被封装成一个ConfigAttribute对象
,这个ConfigAttribute中只有一个getAttribute()方法,返回String字符串,返回ROLE_XXX

AccessDecisionVetor所做的事情,就是比较 用户所具备的角色 和 请求某个资源所需的ConfigAttribute 之间的关系

FilterInvocationSecurityMetadataSource

ObjectPostProcessor:
将Filter注入到spring容器中在这里插入图片描述

FilterSecurityInterceptor : 作为 Spring Security Filter Chain 的最后一个 Filter,承担着非常重要的作用。如获取当前 request 对应的权限配置,调用访问控制器进行鉴权操作等,都是核心功能。

在这里插入图片描述

在这里插入图片描述

感觉就是,,某一个资源,,他需要的权限信息就是ConfigAttribute,,然后被AbstractSecurityInterceptor拦截下来,拦截器去调用AccessDecisionManager 来决定当前用户是否具备访问这个资源的权限
在这里插入图片描述

FilterInvocation :大概意思是:通过Spring Security 封装,可以安全的拿到HttpServletRequest 和 HttpServletResponse对象

代码
  1. 配置ConfigAttribute
    因为是基于web的拦截规则
    在这里插入图片描述

创建一个提供 ConfigAttribute 的 SecurityMetadataSource,这里使用 FilterInvocationSecurityMetadataSource
在这里插入图片描述

package com.cj.springsecurity.config;

import com.cj.springsecurity.model.Menu;
import com.cj.springsecurity.model.Role;
import com.cj.springsecurity.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * FilterInvocationSecurityMetadataSource : 通过当前的请求地址,获取该地址需要的用户角色
 *
 */
@Component
public class MyFilter  implements FilterInvocationSecurityMetadataSource {
    AntPathMatcher pathMatcher = new AntPathMatcher();

    @Autowired
    MenuService menuService;

    /**
     *     每次请求都会调用这个方法,,先根据 url地址,算出来角色,,放入ConfigAttribute中
     * @param o the object being secured   可以转换成  FilterInvocation
     *
     *
     *   FilterInvocation:   通过Spring Security 封装,可以安全的拿到HttpServletRequest 和 HttpServletResponse对象
     *
     *   ConfigAttribute: 请求一个资源,所需要的角色都会被封装成一个 ConfigAttribute 对象
     *             # getAttribute() : 返回一个 String字符串就是角色的名称 ROLE_XXX
     *
     * @return 返回这个 访问这个资源需要的 ConfigAttribute
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {

        FilterInvocation filterInvocation = (FilterInvocation) o;
        // 获取 url请求
        String requestUrl = filterInvocation.getRequestUrl();

        List<Menu> allMenus = menuService.getAllMenus();

        for (Menu menu : allMenus) {
            if (pathMatcher.match(menu.getPattern(),requestUrl)){
                List<Role> roleList = menu.getRoleList();
                List<String> roleNameList = roleList.stream().map(Role::getName).collect(Collectors.toList());
                return SecurityConfig.createList(roleNameList.toArray(new String[roleNameList.size()]));
            }
        }

        return SecurityConfig.createList("ROLE_login");

    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

  1. 配置决策器 AccessDecisionManager

在这里插入图片描述

package com.cj.springsecurity.config;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;
@Component
public class MyAccessDecisionManager  implements AccessDecisionManager {
    /**
     * 比较用户所具备的角色   和 ConfigAttribute 的关系
     * @configAttributes : FilterInvocationSecurityMetadataSource返回的 ConfigAttribute
     * 抛异常,终止,就是不通过
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        boolean isFlag = false;
        for (ConfigAttribute configAttribute : collection) {
            if ("ROLE_login".equals(configAttribute.getAttribute())){
                // 默认 ROLE_login 登录就可以访问
                if (authentication instanceof AnonymousAuthenticationToken){
                    // 匿名用户
                    throw new AccessDeniedException("非法请求");
                }else{
                    // 已经登录
                    break;
                }
            }
            //
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(configAttribute.getAttribute())){
                    // 具备权限    : 判断策略:具备其中一个
                    isFlag = true;
                    break;
                }
            }
        }

        if (!isFlag){
            throw new AccessDeniedException("没有权限");
        }
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

  1. 在配置文件中添加拦截器,将权限决策器和资源所需要的ConfigAttribute 配置到配置文件中
    在这里插入图片描述
package com.cj.springsecurity.config;

import com.cj.springsecurity.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfig03  extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;

    @Autowired
    MyFilter myFilter;

    @Autowired
    MyAccessDecisionManager myAccessDecisionManager;

    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * ObjectPostProcessor:
         *  实现类 :AutowireBeanFactoryObjectPostProcessor
         *         通过postProcess方法手把Bean注入到spring容器进行管理。
         *
         * FilterSecurityInterceptor :
         *          作为 Spring Security Filter Chain 的最后一个 Filter,承担着非常重要的作用。
         *          如获取当前 request 对应的权限配置,调用访问控制器进行鉴权操作等,都是核心功能。
         */
        http.authorizeRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {

            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                // 将  AccessDecisionManager ,FilterInvocationSecurityMetadataSource 放入Filter中
                object.setAccessDecisionManager(myAccessDecisionManager);
                object.setSecurityMetadataSource(myFilter);
                return object;
            }
        }
        ).and()
                .formLogin()
                .permitAll()
                .and()
                .csrf().disable();
    }
}

-- MySQL dump 10.13  Distrib 5.7.9, for Win64 (x86_64)
--
-- Host: 127.0.0.1    Database: security
-- ------------------------------------------------------
-- Server version	5.7.9

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `menu`
--

DROP TABLE IF EXISTS `menu`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pattern` varchar(255) NOT NULL COMMENT '规则',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `menu`
--

LOCK TABLES `menu` WRITE;
/*!40000 ALTER TABLE `menu` DISABLE KEYS */;
INSERT INTO `menu` VALUES (1,'/dba/**'),(2,'/admin/**'),(3,'/user/**');
/*!40000 ALTER TABLE `menu` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `menu_role`
--

DROP TABLE IF EXISTS `menu_role`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `menu_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `mid` int(11) NOT NULL COMMENT 'menu id',
  `rid` int(11) NOT NULL COMMENT 'role id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1 COMMENT='menu role  多对多';
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `menu_role`
--

LOCK TABLES `menu_role` WRITE;
/*!40000 ALTER TABLE `menu_role` DISABLE KEYS */;
INSERT INTO `menu_role` VALUES (1,1,1),(2,2,2),(3,3,3);
/*!40000 ALTER TABLE `menu_role` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `role`
--

DROP TABLE IF EXISTS `role`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  `nameZh` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `role`
--

LOCK TABLES `role` WRITE;
/*!40000 ALTER TABLE `role` DISABLE KEYS */;
INSERT INTO `role` VALUES (1,'ROLE_dba','数据库管理员'),(2,'ROLE_admin','系统管理员'),(3,'ROLE_user','用户');
/*!40000 ALTER TABLE `role` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `user`
--

DROP TABLE IF EXISTS `user`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `enabled` tinyint(1) DEFAULT NULL,
  `accountNonExpired` tinyint(1) DEFAULT NULL,
  `accountNonLocked` tinyint(1) DEFAULT NULL,
  `credentialsNonExpired` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `user`
--

LOCK TABLES `user` WRITE;
/*!40000 ALTER TABLE `user` DISABLE KEYS */;
INSERT INTO `user` VALUES (1,'root','$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq',1,1,1,1),(2,'admin','$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq',1,1,1,1),(3,'cc','$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq',1,1,1,1);
/*!40000 ALTER TABLE `user` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `user_role`
--

DROP TABLE IF EXISTS `user_role`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) DEFAULT NULL,
  `rid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `uid` (`uid`),
  KEY `rid` (`rid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `user_role`
--

LOCK TABLES `user_role` WRITE;
/*!40000 ALTER TABLE `user_role` DISABLE KEYS */;
INSERT INTO `user_role` VALUES (1,1,1),(2,1,2),(3,2,2),(4,3,3);
/*!40000 ALTER TABLE `user_role` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2023-03-02 15:24:14

github:https://github.com/water-kid/spring-security/tree/master/springsecurity

引用:
https://blog.csdn.net/liuminglei1987/article/details/107662200
https://blog.csdn.net/andy_zhang2007/article/details/91563954
https://andyboke.blog.csdn.net/article/details/93377823
https://blog.csdn.net/liuyanglglg/article/details/104761761
https://blog.csdn.net/king101125s/article/details/104198958

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值