對於使用spring security來講,存在一種需求,就是動態去配置url的權限,即在運行時去配置url對應的訪問角色。這裏簡單介紹一下。html
首先須要瞭解spring security內置的各類filter:java
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來設置
這個filter有幾個要素,以下:web
能夠根據狀況本身去從新設置,這裏咱們重寫一下SecurityMetadataSource用來動態獲取url權限配置,還有AccessDecisionManager來進行權限判斷。spring
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(); for (GrantedAuthority ga : authentication.getAuthorities()) { if(ga.getAuthority().equals(needRole)){ //匹配到有對應角色,則容許經過 return; } } } //該url有配置權限,可是固然登陸用戶沒有匹配到對應權限,則禁止訪問 throw new AccessDeniedException("not allow"); } @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public boolean supports(Class<?> clazz) { return true; } }
這裏遍歷判斷該url所需的角色看用戶是否具有,有具有則返回,都不具有則拋出AccessDeniedException異常
public class MyFilterInvocationSecurityMetadataSource implements org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource { private final AntPathMatcher antPathMatcher = new AntPathMatcher(); private final Map<String,String> urlRoleMap = new HashMap<String,String>(){{ put("/open/**","ROLE_ANONYMOUS"); put("/health","ROLE_ANONYMOUS"); put("/restart","ROLE_ADMIN"); put("/demo","ROLE_USER"); }}; @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { FilterInvocation fi = (FilterInvocation) object; String url = fi.getRequestUrl(); // String httpMethod = fi.getRequest().getMethod(); for(Map.Entry<String,String> entry:urlRoleMap.entrySet()){ if(antPathMatcher.match(entry.getKey(),url)){ return SecurityConfig.createList(entry.getValue()); } } //沒有匹配到,默認是要登陸才能訪問 return SecurityConfig.createList("ROLE_USER"); // return null; } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { return FilterInvocation.class.isAssignableFrom(clazz); } }
這裏之內存的map來展現一下,實際應用能夠從分佈式配置中心或者數據庫中讀取,另外循環遍歷這個可能消耗性能,必要時得優化一下。
最後須要綜合配置一下,以下數據庫
@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; } @Bean public AccessDecisionManager myAccessDecisionManager() { return new MyAccessDecisionManager(); } }