spring security 동적 설정 url 권한 의 2 가지 실현 방법

발단
표준 RABC,권한 은 동적 설정 을 지원 해 야 합 니 다.spring security 는 기본적으로 코드 에 권한 을 약정 합 니 다.실제 업무 장면 은 동적 설정 캐릭터 접근 권한 을 지원 해 야 합 니 다.즉,실행 할 때 url 에 대응 하 는 방문 캐릭터 를 설정 해 야 합 니 다.
spring security 를 바탕 으로 이 수 요 를 어떻게 실현 합 니까?
가장 쉬 운 방법 은 필터 하 나 를 사용자 정의 하여 권한 판단 을 완성 하 는 것 입 니 다.그러나 이것 은 spring security 프레임 워 크 에서 벗 어 났 습 니 다.어떻게 spring security 를 바탕 으로 우아 하 게 실현 할 수 있 습 니까?
spring 보안 권한 부여 회고
spring security 는 FilterChainProxy 를 통 해 웹 에 등 록 된 filter 입 니 다.FilterChainProxy 에는 한 번 에 여러 개의 필터 가 포함 되 어 있 습 니 다.먼저 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
가장 중요 한 것 은 FilterSecurity Interceptor 입 니 다.이 필 터 는 주요 인증 논 리 를 실 현 했 고 가장 핵심 적 인 코드 는 여기에 있 습 니 다.

protected InterceptorStatusToken beforeInvocation(Object object) { 
 //     URL    
 Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
 .getAttributes(object);

 
 Authentication authenticated = authenticateIfRequired();

 //   accessDecisionManager  
 try {
 this.accessDecisionManager.decide(authenticated, object, attributes);
 }
 catch (AccessDeniedException accessDeniedException) {
 publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
  accessDeniedException));

 throw accessDeniedException;
 }

 if (debug) {
 logger.debug("Authorization successful");
 }

 if (publishAuthorizationSuccess) {
 publishEvent(new AuthorizedEvent(object, attributes, authenticated));
 }

 // Attempt to run as a different user
 Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
 attributes);

 if (runAs == null) {
 if (debug) {
 logger.debug("RunAsManager did not change Authentication object");
 }

 // no further work post-invocation
 return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
  attributes, object);
 }
 else {
 if (debug) {
 logger.debug("Switching to RunAs Authentication: " + runAs);
 }

 SecurityContext origCtx = SecurityContextHolder.getContext();
 SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
 SecurityContextHolder.getContext().setAuthentication(runAs);

 // need to revert to token.Authenticated post-invocation
 return new InterceptorStatusToken(origCtx, true, attributes, object);
 }
 }
위 에서 보 듯 이 동태 감 권 을 실현 하려 면 두 가지 측면 에서 시작 할 수 있다.
  • 사용자 정의 Security MetadataSource,데이터베이스 에서 ConfigAttribute
  • 를 불 러 옵 니 다.
  • 또한 accessDecisionManager 를 사용자 정의 할 수 있 습 니 다.공식 적 인 Unanimous Based 는 사실 충분히 사용 할 수 있 습 니 다.또한 그 는 AccessDecisionVoter 를 바탕 으로 권한 인증 을 실현 하기 때문에 우 리 는 AccessDecisionVoter 만 사용자 정의 하면 됩 니 다
  • 다음은 어떻게 실현 되 는 지 살 펴 보 자.
    사용자 정의 AccessDecisionManager
    공식 적 인 세 개의 AccessDecisionManager 는 모두 AccessDecisionVoter 를 바탕 으로 권한 인증 을 실현 하기 때문에 우 리 는 하나의 AccessDecisionVoter 만 사용자 정의 하면 된다.
    사용자 정 의 는 주로 AccessDecisionVoter 인 터 페 이 스 를 실현 합 니 다.저 희 는 공식 RoleVoter 를 본 떠 서 하 나 를 실현 할 수 있 습 니 다.
    
    public class RoleBasedVoter implements AccessDecisionVoter<Object> {
     @Override
     public boolean supports(ConfigAttribute attribute) {
     return true;
     }
    
     @Override
     public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
     if(authentication == null) {
     return ACCESS_DENIED;
     }
     int result = ACCESS_ABSTAIN;
     Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
     for (ConfigAttribute attribute : attributes) {
     if(attribute.getAttribute()==null){
     continue;
     }
     if (this.supports(attribute)) {
     result = ACCESS_DENIED;
    
     // Attempt to find a matching granted authority
     for (GrantedAuthority authority : authorities) {
      if (attribute.getAttribute().equals(authority.getAuthority())) {
      return ACCESS_GRANTED;
      }
     }
     }
     }
     return result;
     }
    
     Collection<? extends GrantedAuthority> extractAuthorities(
     Authentication authentication) {
     return authentication.getAuthorities();
     }
    
     @Override
     public boolean supports(Class clazz) {
     return true;
     }
    }
    어떻게 동적 권한 에 가입 합 니까?vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)에 있 는 Object object 의 종 류 는 FilterInvocation 입 니 다.getRequestUrl 을 통 해 현재 요청 한 URL 을 가 져 올 수 있 습 니 다.
    
     FilterInvocation fi = (FilterInvocation) object;
     String url = fi.getRequestUrl();
    따라서 DB 에서 동적 으로 불 러 온 다음 URL 의 ConfigAttribute 를 판단 하면 됩 니 다.
    이 RoleBasedVoter 를 어떻게 사용 합 니까?configure 에서 accessDecisionManager 방법 을 사용 하여 사용자 정의 합 니 다.저 희 는 공식 Unanimous Based 를 사용 한 다음 에 사용자 정의 Role Based Voter 를 가입 하면 됩 니 다.
    
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
     @Override
     protected void configure(HttpSecurity http) throws Exception {
     http
     .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
     .exceptionHandling()
     .authenticationEntryPoint(problemSupport)
     .accessDeniedHandler(problemSupport)
     .and()
     .csrf()
     .disable()
     .headers()
     .frameOptions()
     .disable()
     .and()
     .sessionManagement()
     .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
     .and()
     .authorizeRequests()
     //    accessDecisionManager
     .accessDecisionManager(accessDecisionManager()) 
     .and()
     .apply(securityConfigurerAdapter());
    
     }
    
     @Bean
     public AccessDecisionManager accessDecisionManager() {
     List<AccessDecisionVoter<? extends Object>> decisionVoters
     = Arrays.asList(
     new WebExpressionVoter(),
     // new RoleVoter(),
     new RoleBasedVoter(),
     new AuthenticatedVoter());
     return new UnanimousBased(decisionVoters);
     }
    사용자 정의 Security MetadataSource
    사용자 정의 FilterInvocation Security MetadataSource 는 인터페이스 만 실현 하면 되 며 인터페이스 에서 DB 동적 로드 규칙 을 사용 합 니 다.
    코드 의 정 의 를 재 활용 하기 위해 서 는 코드 에 생 성 된 Security MetadataSource 테 이 프 를 구조 함수 에 기본 FilterInvocation Security MetadataSource 로 전송 할 수 있 습 니 다.
    
    public class AppFilterInvocationSecurityMetadataSource implements org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource {
     private FilterInvocationSecurityMetadataSource superMetadataSource;
     @Override
     public Collection<ConfigAttribute> getAllConfigAttributes() {
     return null;
     }
    
     public AppFilterInvocationSecurityMetadataSource(FilterInvocationSecurityMetadataSource expressionBasedFilterInvocationSecurityMetadataSource){
      this.superMetadataSource = expressionBasedFilterInvocationSecurityMetadataSource;
      // TODO           
     }
    
     private final AntPathMatcher antPathMatcher = new AntPathMatcher();
     
     //       DB  
     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();
     for(Map.Entry<String,String> entry:urlRoleMap.entrySet()){
      if(antPathMatcher.match(entry.getKey(),url)){
      return SecurityConfig.createList(entry.getValue());
      }
     }
     //            
     return superMetadataSource.getAttributes(object);
     }
    
     @Override
     public boolean supports(Class<?> clazz) {
     return FilterInvocation.class.isAssignableFrom(clazz);
     }
    }
    어떻게 사용 합 니까?accessDecisionManager 와 달리 ExpressionUrl AuthorizationConfigurer 는 set 방법 으로 FilterSecurity Interceptor 의 FilterInvocation Security MetadataSource,how to do 를 설정 하지 않 았 습 니 다.
    확장 방법 withObject PostProcessor 를 발견 하면 이 방법 을 통 해 FilterSecurity Interceptor 형식 을 처리 하 는 Object PostProcessor 를 사용자 정의 하면 FilterSecurity Interceptor 를 수정 할 수 있 습 니 다.
    
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
     @Override
     protected void configure(HttpSecurity http) throws Exception {
     http
      .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
      .exceptionHandling()
      .authenticationEntryPoint(problemSupport)
      .accessDeniedHandler(problemSupport)
     .and()
      .csrf()
      .disable()
      .headers()
      .frameOptions()
      .disable()
     .and()
      .sessionManagement()
      .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
     .and()
      .authorizeRequests()
      //    FilterInvocationSecurityMetadataSource
      .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
      @Override
      public <O extends FilterSecurityInterceptor> O postProcess(
       O fsi) {
       fsi.setSecurityMetadataSource(mySecurityMetadataSource(fsi.getSecurityMetadataSource()));
       return fsi;
      }
      })
     .and()
      .apply(securityConfigurerAdapter());
     }
    
     @Bean
     public AppFilterInvocationSecurityMetadataSource mySecurityMetadataSource(FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) {
     AppFilterInvocationSecurityMetadataSource securityMetadataSource = new AppFilterInvocationSecurityMetadataSource(filterInvocationSecurityMetadataSource);
     return securityMetadataSource;
    }
    작은 매듭
    본 고 는 spring security 를 바탕 으로 동적 권한 을 실현 하 는 두 가지 방법 을 소개 했다.하 나 는 accessDecisionManager 를 사용자 정의 하 는 것 이 고,다른 하 나 는 FilterInvocation Security MetadataSource 를 사용자 정의 하 는 것 이다.실제 항목 에 서 는 수요 에 따라 유연 하 게 선택 할 수 있다.
    확장 읽 기:
    Spring Security 구조 와 소스 코드 분석
    총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기