개요
- 상윈 권한이 하위 권한을 포함할 수 있도록 설정할 수 있다.
권한 계층 적용
- RoleHierarchy 클래스는 상위 계층 Role은 하위 계층 Role의 자원에 접근 가능하도록 설정할 수 있는 클래스다.
- 예를들어 ROLE_ADMIN > ROLE_MANAGER > ROLE_USER 일 경우 ROLE_ADMIN 만 있으면 하위 ROLE 의 권한을 모두 포함되게 설정할 수 있다.
- ROLE_HIERARCHY 테이블을 보면 부모와 자식 권한이 매핑되어 저장되어 있다.
- ROLE_HIERARCHY 테이블에 저장된 정보는 정해진 format에 따라 포매팅 되어야 한다.
- 포매팅된 데이터는 RoleHierarchy 가 가지고 있게 된다.
- RoleHierarchyVoter 는 RoleHierarchy 를 생성자로 받으며 RoleHierarchy 에서 설정된 규칙이 적용되어 심사한다.
- ">" 와 줄바꿈을 꼭 지켜서 포매팅해야된다.
- ">" 와 줄바꿈을 꼭 지켜서 포매팅해야된다.
코드 구현
- RoleHierarchy 클래스 구현
@Entity
@Table(name="ROLE_HIERARCHY")
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
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<RoleHierarchy>();
}
- 다음과 같이 조인컬럼을 형성해서 상위 계층 Role은 하위 계층 Role의 자원에 접근 가능하도록 엔티티를 구성한다.
- 디비로 부터 권한 정보를 가져와서 정보를 포매팅하는 클래스인 RoleHierarchyServiceImpl 를 구현한다.
@Service
public class RoleHierarchyServiceImpl implements RoleHierarchyService {
@Autowired
private RoleHierarchyRepository roleHierarchyRepository;
@Transactional
@Override
public String findAllHierarchy() {
List<RoleHierarchy> rolesHierarchy = roleHierarchyRepository.findAll();
Iterator<RoleHierarchy> itr = rolesHierarchy.iterator();
StringBuffer concatedRoles = new StringBuffer();
while (itr.hasNext()) {
RoleHierarchy model = itr.next();
if (model.getParentName() != null) {
concatedRoles.append(model.getParentName().getChildName());
concatedRoles.append(" > ");
concatedRoles.append(model.getChildName());
concatedRoles.append("\n");
}
}
return concatedRoles.toString();
}
}
- 디비에서 권한정보를 불러온다.
- 권한 정보를 바탕으로 권한 계층을 ">" 로 설정하고 줄바꿈도 반드시 해줘서 포매팅 한다.
Config 파일 설정
private AccessDecisionManager affirmativeBased() {
AffirmativeBased affirmativeBased = new AffirmativeBased(getAccessDecisionVoters());
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;
}
@Bean
public RoleHierarchyImpl roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
return roleHierarchy;
}
- AccessDecisionManager 의 구현체인 affirmativeBased 에서 AccessDecisionVoter 를 설정해야한다.
- RoleHierarchyVoter 의 생성자로 RoleHierarchy 객체를 인자로 넘긴다. -> 정확히는 RoleHierarchy 의 구현체인 RoleHierarchyImpl 을 넘긴다.
- 그렇게 되면 RoleHierarchyImpl 의 getReachableGrantedAuthorities 에서 상위 권한이 하위 권한을 포함하도록 설정되는 작업이 이루어 진다.
- RoleHierarchyImpl 의 setHierarchy 메소드에 계층을 포매팅한 정보를 넣어줘야 한다.
- 포매팅은 위에서 구현한 RoleHierarchyServiceImpl 의 findAllHierarchy 메소드에서 진행했다.
- 포매팅한 정보는 원하는 시점에 넣어주면 되는데, 애플리케이션이 실행될때 넣어주도록 설정하겠다.
- 아래와 같이 설정할 수 있다.
@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);
}
}
- run 메소드 안에 SecurityConfig 파일에서 빈으로 등록한 RoleHierarchyImpl 의 setHierarchy 메소드를 통해서 roleHierarchyService 로부터 얻은 계층 정보를 넘겨주면, 서버가 구동될때 계층이 설정된다.
REFERENCES
- 정수원님의 스프링시큐리티
'Spring Security' 카테고리의 다른 글
Method 시큐리티 프로세스 커스텀 (0) | 2022.02.28 |
---|---|
URL 시큐리티 프로세스 커스텀 (0) | 2022.02.28 |
Voter 커스텀 하기 (0) | 2022.02.28 |
@AuthenticationPrincipal (0) | 2022.02.28 |
메소드 시큐리티 (0) | 2022.02.28 |