JPA 를 이용한 Spring Security 로그인 구현(H2 DB)
오늘은 드디어 JPA를 사용하여 H2 데이터베이스에 있는 유저 정보를 이용해서 로그인을 구현해 보도록 하겠습니다.
일단 UserRepository를 만듭니다.
package com.myBoard.demo.model;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByUserName(String userName);
}
UserRepository는 Interface로써, 실제 함수가 아니라, 함수를 정의해 놓은 것에 불과합니다.
Repository 패턴을 왜 사용하는지, 또 무슨 장점이 있는지는 다음에 알아보도록 하고,
이 Repository를 어떻게 사용하는지 살펴봅시다.
package com.myBoard.demo;
// import 생략
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
Optional<User> user = userRepository.findByUserName(userName);
user.orElseThrow(() -> new UsernameNotFoundException("Not found: " + userName));
return user.map(MyUserDetails::new).get();
// return new MyUserDetails(username);
}
}
그 전 포스트에서 사용했었던 MyUserDetailsService에서 사용합니다.
@Autowired 어노테이션은 userRepository 인터페이스를 implement 하는 class 를 자동으로 만들어서 제공해줍니다.
그럼 그 class는 어디에서 만들어야 할까요?
만들 필요가 없다! 가 정답입니다. JPA Repository는 자동으로 만들어지고 자동으로 주입된다고 합니다.
마지막의 map은, user가 Optional 타입이기 때문에,
(Optional에 대해서는 www.daleseo.com/java8-optional-before/ 참조)
user.map의 의미는, MyUserDetails 클라스의 new function에 user가 있을 경우 대입한 이후, 그 이후 return 되는 Optional을 값을 나타내는 것을 get() function이 하는 것입니다.
그럼, MyUserDetails를 한번 볼까요?
package com.myBoard.demo.model;
// import 생략
public class MyUserDetails implements UserDetails {
private String userName;
private String password;
private boolean active;
private List<GrantedAuthority> authorities;
public MyUserDetails(User user) {
this.userName = user.getUserName();
this.password = user.getPassword();
this.active = user.isActive();
this.authorities = Arrays.stream(user.getRole().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return authorities;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return password;
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return userName;
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return active;
}
}
this.authorities 에 화려한 함수들이 많지만, 결국 이 클라스가 하는 일은,
constructor에서 User 값을 받아서, 그 parameter들을 채워 넣는 일입니다.
지난번과같이 하드코딩되어있는 값이 아니라, User에서 받은 값들을 채워 넣었기 때문에,
database의 정보들이 전달됨을 알 수 있습니다.
그럼 이제 다 된 걸까요?
아닙니다! 아직 database 연결이 남았죠!
application.properties에 가서, database 연결을 시켜주고, id와 password도 설정해줍니다.
spring.datasource.url=jdbc:h2:mem:jinmo
spring.h2.console.enabled=true
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.platform=h2
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
아, 그리고 한 가지 더!
JPA를 사용하기 때문에, data.sql을 사용할 때 이제 sql 문법을 사용해 주어야 합니다.
INSERT INTO USER (ID, ACTIVE, PASSWORD, PASSWORD_CONFIRM, ROLE, USER_NAME)
VALUES (1, true, 1234, 1234, 'admin', 'faker');
INSERT INTO USER (ID, ACTIVE, PASSWORD, PASSWORD_CONFIRM, ROLE, USER_NAME)
VALUES (2, true, 1234, 1234, 'admin', 'madlife');
INSERT INTO USER (ID, ACTIVE, PASSWORD, PASSWORD_CONFIRM, ROLE, USER_NAME)
VALUES (3, true, 1234, 1234, 'admin', 'showmaker');
이제 다 됬습니다!
spring boot application을 재시작하고, id/pwd를 넣고 로그인을 하면
화면이 나옵니다.