spring Security 2. - 회원가입 폼

알파로그·2023년 3월 21일
0

Spring Boot

목록 보기
24/57

✏️ Configuation 계층

  • BCryptPasswordEncoder 를 Bean 으로 등록해 Container 에 저장
    • BCryptPasswordEncoderPasswordEncoder 의 구현체이다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    //-- Spring Security 모든 페이지에 대해 접근 허용 --//
    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests().requestMatchers(
                new AntPathRequestMatcher("/**")
        ).permitAll();
        return http.build();
    }

    //-- Spring Security password 암호화 설정 빈 등록 --//
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

✏️ Service 계층

  • PasswordEncoder 를 DI 해 클라이언트가 입력한 Password 를 암호화 한다.
  • 암호화된 password 를 Entity 에 저자하고 영속화 한다.
@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository repository;
    //-- password encoder DI --//
    private final PasswordEncoder passwordEncoder;

    public SiteUser create(String username, String email, String password) {
        SiteUser user = new SiteUser();
        user.setUsername(username);
        user.setEmail(email);
        // password 를 암호화해서 자장하는 작업
        user.setPassword(passwordEncoder.encode(password));

        repository.save(user);
        return user;
    }
}

✏️ Controller 계층

📍 DTO

단순 회원가입 정보를 전달할 때 사용할 DTO 를 생성한다.

  • Entity 에 선언하기 애매했던 어노테이션들을 달아서 구체적인 조건을 만들 수 있다.
    ⚠️ @Email 도 중복이 불가능한 속성을 갖고있음
@Data
public class UserCreateForm {

    @Size(min = 3, max = 25)
    @NotEmpty(message = "사용자 ID 는 필수항목입니다.")
    private String username;

    @NotEmpty(message = "비밀번호는 필수항목입니다.")
    private String password1;

    @NotEmpty(message = "비밀번호 확인은 필수항목입니다.")
    private String password2;

    @NotEmpty(message = "이메일은 필수항목입니다.")
    @Email  // 입력한 양식이 email 이 맞는지 확인하는 어노테이션
    private String email;
}

📍 Controller

  • bindingResult.rejectValue
    • 개발자가 정의한 조건과 맞지 않을경우 오류를 발생시키는 라이브러리
      • method 가 호출될경우 오류 내용이 저장된다.
      • 사용자가 입력한 내용과 오류코드를 함께 확인할 수 있다.
    • hasErrors()
      • 클라이언트가 입력한 값이 개발자가 정의한 양식과 일치하지 않을경우 ture 를 반환한다.
    • 첫번째 Param : 필드 명
    • 두번째 Param : 오류 코드
      • 유지보수를 위해서 오류코드를 미리 정의해놓고 사용하는 것이 좋다.
    • 세번째 Param : 에러 메시지
  • 예외처리
    • 이름 중복, 또는 이 이외에 발생할 예외에 대한 처리를 하는 로직
@Controller
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    //-- 가입 폼 --//
    @GetMapping("/signup")
    public String signup(UserCreateForm userCreateForm) {
        return "signup_form";
    }

    //-- 가입 처리 --//
    @PostMapping("/signup")
    public String signup(
            @Valid UserCreateForm userCreateForm,
            BindingResult bindingResult
    ) {
        // userCreateForm 의 양식과 일치하는지 검증하는 작업
        // 일차하지 않을경우 userCreateForm 에 정의한 메시지가 modele 에 저장된다.
        if (bindingResult.hasErrors()) return "signup_form";

        // 페스워드 1과 2가 동일한지 확인하는 작업
        if (!userCreateForm.getPassword1().equals(userCreateForm.getPassword2())) {
            bindingResult.rejectValue(
                    "password2",
                    "passwordInCorrect",
                    "2개의 패스워드가 일치하지 않습니다.");
            return "signup_form";
        }
            
        //-- 예외처리 로직 --//
        try {
            // 회원가입 시도
            userService.create(
                    userCreateForm.getUsername(),
                    userCreateForm.getEmail(),
                    userCreateForm.getPassword1()
            );
        // 이름이 중복일 경우
        } catch (DataIntegrityViolationException e) {
            e.printStackTrace();
            bindingResult.reject("signupFailed", "이미 등록된 사용자입니다.");
            return "signup_form";
        // 그 외의 예외 처리
        } catch (Exception e) {
            e.printStackTrace();
            bindingResult.reject("signupFailed", e.getMessage());
            return "signup_form";
        }
        // try 가 성공할 경우 메인 페이지로 리다이렉트
        return "redirect:/";
    }

✏️ web 계층

📍 flagment

  • DTO 에서 정의한 조건을 만족시키지 못할경우 메시지가 출력됨
<div th:fragment="formErrorsFragment" class="alert alert-danger"
     role="alert" th:if="${#fields.hasAnyErrors()}">
    <div th:each="err : ${#fields.allErrors()}" th:text="${err}" />
</div>

📍 가입 폼

<html layout:decorate="~{layout}" xmlns:layout="http://www.w3.org/1999/xhtml">
<div layout:fragment="content" class="container my-3">
    <div class="my-3 border-bottom">
        <div>
            <h4>회원 가입</h4>
        </div>
    </div>
<!--    가입 form 시작-->
    <form th:action="@{/user/signup}" th:object="${userCreateForm}" method="post">
<!--        form_errors 를 호출해 양식에 맞게 작성되었는지 검증-->
        <div th:replace="~{form_errors :: formErrorsFragment}"></div>
<!--        클라이언트 입력 공간 시작-->
        <div class="mb-3">
            <label for="username" class="form-label">사용자 ID</label>
            <input type="text" th:field="*{username}" class="form-control">
        </div>
        <div class="mb-3">
            <label for="password1" class="form-label">비밀번호</label>
            <input type="password" th:field="*{password1}" class="form-control">
        </div>
        <div class="mb-3">
            <label for="password2" class="form-label">비밀번호 확인</label>
            <input type="password" th:field="*{password2}" class="form-control">
        </div>
        <div class="mb-3">
            <label for="email" class="form-label">이메일</label>
            <input type="email" th:field="*{email}" class="form-control">
        </div>
<!--        클라이언트 입력 공간 끝-->
<!--        제출 버튼-->
        <button type="submit" class="btn btn-primary">회원 가입</button>
    </form>
<!--    가입 form 종료-->
</div>
</html>

📍 네비게이션 바에 링크 추가

<nav th:fragment="navbarFragment"
     class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
    <div class="container-fluid">
        <a class="navbar-brand" href="/">SBB</a>
        <button class="navbar-toggler"
                type="button"
                data-bs-toggle="collapse"
                data-bs-target="#navbarSupportedContent"
                aria-controls="navbarSupportedContent"
                aria-expanded="false"
                aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse"
             id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<!--                로그인 페이지로 이동 시작-->
                <li class="nav-item">
                    <a class="nav-link" th:href="@{/user/login}">로그인</a>
                </li>
<!--                로그인 페이지로 이동 종료-->
<!--                회원가입 페이지로 이동 시작-->
                <li class="nav-item">
                    <a class="nav-link" th:href="@{/user/signup}">회원가입</a>
                </li>
<!--                회원가입 페이지로 이동 종료-->
            </ul>
        </div>
    </div>
</nav>
profile
잘못된 내용 PR 환영

0개의 댓글