Technology-Share
[SpringSecurity #02] 스프링 시큐리티 JPA 연동 본문
# 스프링 시큐리티 JPA 연동하기
1. 먼저 JPA와 연동하기 위해 JPA, 데이터베이스인 h2 의 의존성을 추가한다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
2. 스프링 시큐리티에서 로그인할 계정정보 테이블을 만들기 위해 아래와 같은 트리구조를 형성해준다.
- Account 도메인 클래스
@Entity
@Getter
@Setter
public class Account {
@Id
@GeneratedValue
private Long id;
@Column(unique = true)
private String username;
private String password;
private String role;
}
- Account Jpa Repository
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
Account findAccountByUsername(String username);
}
- AccountService
@Service
@RequiredArgsConstructor
public class AccountService implements UserDetailsService {
private final AccountRepository accountRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountRepository.findAccountByUsername(username);
if (account == null) {
throw new UsernameNotFoundException(username);
}
return User.builder()
.username(account.getUsername())
.password(account.getPassword())
.roles(account.getRole())
.build();
}
}
이때 implements 한 UserDetailsService 는 시큐리티에서 제공하는 인터페이스이다.
loadUserByUsername 을 Override 를 시킨다.
이 메소드를 활용하면 데이터베이스에 있는 유저 정보를 로그인에 사용하는 메소드이며,
재정의를 통해 로그인 유저를 가져오는 방법을 커스텀해줄 수 있다.
(JPA 활용 안하면 여기에 DAO 를 통해 유저 정보를 가져온다.)
이 메소드는 사용자가 로그인 할 때 입력하는 username 을 통해 계정정보를 가져와서
UserDetails 라는 타입으로 리턴해주기 위한 메소드이다.
UserDetails 라는 객체를 만들 때 위와 같이 build 형태를 따르며, 이때 데이터에서 가져온
account 의 username, password, role 를 주입시켜 생성한다.
3. 그럼 이제 테스트를 위한 계정 정보 등록 테스트 코드를 짜준다.
@RestController
@RequiredArgsConstructor
public class AccountController {
private final AccountRepository accountRepository;
private final AccountService accountService;
@GetMapping("/account/{role}/{username}/{password}")
public Account createAccount(@ModelAttribute Account account) {
Account newAccount = accountService.createNew(account);
return newAccount;
}
}
이때 url 로 받은 정보들을 Acconut 객체로 맵핑하여 AccountService 에서 저장을 시켜준다.
- AccountService
public Account createNew(Account account) {
account.encodePassword(account);
return this.accountRepository.save(account);
}
- Account
public void encodePassword(Account account) {
this.password = "{noop}" + account.getPassword();
}
위의 메소드들을 사용해 사용자에게 입력받은 패스워드를 인코딩하여 정보를 저장하고, 그 객체를 Return 한다.
* {noop} 은 인코딩을 사용하지 않은 것을 의미한다.
그리고 위의 url 이 로그인 없이 동작할 수 있도록 아래처럼 설정을 바꿔준다.
- SecurityConfig
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/", "/info", "/account/**").permitAll()
.mvcMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
그러면 H2 데이터베이스에 저장된 유저 정보로 로그인이 가능해진다. (일반 유저, 관리자)
4. 이제 위에서 설정했던 인코딩을 좀더 리팩토링 해보도록 하겠다.
먼저 GongmoApplication 에 PasswordEncoder 를 Bean으로 등록함으로써 어디서든 사용가능하게 한다.
@SpringBootApplication
public class GongmoApplication {
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
public static void main(String[] args) {
SpringApplication.run(GongmoApplication.class, args);
}
}
이때 NoOpPasswordEncoder 는 옛날버전에서 쓰던 메소드로, 위의 {noop} 와 같은 기능을 가지는 인코더이다.
- AccountService
public Account createNew(Account account) {
account.encodePassword(passwordEncoder);
return this.accountRepository.save(account);
}
AccountService 에서 PasswordEncoder 의존성 주입을 한 후 Account 모델에 파라미터로 넘긴다.
- Account
public void encodePassword(PasswordEncoder passwordEncoder) {
this.password = passwordEncoder.encode(this.password);
}
Account 클래스에 password 를 인코딩 할 수 있도록 코드를 수정해준다.
5. 그러면 이제 Security 에서 권장하는 Password Encoder 를 사용하도록 하겠다.
@Bean
public PasswordEncoder passwordEncoder() {
// return NoOpPasswordEncoder.getInstance();
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
기존에 사용하던 Encoder 대신 위의 Encoder를 사용한다.
{
"id":1,
"username":"jake",
"password":"{bcrypt}$2a$10$oFTspgzWB8lk/7eT4zEDu.mFrbaUxeiOEjdZ2B5RvmcVVzHBD8GAC",
"role":"USER"
}
그러면 위와 같이 암호화되어 저장된다.
'SpringBoot' 카테고리의 다른 글
[SpringSecurity #03] 스프링 시큐리티 테스트 코드 작성 (0) | 2020.09.20 |
---|---|
[SpringSecurity #01] 스프링 시큐리티 개발환경 구성 및 사용방법 (0) | 2020.09.20 |
[SpringBoot] 멀티 모듈 프로젝트 생성 (0) | 2020.07.11 |
[ #SpringBoot] Classpath 오류 (0) | 2020.06.06 |
[#SpringBoot] Jpa 개념 정리 (0) | 2020.05.30 |