一个资源需要什么权限才能访问,资源 和 权限的表,,动态配置资源,权限
涉及到的类:
授权的接口:
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对象
代码
- 配置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;
}
}
- 配置决策器 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;
}
}
- 在配置文件中添加拦截器,将权限决策器和资源所需要的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