본문 바로가기

Backend/Spring & SpringBoot

[Spring Security] Spring Boot에서 Spring Security 설정하기 (Database)

728x90

# 구현 과정

 

1) Database를 사용하여 Role에 따라 Access Control 하도록 한다.

- User JPA Entity, Role JPA Entity 가 필요하다. (N:M 매핑 관계로 함)

- User와 Role entity와 관계된 Spring Data JPA repository를 만든다. 

 

2) UserDetailsService와 UserDetails를 만든다.

- UserDetailsService 인터페이스에서 정의된, loadUserByUsername()를 구현한다. 

- UserDetails 인터페이스에 User Entity, Role Entity를 넣어주는 것이 UserDetailsService의 loadUserByUsername 메서드이다.

 

3) Spring Security 설정을 커스터마이징 한다. 

- UserDetailsService의 구현체의 이름을 넘겨줘서 Authentication을 구현한다. 

- 특정 url에 어떤 권한을 갖는지에 대해서 설정한다. 


1. DB table 살펴보기

- JPA 사용, email을 username으로 사용함. 

- users, roles는 Many to Many관계로 JoinTable을 사용한다.

- id가 hibernate_sequence를 통해 자동으로 생성된다. 

테이블의 구조


2. pom.xml 의존성 추가

- jpa, mysql사용 

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-jpa</artifactId>
 </dependency>

 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
 </dependency>

3. application.properties에서 DataSource와 JPA설정 

> mysql -u root -p
> ~ 비밀번호 입력하여 mysql 접속

mysql> create database member default character set utf8 collate utf8_general_ci;  // DB 생성 명령어 
# DataSource Setting
spring.datasource.url=jdbc:mysql://localhost:3306/member?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul
spring.datasource.username=root
spring.datasource.password=rootpw
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.initialization-mode=always 
spring.datasource.sql-script-encoding= UTF-8

# JPA Setting
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=false

- spring.jpa.hibernate.ddl-auto=create 이므로 entity에 맞는 table을 자동으로 생성해줌. 


4. Entity 생성 (User.java, Role.java)

> User.java

@Entity
@Table(name="users")
@Getter
@Setter
@NoArgsConstructor
public class User
{
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Integer id;

    @Column(nullable=false) // Null이 되면 안됨.
    private String password;

    @Column(nullable=false, unique=true) //unique=true 해야함.
    private String email;

    @ManyToMany(cascade=CascadeType.MERGE) 
    @JoinTable(
            name="user_role",
            joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")},
            inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
    private List<Role> roles;
}

 

> Role.java

@Entity
@Table(name="roles")
@Getter
@Setter
public class Role
{
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Integer id;

    @Column(nullable=false, unique=true)
    private String rolename;

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

    public Role(String rolename) {
        this.rolename = rolename;
    }
}

5. CRUD를 구현하기 위해 JpaRepository를 상속받은 UserRepository, RoleRepository 구현 

> UserRepository.java

public interface UserRepository extends JpaRepository<User, Integer>{
    Optional<User> findByEmail(String email);
}

> RoleRepository.java

public interface RoleRepository extends JpaRepository<Role, Integer> {
    Optional<Role> findByRolename(String rolename);
}

 


# 참조 : Spring Security의 동작 방식

Spring Security 동작방식

- 사용자가 request(username, password)를 보낼 때

- Authenticationfilter 가 받아서, username과 Password와 관련된 Token을 생성한다. 

- 토큰 값을 AuthenticationManager가 받아,

- AuthenticationManager의 구현체인 AuthenticationProvider에게 넘긴다.

(AuthenticationProvider은 여러 개 있을 수 있음)

- AuthenticationProvider는 사용자가 보낸 password를 바탕으로 해서 PasswordEncoder를 통해서, Hashed password를 얻어낸다.

- 또한 AuthenticationProvider가 UserDetailsService를 사용하여 DB의 User, Role에 접근한다. 

- UserDetailsService에서 loadUserByUsername()을 통해 UserDetails를 리턴 받는다.

- UserDetails의 password와 사용자가 넘겨준 password(Hashed password)를 바탕으로 하여 확인한다.

- 인증이 성공적으로 이루어지면, AuthenticationFilter안에 SecurityContext에 Authentication 정보를 저장하게 된다.

 

6. UserDetailsService Interface의 loadUserByUsername()를 구현하여 UserDetails를 return 받기

@Service
@Transactional
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    //메서드가 자동으로 불림.
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user = userRepository.findByEmail(username)
                .orElseThrow(() -> new UsernameNotFoundException("Email: " + username + " not found"));
        return new org.springframework.security.core.userdetails.User(user.getEmail(),
                user.getPassword(), getAuthorities(user));

    }

    private static Collection<? extends GrantedAuthority> getAuthorities(User user){
        String[] userRoles = user.getRoles()
                .stream()
                .map((role) -> role.getRolename())
                .toArray(String[]::new);

        Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(userRoles);
        return authorities;
    }
}

7. WebSecurityConfigurerAdapter을 상속받는 Spring Seucirty Config 파일 생성 

@Configuration
@EnableWebSecurity  //얘가 @Configuration이 들어있음
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //AuthenticationProvider가 PasswordEncoder와 UserDetailsService를 사용한다.

    @Autowired
    private UserDetailsService customUserDetailsService;

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

    //인증과 관련된 메서드,
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        //어떤 UserDetailsService를 사용하고, 어떤 PasswordEncoder를 사용하는지
        auth.userDetailsService(customUserDetailsService)
                .passwordEncoder(passwordEncoder());
    }
}

 

 


이전 포스팅 : in-memory에서 Spring Security 기본 설정

 

[Spring Security] Spring Boot에서 Spring Security 설정하기 (in-memory)

# Spring Boot에서 Security를 어떻게 쓰는지 알아보겠다. 0. 프로젝트 생성 1. Maven Dependency 추가 : Default Auto Configuration에 대해서 살펴보기. (New Project에서 생성 Security > Spring Security 추..

zeroco.tistory.com

 

728x90