1. 程式人生 > >SpringBoot通過自己的配置檔案或者從資料庫spring security動態配置url許可權

SpringBoot通過自己的配置檔案或者從資料庫spring security動態配置url許可權

我使用springboot的時候想做自己的配置檔案的,用不了xml就重寫了過濾器

首先需要了解spring security內建的各種filter:

Alias Filter Class Namespace Element or Attribute
CHANNEL_FILTER ChannelProcessingFilter http/intercept-url@requires-channel
SECURITY_CONTEXT_FILTER SecurityContextPersistenceFilter http
CONCURRENT_SESSION_FILTER ConcurrentSessionFilter session-management/concurrency-control
HEADERS_FILTER HeaderWriterFilter http/headers
CSRF_FILTER CsrfFilter http/csrf
LOGOUT_FILTER LogoutFilter http/logout
X509_FILTER X509AuthenticationFilter http/x509
PRE_AUTH_FILTER AbstractPreAuthenticatedProcessingFilter Subclasses N/A
CAS_FILTER CasAuthenticationFilter N/A
FORM_LOGIN_FILTER UsernamePasswordAuthenticationFilter http/form-login
BASIC_AUTH_FILTER BasicAuthenticationFilter http/http-basic
SERVLET_API_SUPPORT_FILTER SecurityContextHolderAwareRequestFilter http/@servlet-api-provision
JAAS_API_SUPPORT_FILTER JaasApiIntegrationFilter http/@jaas-api-provision
REMEMBER_ME_FILTER RememberMeAuthenticationFilter http/remember-me
ANONYMOUS_FILTER AnonymousAuthenticationFilter http/anonymous
SESSION_MANAGEMENT_FILTER SessionManagementFilter session-management
EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter http
FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor http
SWITCH_USER_FILTER SwitchUserFilter N/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感謝這位樓主,