이 시리즈에 나오는 모든 내용은 인프런 인터넷 강의 - 스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 에서 기반된 것입니다. 그리고 여기서 인용되는 PPT 이미지 또한 모두 해당 강의에서 가져왔음을 알립니다. 추가적으로 여기에 작성된 코드들 또한 해당 강의의 github 에 올라와 있는 코드를 참고해서 만든 겁니다.
현재는 Admin 권한이 하나 있으면, 그 권한만 갖는다.
하지만 실제로는 우리가 원하는 것은 만약 Admin 권한이 있다면 User, Manager 권한 모두 갖도고록 하고 싶다. 즉 Admin 을 최상위 권한으로 두고 싶다는 의미다.
이러기 위해서는 권한을 계층적으로 적용시켜야 하는데,
스프링 시큐리티를 사용하면 쉽게 구현할 수 있다. 아래 그림을 보자.
RoleHierarchy 를 통해서 권한에 계층으로 다룰 수 있다.
ROLE_ADMIN >
ROLE_MANAGER >
ROLE_USER 순으로 권한이 있을 경울 ROLE_ADMIN 만 있으면 하위 권한을 모두 포함시킨다.
RoleHierarcy 클래스는 문자열을 통해서 이런 권한 계층을 분석한다.
ROLE_SUPER_ADMIN > ROLE_MANAGER
ROLE_MANAGER > ROLE_USER
RoleHierarchyVoter : RoleHierarchy 를 생성자로 받아서 설정된 계층을 토대로 권한 심사를 하게 된다.
구구절절 말해서 뭐하랴. 그냥 구현 코드만 보자.
@Entity
@Table(name = "ROLE_HIERARCHY")
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@ToString(exclude = {"parentName", "roleHierarchy"})
public class RoleHierarchy implements Serializable {
@Id
@GeneratedValue
private Long id;
@Column(name = "child_name")
private String childName;
@ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
@JoinColumn(name = "parent_name", referencedColumnName = "child_name")
private RoleHierarchy parentName;
@OneToMany(mappedBy = "parentName", cascade = {CascadeType.ALL})
private Set<RoleHierarchy> roleHierarchy = new HashSet<>();
}
import io.security.corespringsecurity.domain.entity.RoleHierarchy;
import org.springframework.data.jpa.repository.JpaRepository;
public interface RoleHierarchyRepository extends JpaRepository<RoleHierarchy, Long> {
RoleHierarchy findByChildName(String roleName);
}
@Service
@RequiredArgsConstructor
public class RoleHierarchyServiceImpl implements RoleHierarchyService {
private final RoleHierarchyRepository roleHierarchyRepository;
@Transactional
@Override
public String findAllHierarchy() {
List<RoleHierarchy> roleHierarchy = roleHierarchyRepository.findAll();
StringBuilder concatRoles = new StringBuilder();
for (RoleHierarchy role : roleHierarchy) {
if (role.getParentName() != null) {
concatRoles.append(role.getParentName().getChildName());
concatRoles.append(" > ");
concatRoles.append(role.getChildName());
concatRoles.append("\n");
}
}
return concatRoles.toString();
}
}
INSERT INTO public.role_hierarchy
(id, child_name, parent_name)
VALUES(0, 'ROLE_ADMIN', null),
(1, 'ROLE_MANAGER', 'ROLE_ADMIN'),
(2, 'ROLE_USER', 'ROLE_MANAGER');
select * from role_hierarchy rh ;
@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ... 생략 ...
private AccessDecisionManager affirmativeBased() {
AffirmativeBased affirmativeBased = new AffirmativeBased(getAccessDecistionVoters());
return affirmativeBased;
}
private List<AccessDecisionVoter<?>> getAccessDecistionVoters() {
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;
}
@Bean
public RoleHierarchyImpl roleHierarchy() {
return new RoleHierarchyImpl();
}
@Bean
public MyUrlFilterInvocationSecurityMetadatsSource myUrlFilterInvocationSecurityMetadataSource() throws Exception {
return new MyUrlFilterInvocationSecurityMetadatsSource(urlResourcesMapFactoryBean().getObject(), securityResourceService);
}
@Bean
public PermitAllFilter customFilterSecurityInterceptor() throws Exception {
PermitAllFilter permitAllFilter = new PermitAllFilter(permitAllResources);
permitAllFilter.setSecurityMetadataSource(myUrlFilterInvocationSecurityMetadataSource());
permitAllFilter.setAccessDecisionManager(affirmativeBased());
permitAllFilter.setAuthenticationManager(authenticationManagerBean());
return permitAllFilter;
}
}
import io.security.corespringsecurity.service.RoleHierarchyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.stereotype.Component;
@Component
public class SecurityInitializer implements ApplicationRunner {
@Autowired
private RoleHierarchyService roleHierarchyService;
@Autowired
private RoleHierarchyImpl roleHierarchy;
@Override
public void run(ApplicationArguments args) throws Exception {
String allHierarchy = roleHierarchyService.findAllHierarchy();
roleHierarchy.setHierarchy(allHierarchy);
}
}