이번에는 Spring Security를 활용한 회원가입 로직을 살펴보고자 한다.
사실 회원가입 로직에서는 Security Filter가 관여하는 부분이 없기 때문에 기존 Spring boot의 로그인 로직과 유사하다.
빠르게 살펴보자
01. 전체 로직
사실 로직 자체는 보편적인 회원가입 로직과 동일하다.
과정은 아래와 같다.
- 클라이언트가 회원가입 요청을 보냄
요청과 함께 가입할 username과 password값도 함께 전달함 - Controller는 이러한 요청을 전달받음
이때 클라이언트가 전달한 값을 Dto 객체를 사용해서 전달받음 - Contorller는 전달받은 Dto객체를 Service로 전달.
- Service단에서 실제 비즈니스 로직을 처리
- 전달받은 username이 기존에 존재하는지 중복 검사
- 전달받은 password를 암호화
- 비즈니스 로직을 처리한 후 Entity객체 형태로 Repository에게 전달
- Repository는 전달받은 값을 실제 DB에 저장
02. DB Table 구조
테이블의 구조는 위와 같다.
03. Entity
UserEntity.java
package com.example.jwt.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
public class UserEntity {
@Id // 기본키 설정
@GeneratedValue(strategy = GenerationType.IDENTITY) // 기본키 생성을 DB에 위임
private int id;
@Column(unique = true)
private String username;
private String password;
private String role; // security에서의 role(ex. admin)
}
- unique = true 옵션을 줘서
username
이 중복되지 않는 값임을 보장한다.
04. Repository
UserRepository.java
package com.example.jwt.repository;
import com.example.jwt.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Integer> { // JPA Repo 사용<entity객체, pk 자료형>
boolean existsByUsername(String username);
UserEntity findByUsername(String username);
}
JPA를 사용해, 기본적인 쿼리를 빠르게 구현한다.
existsByUsername()
: 기존에 동일한 username이 존재하는지 판별
05. Service
암호화 Bean 객체 등록
SecurityFilterChain.java
/**
* 단방향 암호화
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
Configuration 클래스에 BCryptPasswordEncoder
Bean 객체를 등록한다.
해당 객체는 스프링 시큐리티에서 제공하는 암호화 객체이다.
이 객체를 사용하여, 패스워드의 암호화를 진행한다.
이후에 이 객체는 로그인 검증 과정에서도 사용된다.
암호화 로직은 BCrypt 로직을 사용하는데, 이 로직은 스프링 부트에서 많이 사용하는 단방향 인코딩 로직이라고 한다.
이에 관한 내용은 기회가 되면 자세히 설명하고자 한다. 우선 이번 포스팅에서는 암호화 로직 중 하나라고 생각하고 넘어가자.
06. Service 로직
JoinService.java
package com.example.jwt.service;
import com.example.jwt.dto.JoinDto;
import com.example.jwt.entity.UserEntity;
import com.example.jwt.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class JoinService {
UserRepository userRepository;
BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
public JoinService(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
public void joinProcess(JoinDto joinDto){
// 중복 검사
boolean isUser = userRepository.existsByUsername(joinDto.getUsername());
if (isUser) return;
UserEntity data = new UserEntity();
data.setUsername(joinDto.getUsername());
data.setPassword(bCryptPasswordEncoder.encode(joinDto.getPassword()));
data.setRole("ROLE_ADMIN");
userRepository.save(data);
}
}
- 먼저 중복 검사를 진행한다.
위에서 정의한existsByUsername()
을 사용해 중복 검사를 진행한다. - 이후 password를 암호화 처리한다.
07. Controller
Dto
JoinDto.java
package com.example.jwt.dto;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class JoinDto {
private String username;
private String password;
}
먼저 클라이언트가 전송한 데이터를 전달받는데 사용할 Dto객체를 먼저 정의하자.
Controller
JoinController.java
package com.example.jwt.controller;
import com.example.jwt.dto.JoinDto;
import com.example.jwt.service.JoinService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class JoinController {
JoinService joinService;
@Autowired
public JoinController(JoinService joinService) {
this.joinService = joinService;
}
@GetMapping("/join")
public String joinPage(){
return "join";
}
@PostMapping("/joinProc")
public String joinProcess(JoinDto joinDto){
joinService.joinProcess(joinDto);
return "redirect:/login";
}
}
/join
페이지에서 클라이언트가 회원가입 정보를 입력하면, 해당 정보가 /joinProc
로 날아가 처리된다.
회원 가입 처리 후에는 /login
페이지로 redirect한다./join
페이지는 아래에 후술한다.
08. View
resources/templates/join.mustache
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="/joinProc" method="post" name="joinForm">
<input type="text" name="username" placeholder="Username"/>
<input type="password" name="password" placeholder="Password"/>
<input type="submit" value="Join"/>
</form>
</body>
</html>
/join
페이지의 템플릿이다.
form을 통해 회원가입 정보를 입력 받은 후 /joinProc
로 전달한다.
09. 실행 결과
회원가입 이전 DB 테이블
회원가입
회원가입 이후 DB 테이블
회원가입 이후 test_join 레코드가 정상적으로 추가된 것을 확인 가능하다.
Reference
'Back End > Spring && Spring Boot' 카테고리의 다른 글
[Spring Security] 세션 관련 설정하기 - 세션 만료 시간, 최대 세션 갯수, 세션 고정 설정 (0) | 2024.06.04 |
---|---|
[Spring Security] 로그인 과정 살펴보기 (0) | 2024.06.03 |
[Spring Boot] Properties 파일 따로 분리하기 (0) | 2024.06.02 |
[Spring Security] Security 인가 작업 구현 하기 (0) | 2024.06.01 |
[Spring Security] Spring Security 개념과 작동 방식 (0) | 2024.06.01 |