FilterInvocationMetadataSource
- SecurityMetadatasource 를 구현한 클래스가 FilterInvocationSecurityMetadataSource 다.
- UrlFilterInvocationSecurityMatadataSource 라는 이름으로 구현
어떤 기능을 구현하는가
- 사용자가 접근하고자 하는 Url 자원 대한 권한 추출
- AccessDecisionManager 에게 전달하여 인가처리 요청
- DB 에서 자원, 권한 정보를 매핑하여 Map으로 관리 함
- 사용자 요청에 매핑된 권한정보 확인
구현 흐름
- 사용자의 요청
- FilterInvocationMetadataSource에는 DB 에서 받은 권한, 자원정보를 가지고 Map객체에 저장
- 요청정보에 대한 권한정보를 추출
- 권한 목록 존재시 AccessDecisionManger 에게 심사요청 (decide(Authentication, FilterInvocation, List<ConfigAttribute>)
구현
FilterInvocationMetadataSource를 구현
- LinkedHashMap <RequestMatcher, List<ConfigAttribute>> 필드를 선언
- getAttributes() 는 매개값으로 FilterInvocation 이 필요하다.
- 매개값인 FilterInvocation으로 HttpServletRequest를 생성(사용자 요청 값)
- Map의 EntrySet 으로 반복 구문을 돌아 키인 RequestMatcher 를 가져와 요청정보가 일치하는지 확인
public class UrlFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private LinkedHashMap<RequestMatcher, List<ConfigAttribute>> requestMap ;
private SecurityResourceService securityResourceService;
public UrlFilterInvocationSecurityMetadataSource(LinkedHashMap<RequestMatcher, List<ConfigAttribute>> resourcesMap, SecurityResourceService securityResourceService) {
this.requestMap = resourcesMap;
this.securityResourceService = securityResourceService;
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
HttpServletRequest request = ((FilterInvocation) object).getRequest();
if(requestMap != null){
for(Map.Entry<RequestMatcher, List<ConfigAttribute>> entry : requestMap.entrySet()){
RequestMatcher matcher = entry.getKey();
if(matcher.matches(request)){
return entry.getValue();//권한정보
}
}
}
return null;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
Set<ConfigAttribute> allAttributes = new HashSet<>();
for (Map.Entry<RequestMatcher, List<ConfigAttribute>> entry : requestMap
.entrySet()) {
allAttributes.addAll(entry.getValue());
}
return allAttributes;
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
public void reload(){
LinkedHashMap<RequestMatcher, List<ConfigAttribute>> reloadedMap = securityResourceService.getResourceList();
Iterator<Map.Entry<RequestMatcher, List<ConfigAttribute>>> iterator = reloadedMap.entrySet().iterator();
requestMap.clear();
while(iterator.hasNext()){
Map.Entry<RequestMatcher, List<ConfigAttribute>> entry = iterator.next();
requestMap.put(entry.getKey(), entry.getValue());
}
}
}
설정클래스로 이동
- exceptionHandling() 메서드의 하위 API 에
.addFilterAt(customFilterSecurityInterceptor(), FilterSecurityInterceptor.class) 로 필터 를 앞에 추가
- FilterSecurityInterceptor 를 Bean으로 등록
필요한 객체들은 FilterInvocationSecurityMetadataSource, AccessDecisionManager 구현체, authenticationManager 이다.
@Bean
public FilterSecurityInterceptor filterSecurityInterceptor() throws Exception {
FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
filterSecurityInterceptor.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource());//구현체
filterSecurityInterceptor.setAccessDecisionManager(affirmativeBased());//1개라도 허용이면 통과
filterSecurityInterceptor.setAuthenticationManager(authenticationManagerBean());//메서드에서 추출
return filterSecurityInterceptor;
}
또한, AccessDecisionManager를 등록할 때 필요한 객체는 AccessDecisionVoter 이다.
private AccessDecisionManager affirmativeBased() {
AffirmativeBased affirmativeBased = new AffirmativeBased(getAccessDecisionVoters());//getter 로 가져옴
return affirmativeBased;
}
private List<AccessDecisionVoter<?>> getAccessDecisionVoters() {
List<AccessDecisionVoter<? extends Object>> accessDecisionVoters = new ArrayList<>();
accessDecisionVoters.add(roleVoter());
return accessDecisionVoters;
}
@Bean
public AccessDecisionVoter<? extends Object> roleVoter() {
RoleHierarchyVoter roleHierarchyVoter = new RoleHierarchyVoter(roleHierarchy());
return roleHierarchyVoter;
}
그리고 구현한 FilterInvocationSecurityMetadataSource 도 Bean 으로 등록되야한다.
@Bean
public UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource() throws Exception {
return new UrlFilterInvocationSecurityMetadataSource(urlResourcesMapFactoryBean().getObject(), securityResourceService);
}
이후에 configure(HttpSecurity) 에 API 등록
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
.accessDeniedPage("/denied")
.accessDeniedHandler(accessDeniedHandler())
.and()
.addFilterAt(customFilterSecurityInterceptor(), FilterSecurityInterceptor.class);
** FilterSecurityInterceptor 는 커스텀 FilterSecurityInterceptor 동작 후에는 작동하지 않고 chain 한다.
** FilterInvocationSecurityMetadataSource 에 선언한 Map 에는 AntRequestMatcher 와 ConfigAttribute 의 구현체인 SecurityConfig 클래스가 있다.
'springframework > 시작하자SpringSecurity' 카테고리의 다른 글
40.FilterInvocationSecurityMetadataSource (2) (0) | 2020.09.30 |
---|---|
38.인가프로세스의 아키텍쳐 (0) | 2020.09.30 |
37. 동적 인가 방식 개요 (0) | 2020.09.30 |
39.Ajax 로그인 구현, CSRF (0) | 2020.09.30 |
38.AjaxLoginUrlAuthenticationEntryPoint, AjaxAccessDeniedHandler (0) | 2020.09.30 |