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
這個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感謝這位樓主,