SpringBoot通过自己的配置文件或者从数据库spring security动态配置url权限

本文介绍如何在Spring Security中自定义权限配置,包括通过MyFilterInvocationSecurityMetadataSource类动态获取URL权限配置,并利用MyAccessDecisionManager进行权限判断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我使用springboot的时候想做自己的配置文件的,用不了xml就重写了过滤器

首先需要了解spring security内置的各种filter:

Alias Filter Class Namespace Element or Attribute
CHANNEL_FILTERChannelProcessingFilterhttp/intercept-url@requires-channel
SECURITY_CONTEXT_FILTERSecurityContextPersistenceFilterhttp
CONCURRENT_SESSION_FILTERConcurrentSessionFiltersession-management/concurrency-control
HEADERS_FILTERHeaderWriterFilterhttp/headers
CSRF_FILTERCsrfFilterhttp/csrf
LOGOUT_FILTERLogoutFilterhttp/logout
X509_FILTERX509AuthenticationFilterhttp/x509
PRE_AUTH_FILTERAbstractPreAuthenticatedProcessingFilter SubclassesN/A
CAS_FILTERCasAuthenticationFilterN/A
FORM_LOGIN_FILTERUsernamePasswordAuthenticationFilterhttp/form-login
BASIC_AUTH_FILTERBasicAuthenticationFilterhttp/http-basic
SERVLET_API_SUPPORT_FILTERSecurityContextHolderAwareRequestFilterhttp/@servlet-api-provision
JAAS_API_SUPPORT_FILTERJaasApiIntegrationFilterhttp/@jaas-api-provision
REMEMBER_ME_FILTERRememberMeAuthenticationFilterhttp/remember-me
ANONYMOUS_FILTERAnonymousAuthenticationFilterhttp/anonymous
SESSION_MANAGEMENT_FILTERSessionManagementFiltersession-management
EXCEPTION_TRANSLATION_FILTERExceptionTranslationFilterhttp
FILTER_SECURITY_INTERCEPTORFilterSecurityInterceptorhttp
SWITCH_USER_FILTERSwitchUserFilterN/A
这里我们要操作的是FilterSecurityInterceptor这个interceptor,使用withObjectPostProcessor来设置

FilterSecurityInterceptor

这个filter有几个要素,如下:

  • SecurityMetadataSource
  • AccessDecisionManager
  • AuthenticationManager

可以根据情况自己去重新设置,这里我们重写一下SecurityMetadataSource用来动态获取url权限配置,还有AccessDecisionManager来进行权限判断



MyFilterInvocationSecurityMetadataSource这个是用来配置URL的可以从配置文件中拿也可以从数据库中取

package com.ewe.user.security;


import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.util.AntPathMatcher;


import com.alibaba.fastjson.JSONObject;
import com.ewe.user.utils.FormatUtils;


public class MyFilterInvocationSecurityMetadataSource implements org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource {
   //配置文件的加载
private static String urlRoleMap;   
private static final Logger LOGGER = LoggerFactory.getLogger(MyUserDetails.class);
    static {   
        Properties prop = new Properties();   
        InputStream in = Object.class.getResourceAsStream("/spring-security.properties");   
        LOGGER.error("加载URL的配置文件");
        try {   
            prop.load(in);   
            urlRoleMap = prop.getProperty("urls").trim();   
            
        } catch (IOException e) {   
        LOGGER.error("spring-security.properties配置路径不存在{}",e.getMessage());
            e.printStackTrace();   
        }   
    } 
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
 


    @Override
    public  Collection<ConfigAttribute>  getAttributes(Object object) throws IllegalArgumentException {
        FilterInvocation fi = (FilterInvocation) object;
        StringBuffer roles = new StringBuffer("START");
        String url = fi.getRequestUrl();
//        String httpMethod = fi.getRequest().getMethod();
        JSONObject jsonObject=null;
try {
jsonObject = JSONObject.parseObject(urlRoleMap);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
    String value="";
    //遍历json
    for(String key: jsonObject.keySet()){
    if(antPathMatcher.match(key,url)){
    value=jsonObject.getString(key);
    //如果有,号的就说明是多个角色
        roles.append(","+value);
            }
    }
        if(!("START").equals(roles)){
        return SecurityConfig.createList(roles.toString());
        }else{
        LOGGER.error("没有匹配到URL");
            //没有匹配到
            // return SecurityConfig.createList("NULL");
            throw new AccessDeniedException("not allow");
        }
        
//        return null;
    }


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


    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}


以下是我的配置文件的信息spring-security.properties,已json对象键值对存储 url和role,不能转换成map,map不能有重复的key值的
所以我修改了参考链接的一些数据格式
我参考的是:https://segmentfault.com/a/1190000010672041
#url and roles
urls={\
  "/users/login":"NONE",\
  "/users/register":"NONE",\
  "/users/logout**":"ADMIN,CEO,MANAGER,ASSISTANT,EMPLOYEE",\
  "/users":"ADMIN,CEO,MANAGER,ASSISTANT,EMPLOYEE",\
   "/users/*":"ADMIN,CEO,MANAGER,ASSISTANT,EMPLOYEE",\
  "/users/*/password/":"ADMIN,CEO,MANAGER,ASSISTANT,EMPLOYEE",\
  "/users/*/freeze":"ADMIN,CEO,MANAGER,ASSISTANT,EMPLOYEE",\
  "/users/*/activate":"ADMIN,CEO,MANAGER,ASSISTANT,EMPLOYEE",\
  "/users/info**":"ADMIN,CEO,MANAGER,ASSISTANT,EMPLOYEE",\
  \
  "/roles*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/roles/*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/roles/*/add-user":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/roles/*/remove-user":"ADMIN,CEO,MANAGER,ASSISTANT",\
  \
  "/employees":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/employees/batch":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/employees/*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/employees*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/employees/expired-tips*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  \
  "/passports*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/passports/*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  \
  "/payment":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/payment/*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  \
  "/salary":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/salary*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/salary/*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  \
  "/jobs":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/jobs/**":"ADMIN,CEO,MANAGER,ASSISTANT",\
  \
  "/files":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/files/batch":"ADMIN,CEO,MANAGER,ASSISTANT",\
  \
  "/dictionary/*":"ADMIN,CEO,MANAGER,ASSISTANT",\
  "/dictionary**":"ADMIN,CEO,MANAGER,ASSISTANT"\
  }

MyAccessDecisionManager用来验证url的


这里遍历判断该url所需的角色看用户是否具备,有具备则返回,都不具备则抛出AccessDeniedException异常

package com.ewe.user.security;


import java.util.Collection;
import java.util.Iterator;


import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.CollectionUtils;


public class MyAccessDecisionManager implements org.springframework.security.access.AccessDecisionManager {


    @Override
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        //这段代码其实不需要,因为spring-security-core-4.1.4.RELEASE-sources.jar!/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java第215行判断提前返回了,不会进入decide方法
        if (CollectionUtils.isEmpty(configAttributes)) {
            throw new AccessDeniedException("not allow");
        }
        Iterator<ConfigAttribute> ite = configAttributes.iterator();
        while (ite.hasNext()) {
            ConfigAttribute ca = ite.next();
            String needRole = ((org.springframework.security.access.SecurityConfig) ca).getAttribute();
            String[] roles =needRole.split(",");
            for (GrantedAuthority ga : authentication.getAuthorities()) {


            for(String role1:roles){
                //登陆注册的通过
                if(("NONE").equals(role1)){
                        //匹配到有对应角色,则允许通过
                        return;
                    }
            if(ga.getAuthority().equals(role1)){
                        //匹配到有对应角色,则允许通过
                        return;
                    }
            }
            }
        }
        //该url有配置权限,但是当然登录用户没有匹配到对应权限,则禁止访问
        throw new AccessDeniedException("not allow");
    }
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }


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


最后装配我们的规则过滤器

@EnableWebSecurity
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    public <O extends FilterSecurityInterceptor> O postProcess(
                            O fsi) {
                        fsi.setSecurityMetadataSource(mySecurityMetadataSource());
                        fsi.setAccessDecisionManager(myAccessDecisionManager());
                        return fsi;
                    }
                });
    }

    @Bean
    public FilterInvocationSecurityMetadataSource mySecurityMetadataSource() {
        MyFilterInvocationSecurityMetadataSource securityMetadataSource = new MyFilterInvocationSecurityMetadataSource();
        return securityMetadataSource;
    }

参考下https://segmentfault.com/a/1190000010672041感谢这位楼主,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值