728x90
반응형
1. 서론
지난 포스팅에서는 하드코딩된 하나의 유저로 로그인 하는 과정을 학습해보았습니다. 이 방법은 실무에 적합하지 않습니다.
이번 포스팅에서는 여러 유저의 정보를 통해 회원가입을 하고, 회원 가입된 유저를 로그인하는 방법에 대해 학습하겠습니다.
2. 본론
1. User
- 우리는 유저 정보를 저장하기 위해 JPA를 이용할 것입니다. 그러기 위해서 가장 먼저 필요한 것이 도메인 설정입니다.
- 유저에는 UserDetails를 implement 해줍니다. 관련 설정들을 모두 오버라이딩 해줍니다.
import lombok.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@Entity
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class User implements UserDetails {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
@Column(name = "email", unique = true)
private String email;
@Column(name = "password")
private String password;
@Column(name = "auth")
private String auth;
@Builder
public User(String email, String password, String auth) {
this.email = email;
this.password = password;
this.auth = auth;
}
// 사용자의 권한을 콜렉션 형태로 반환
// 단, 클래스 자료형은 GrantedAuthority를 구현해야함
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> roles = new HashSet<>();
for (String role : auth.split(",")) {
roles.add(new SimpleGrantedAuthority(role));
}
return roles;
}
// 사용자의 id를 반환 (unique한 값)
@Override
public String getUsername() {
return email;
}
// 사용자의 password를 반환
@Override
public String getPassword() {
return password;
}
// 계정 만료 여부 반환
@Override
public boolean isAccountNonExpired() {
// 만료되었는지 확인하는 로직
return true; // true -> 만료되지 않았음
}
// 계정 잠금 여부 반환
@Override
public boolean isAccountNonLocked() {
// 계정 잠금되었는지 확인하는 로직
return true; // true -> 잠금되지 않았음
}
// 패스워드의 만료 여부 반환
@Override
public boolean isCredentialsNonExpired() {
// 패스워드가 만료되었는지 확인하는 로직
return true; // true -> 만료되지 않았음
}
// 계정 사용 가능 여부 반환
@Override
public boolean isEnabled() {
// 계정이 사용 가능한지 확인하는 로직
return true; // true -> 사용 가능
}
}
2. UserRepository
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
3. JwtUserDetailsService
- 이 부분을 수정할 것입니다. 하드코딩되어있었던 유저 정보 대신, DB에서 정보를 가져와 비교해보는 검증 과정을 코딩하여봅시다. 또한 회원가입을 위해 정보를 삽입할 수 있는 서비스 계층을 만들어 봅시다.
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
@RequiredArgsConstructor
public class JwtUserDetailsService implements UserDetailsService {
// @Override
// public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// if ("user_id".equals(username)) {
// return new User("user_id", "$2a$10$jCvWm3NXDRFs/EfuI4h4.u0ZxNocv.ZkgEy6qbjVXrfQ5.KzLfhAe",
// new ArrayList<>());
// } else {
// throw new UsernameNotFoundException("User not found with username: " + username);
// }
// }
private final UserRepository userRepository;
/**
* Spring Security 필수 메소드 구현
*
* @param email 이메일
* @return UserDetails
* @throws UsernameNotFoundException 유저가 없을 때 예외 발생
*/
@Override // 기본적인 반환 타입은 UserDetails, UserDetails를 상속받은 UserInfo로 반환 타입 지정 (자동으로 다운 캐스팅됨)
public User loadUserByUsername(String email) throws UsernameNotFoundException { // 시큐리티에서 지정한 서비스이기 때문에 이 메소드를 필수로 구현
return userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException((email)));
}
/**
* 회원정보 저장
*
* @param infoDto 회원정보가 들어있는 DTO
* @return 저장되는 회원의 PK
*/
@Transactional
public Long save(UserDto infoDto) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
infoDto.setPassword(encoder.encode(infoDto.getPassword()));
return userRepository.save(User.builder()
.email(infoDto.getEmail())
.auth(infoDto.getAuth())
.password(infoDto.getPassword()).build()).getId();
}
}
4. UserDto
- json내부 정보를 감싸기 위해 Dto를 설정합니다.
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserDto {
private String email;
private String password;
private String auth;
}
5. UserController
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class UserController {
private final JwtUserDetailsService userService;
@PostMapping("/signup")
public Response signup(@RequestBody UserDto infoDto) { // 회원 추가
Response response = new Response();
try {
userService.save(infoDto);
response.setResponse("success");
response.setMessage("회원가입을 성공적으로 완료했습니다.");
} catch (Exception e) {
response.setResponse("failed");
response.setMessage("회원가입을 하는 도중 오류가 발생했습니다.");
response.setData(e.toString());
}
return response;
}
}
6. Response
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class Response {
private String response;
private String message;
private Object data;
public Response(String response, String message, Object data) {
this.response = response;
this.message = message;
this.data = data;
}
}
7. application.yml
- h2 데이터베이스를 사용할 것입니다.
spring:
jwt:
secret: lemorninglemorninglemorninglemorninglemorninglemorninglemorninglemorninglemorninglemorning
datasource:
# url: jdbc:h2:tcp://localhost/~/testDB
url: jdbc:h2:mem:testDB
username: sa
password:
driver-class-name: org.h2.Driver
devtools:
livereload:
enabled: true # 이 옵션은 정적 파일 변동 시 자동 반영될 수 있도록 돕는다.
jpa:
hibernate:
ddl-auto: create # 이 옵션은 애플리케이션 실행 시점에 테이블을 drop 하고, 다시 생성한다
properties:
hibernate:
# show_sql: true
format_sql: true
thymeleaf:
prefix: classpath:/templates/
suffix: .html
logging:
level:
org.hibernate.SQL: debug
org.hibernate.type: trace # sql query 에 들어오는 파라메타 값 추적
8. test
- 이제 서버를 실행시켜보고, 회원가입을 진행해봅시다.
- 주의해야할 것이 있습니다. 회원 가입 시, controller단에서 userDto를 객체로 받기 때문에, 인증시 사용되는 username,password를 이용하면 안됩니다. 커스터마이징한 email,password로 지정해야합니다. 이것을 프론트엔드 개발자와 협의하는 과정이 필요합니다.
그 다음 로그인 시에는 username, password를 이용합니다.
3. 결론
- DB에 여러명의 유저를 저장하고(회원가입), 로그인하는 API를 만들어보았습니다.
728x90
반응형
'Dev > SpringBoot' 카테고리의 다른 글
26. [springboot] Thymeleaf 값 전달방법 기초(짱쉬움) (0) | 2020.11.04 |
---|---|
25. [springboot] Thymeleaf select option에 관하여 (0) | 2020.11.02 |
23. [SpringBoot] RESTFul한 로그인 구현 예제 (0) | 2020.10.05 |
22. [SpringBoot] 스프링부트 spring security 완전 기초 예제 (0) | 2020.10.04 |
21. [SpringBoot] 간단하게 비밀번호 암호화해보기 (2) | 2020.10.01 |