본문 바로가기
Dev/SpringBoot

15. [Spring Boot] URL별 접근권한 DB에서 가져와서 처리하기(2)

by VIPeveloper 2020. 6. 18.
반응형

1. 서론

지난 포스팅에서는 urlResourcesMapFactoryBean().getObject() 처리를 어떻게 해줄 것인지까지 알아보았습니다.

이번포스팅에서는 UrlResourcesMapFactoryBean클래스의 동작 과정에 대해 알아보겠습니다.

지난 포스팅을 보시려면 아래 링크를 참조하세요.

 

14. [Spring Boot] URL별 접근권한 DB에서 가져와서 처리하기(1)

1. 서론 - 이번 포스팅에서는 URL이 조회될 때마다 해당 URL의 접근권한을 확인해서 접속 가능, 불가능을 처리하는 방법을 포스팅해보겠습니다. - 과정이 조금 많이 복잡해서 이해하기 힘들었는데,

dkyou.tistory.com

현재 경로 : 
SecurityConfig.java >
.addFilterBefore(customFilterSecurityInterceptor(),FilterSecurityInterceptor.class) >

customFilterSecurityInterceptor >
filterSecurityInterceptor.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource()) >
new UrlFilterInvocationSecurityMetadataSource(urlResourcesMapFactoryBean().getObject()) > urlFilterInvocationSecurityMetadataSource()

 

 

2. 본론

- 먼저 UrlResourcesMapFactoryBean클래스를 만들어줍니다. 내용은 아래와 같습니다.

 

UrlResourcesMapFactoryBean.java

현재 경로 : 
SecurityConfig.java >
.addFilterBefore(customFilterSecurityInterceptor(),FilterSecurityInterceptor.class) >

customFilterSecurityInterceptor > 
filterSecurityInterceptor.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource()) > 
new UrlFilterInvocationSecurityMetadataSource(urlResourcesMapFactoryBean().getObject()) > 

urlFilterInvocationSecurityMetadataSource() >

urlResourcesMapFactoryBean.getObect()

import com.ktnet.service.SecurityResourceService;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.util.matcher.RequestMatcher;

import java.util.LinkedHashMap;
import java.util.List;

public class UrlResourcesMapFactoryBean implements FactoryBean<LinkedHashMap<RequestMatcher, List<ConfigAttribute>>> {

    private SecurityResourceService securityResourceService;    // 직접 만든 클래스

    private LinkedHashMap<RequestMatcher,List<ConfigAttribute>> resourceMap;

    public void setSecurityResourceService(SecurityResourceService securityResourceService) {
        this.securityResourceService = securityResourceService;
    }

    @Override
    public LinkedHashMap<RequestMatcher, List<ConfigAttribute>> getObject() throws Exception {
        if(resourceMap == null){
            resourceMap = securityResourceService.getResourceList();
        }
        return resourceMap;
    }

    @Override
    public Class<?> getObjectType() {
        return LinkedHashMap.class;
    }

    @Override
    // 메모리에 단 하나만 존재할 수 있도록
    public boolean isSingleton() {
        return true;
    }
}

 

1. 어느정도 실마리가 잡히고 있습니다. implements로 FactoryBean<...>을 해주었는데, @Override된 메서드중에 getObject()메서드가 있습니다!

 

2. 코드를 살펴보면, resourceMap이 null일 경우 securityResourceService.getResourceList()를 받아서 넣어주는 것으로 되어있네요.

 

3. 그런데 이건 securityResourceService클래스에서 제공해주는 메서드입니다! 이것 또한 코드로 구현해주어야 합니다.. 마치 러시아 인형처럼 계속해서 파고드는 모습을 보여줍니다.

타고타고타고...들어가기!

SecurityResourceService.java

- 이 클래스를 구현해줍니다.

 

현재 경로 : 
SecurityConfig.java >
.addFilterBefore(customFilterSecurityInterceptor(),FilterSecurityInterceptor.class) >

customFilterSecurityInterceptor > 
filterSecurityInterceptor.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource()) > 
new UrlFilterInvocationSecurityMetadataSource(urlResourcesMapFactoryBean().getObject()) > 

urlFilterInvocationSecurityMetadataSource() >

urlResourcesMapFactoryBean.getObect() >

SecurityResourceService.getObject() >

import com.ktnet.domain.Resources;
import com.ktnet.domain.RoleResources;
import com.ktnet.repository.ResourcesRepository;
import com.ktnet.repository.RoleResourcesRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

@Transactional
public class SecurityResourceService {

    @Autowired
    private RoleResourcesRepository roleResourcesRepository;

    @Autowired
    private ResourcesRepository resourcesRepository;

    public SecurityResourceService(ResourcesRepository resourcesRepository) {
        this.resourcesRepository = resourcesRepository;
    }

    public LinkedHashMap<RequestMatcher, List<ConfigAttribute>> getResourceList(){

        // DB로부터 권한과 자원정보를 가져온다.
        LinkedHashMap<RequestMatcher,List<ConfigAttribute>> result = new LinkedHashMap<>();
        List<Resources> resourcesList = resourcesRepository.findAll();  // Resources 데이터 전부 가져오기
        List<RoleResources> roleResources = roleResourcesRepository.findAll();  // RoleResources 데이터 전부 가져오기
        resourcesList.forEach(resource ->{  // For문 돌면서~
            List<ConfigAttribute> configAttributeList = new ArrayList<>();  // 권한정보 저장 리스트 생성해주고 여기에 담을꺼다.
            // 자원에 해당되는 권한 목록을 가져온다.
            Long resourceId = resource.getId();   // for문 내부에서 도는 요소. resourceId.
            roleResources.forEach(roleResource ->{  // 이중 For문 시작!
                if(roleResource.getResources().getId() == resourceId)   // Resource의 아이디와 roleResource에서 뽑은 아이디가 같다면
                    configAttributeList.add(new SecurityConfig(roleResource.getRole().getRoleName()));  // 권한정보 이름 더하기!
            });
            result.put(new AntPathRequestMatcher(resource.getResourceName()),configAttributeList);  // 해시로 {이름:[권한들]} 매핑해주기
        });
        return result;
    }
}

 

여기서 반환되는 result값은 resourceMap으로 변환되어 LinkedHashMap으로 반환됩니다.

@Override
    public LinkedHashMap<RequestMatcher, List<ConfigAttribute>> getObject() throws Exception {
        if(resourceMap == null){
            resourceMap = securityResourceService.getResourceList();
        }
        return resourceMap;
    }

 

이 resourceMap은 SecurityConfig의 이 메서드에 연결되며, setSecurityResourceService에 반영되어 리턴됩니다.

private UrlResourcesMapFactoryBean urlResourcesMapFactoryBean() {
        UrlResourcesMapFactoryBean urlResourcesMapFactoryBean = new UrlResourcesMapFactoryBean();
        urlResourcesMapFactoryBean.setSecurityResourceService(securityResourceService);
        return urlResourcesMapFactoryBean;
    }

 

SecurityConfig.java의 두번째줄인 것은 간단합니다.

filterSecurityInterceptor.setAccessDecisionManager(affirmativeBased());
private AccessDecisionManager affirmativeBased() {
  AffirmativeBased affirmativeBased = new AffirmativeBased(getAccessDecisionVoters());
  return affirmativeBased;
}


private List<AccessDecisionVoter<? extends Object>> getAccessDecisionVoters() {
  return Arrays.asList(new RoleVoter());
}

으로 간단하게 작성해주면 됩니다.

 

세번째 줄도 간단히 작성해줍니다.

// 인증 매니저
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

 

3. 결론

- 유저 계정으로 접속을 하게되면, 유저페이지url에는 접속이 가능하지만, users/testadmin페이지에는 접속이 불가능한 것을 볼 수 있습니다.

 

유저권한이 접속가능한 페이지
접근불가페이지

 

반응형