반응형

전자정부프레임워크Spring Security를 사용하여 **역할 기반 권한 관리(RBAC)**를 구현하는 방법을 학습합니다.

RBAC는 보안의 핵심으로, 사용자가 수행할 수 있는 작업을 역할에 따라 제어하는 방식입니다.


1. 역할 기반 권한 관리의 개념

1-1. RBAC란?

  • 역할(Role): 사용자의 직책이나 책임을 나타냅니다.
  • 권한(Permission): 특정 리소스에 대한 작업 권한(읽기, 쓰기 등)을 정의합니다.
  • 사용자(User): 역할을 부여받아 리소스를 제어합니다.

1-2. RBAC의 장점

  • 보안 정책 관리가 용이함.
  • 역할 변경 시 권한 자동 업데이트.
  • 조직 구조에 맞는 보안 모델 제공.

2. 구현 계획

2-1. 요구사항 정의

  1. 관리자(admin)와 사용자(user)로 역할 분리.
  2. 관리자만 특정 리소스에 접근 가능하도록 제한.
  3. 사용자와 역할을 데이터베이스로 관리.

2-2. 구현 흐름

  1. 역할(Role)과 사용자(User) 엔티티 설계.
  2. 권한 부여를 위한 Spring Security 설정.
  3. 사용자 인증과 역할 기반 권한 검증 구현.

3. 구현 코드

3-1. 엔티티 설계

User 엔티티

import javax.persistence.*;
import java.util.Set;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles;

    // Getters and Setters
}

Role 엔티티

import javax.persistence.*;
import java.util.Set;

@Entity
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToMany(mappedBy = "roles")
    private Set<User> users;

    // Getters and Setters
}

3-2. 저장소 설계

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}
import org.springframework.data.jpa.repository.JpaRepository;

public interface RoleRepository extends JpaRepository<Role, Long> {
}

3-3. Spring Security 설정

UserDetailsService 구현

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        return new CustomUserDetails(user);
    }
}

CustomUserDetails 클래스

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.stream.Collectors;

public class CustomUserDetails implements UserDetails {

    private final User user;

    public CustomUserDetails(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getRoles().stream()
                   .map(role -> new SimpleGrantedAuthority(role.getName()))
                   .collect(Collectors.toSet());
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

Spring Security 설정

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    private final UserDetailsService userDetailsService;

    public SecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
            .anyRequest().authenticated()
            .and()
            .formLogin();

        return http.build();
    }

    @Bean
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
}

4. 테스트

  1. 사용자 추가
    • DB에 사용자 계정과 역할을 추가합니다.
    • 비밀번호는 BCrypt를 사용하여 암호화합니다.
  2. 권한 테스트
    • 관리자 경로(/admin/**)는 관리자만 접근 가능.
    • 사용자 경로(/user/**)는 사용자 및 관리자 모두 접근 가능.
    • 인증되지 않은 사용자는 로그인 페이지로 리다이렉트됩니다.

5. 마무리

이번 학습에서는 전자정부프레임워크Spring Security를 활용하여 역할 기반 권한 관리를 구현하였습니다.
RBAC를 통해 보안과 관리 효율성을 높일 수 있었습니다.
다음에서는 전자정부프레임워크에서 REST API 보안 강화를 학습합니다.

반응형

+ Recent posts