-
[Spring Boot] 2-4. 스프링 시큐리티, 회원 가입 구현하기프로젝트/도서 관리 시스템 2023. 7. 5. 13:23
스프링 시큐리티를 이용한 로그인 처리는 아래 포스팅에 정리해두었다. 아래 포스팅을 봐야지만 회원 가입을 구현할 수 있다.
https://wonisdaily.tistory.com/270
📑 memberSaveForm (domain)
앞에서 로그인시 domain 클래스로 UserDetails 인터페이스를 구현하는 클래스를 사용하였다. 처음엔 나도 그냥 DB에 생성해둔 컬럼들을 하나의 domain 클래스로 만들어서 사용하려고 했다.
그러나 김영한 강사님의 강의 내용 중 다음과 같은 내용을 발견했다.
회원을 등록하는 기능을 구현한다고 할때 회원 등록시 회원과 관련된 데이터만 전달받는 것이 아니라, 약관 정보도 추가로 받는 등 회원 테이블과 관계없는 수 많은 부가 데이터가 넘어온다. 그래서 보통 회원을 MemberVO라고 봤을때, MemberVO을 직접 전달받는 것이 아니라, 복잡한 폼의 데이터 컨트롤러까지 전달할 별도의 객체로 만들어 전달한다. 예를 들면 MemberSaveForm이라는 폼을 전달받은 전용 객체를 만들어 @ModelAttribute로 사용한다.
따라서 나는 MemberSaveForm과 MemberUpdateForm 으로 domain 객체를 나눌 생각이다.
package com.wish.library.security.domain; import lombok.*; import java.util.Date; @Getter @Setter @ToString @AllArgsConstructor @NoArgsConstructor //기본 생성자를 생성해준다. @Builder public class MemberSaveForm { private String email; // 회원 아이디 private String password; //비밀번호 private String name; private String nickname; private Sex mfCode; //성별 (men, female) private String cellNo; }
📑 MemberMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.wish.library.security.mapper.UserMapper"> <insert id="insert"> insert into members(email, password, name, nickname, mfCode, cellNo) values (#{email}, #{password}, #{name}, #{nickname}, #{mfCode}, #{cellNo}) </insert> </mapper>
📑 MemberMapper.java
package com.wish.library.security.mapper; import com.wish.library.security.domain.MemberSaveForm; import com.wish.library.security.domain.UserDTO; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper { int insert(MemberSaveForm member); }
📑 회원 가입 테스트
package com.wish.library.security.mapper; import com.wish.library.security.domain.MemberSaveForm; import com.wish.library.security.domain.Sex; import com.wish.library.security.service.MemberService; import lombok.extern.slf4j.Slf4j; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.crypto.password.PasswordEncoder; import static org.junit.jupiter.api.Assertions.*; import static org.assertj.core.api.Assertions.*; @SpringBootTest @Slf4j class UserMapperTest { @Autowired private PasswordEncoder passwordEncoder; @Autowired private UserMapper userMapper; @Test public void 회원가입_테스트(){ MemberSaveForm member = MemberSaveForm.builder() .email("test@gmail.com") .password(passwordEncoder.encode("test")) .name("test회원") .nickname("test회원") .mfCode(Sex.FEMALE) .cellNo("01077778888") .build(); int insertResult = userMapper.insert(member); Assertions.assertThat(insertResult).isEqualTo(1); } }
📑 MemberServiceImpl.java
package com.wish.library.security.service; import com.wish.library.security.domain.MemberSaveForm; import com.wish.library.security.mapper.UserMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Slf4j @RequiredArgsConstructor public class MemberServiceImpl implements MemberService{ private final UserMapper mapper; private final PasswordEncoder passwordEncoder; @Transactional @Override public boolean save(MemberSaveForm member) { log.info("============member save service"); try{ member.setPassword(passwordEncoder.encode(member.getPassword())); mapper.insert(member); return true; }catch(Exception e){ e.printStackTrace();//에러의 발생 근원지를 찾아서 단계별로 에러를 출력 log.info("MemberService 회원가입 에러 ={}", e.getMessage()); } return false; } }
여기서 form에서 받아온 정보들 중 비밀번호를 인코딩하여 저장한다. 그다음 mapper.insert(member)로 넘겨주는 것을 잊지말자!
📑 MemberController.java
package com.wish.library.security.controller; import com.wish.library.security.domain.MemberSaveForm; import com.wish.library.security.domain.UserDTO; import com.wish.library.security.service.CustomUserService; import com.wish.library.security.service.MemberService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.support.RedirectAttributes; /** * GET 방식, 화면 조회시 */ @Controller @RequiredArgsConstructor @Slf4j public class UserController { private final CustomUserService customUserService; private final MemberService memberService; @GetMapping("/") public String home(Model model){ String email = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if(!(email.equals("anonymousUser"))) { log.info("index page, security 조회 email, getPrincipal : " + email); UserDTO user = (UserDTO) customUserService.loadUserByUsername(email); user.setPassword(null); model.addAttribute("user", user); } return "index"; } @GetMapping("/login") public String login(){ return "login"; } @GetMapping("/join") public String join(){ return "join"; } @PostMapping("/join") public String join(@ModelAttribute MemberSaveForm memberSaveForm, RedirectAttributes rttr) { log.info("=================join Controller"); boolean saveResult = memberService.save(memberSaveForm); if(saveResult){ rttr.addFlashAttribute("result", "회원 가입 완료"); return "redirect:/login"; } return "join"; } }
Service 단에서 넘겨준 값이 true일 경우에, 즉 회원 가입이 유효성 검사를 통과했을 경우 login 페이지로 넘겨준다. 그 사이 login.jsp 화면에 "회원 가입 완료"라는 result message를 전달해준다. 앞에 login.jsp에 script 부분에서 아래와 같이 처리해줬다. 아직 유효성 검사 로직은 만들기 전이다. 이전 세션을 이용한 로그인/회원가입에서는 javascript로 정규식을 사용한 유효성 검사를 처리해줬다. 이번 시큐리티에서는 @Validated 어노테이션을 사용할 수 있는 Bean Vaildation을 이용할 예정이다. 다음 포스팅에서 정리하겠다.
📑 join.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!doctype html> <html lang="en" data-bs-theme="auto"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> <meta name="generator" content="Hugo 0.111.3"> <meta name="_csrf" content="${_csrf.token}"/> <meta name="_csrf_header" content="${_csrf.headerName}"/> <title>회원가입</title> <link rel="canonical" href="https://getbootstrap.com/docs/5.3/examples/sign-in/"> <link href="assets/dist/css/sign-in.css" rel="stylesheet"> <jsp:include page="./includes/common_includes.jsp"></jsp:include> <%-- <script type="text/javascript" src="assets/dist/js/commonAjax.js"></script> <script type="text/javascript" src="assets/dist/js/member.js"></script>--%> <style> @media (min-width: 768px) { .bd-placeholder-img-lg { font-size: 3.5rem; } } .nav-scroller .nav { display: flex; flex-wrap: nowrap; padding-bottom: 1rem; margin-top: -1px; overflow-x: auto; text-align: center; white-space: nowrap; -webkit-overflow-scrolling: touch; } </style> </head> <script> </script> <body class="text-center"> <main class="form-signin w-100 m-auto"> <form name="join_form" action="/join" method="post"> <%--<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />--%> <img class="mb-4" src="assets/brand/bootstrap-logo.svg" alt="" width="72" height="57" onclick="location.href='/'"> <h1 class="h3 mb-3 fw-normal">Please join in</h1> <div class="form-floating"> <input type="email" class="form-control" name="email" id="email" placeholder="name@example.com"> <label for="email">Email address</label><%--<button class="btn btn-light rounded-pill px-3" type="button" name="btn" id="btnEmailCheck">Duplicate check</button>--%> <span id="id-check"></span> </div> <div class="form-floating"> <input type="password" class="form-control" name="password" id="password" placeholder="Password" autoComplete="off"> <label for="password">Password</label> </div> <div class="form-floating"> <input type="password" class="form-control" name="passwordConfirm" id="passwordConfirm" placeholder="passwordConfirm" autoComplete="off"> <label for="passwordConfirm">passwordConfirm</label> </div> <div class="form-floating"> <input type="text" class="form-control" name="name" id="name" placeholder="name"> <label for="name">name</label> </div> <div class="form-floating"> <input type="text" class="form-control" name="nickname" id="nickname" placeholder="nickname"> <label for="nickname">nickname</label> <%--<button class="btn btn-light rounded-pill px-3" type="button" name="btn" id="btnNicknameCheck">Duplicate check</button>--%> <span id="nickname-check"></span> </div> <div class="form-floating"> <div class="form-check"> <input id="men" name="mfCode" value="MEN" type="radio" class="form-check-input" checked> <label class="form-check-label" for="men">Men</label> </div> <div class="form-check"> <input id="female" name="mfCode" value="FEMALE" type="radio" class="form-check-input"> <label class="form-check-label" for="female">Female</label> </div> </div> <div class="form-floating"> <input type="text" class="form-control" name="cellNo" id="cellNo" placeholder="cellNo"> <label for="cellNo">cellNo</label> </div> <button class="w-50 btn btn-lg btn-dark" type="button" onclick="history.back();" >이전페이지로가기</button> <%--<button class="w-50 btn btn-lg btn-primary" type="button" name="btn" id="btnSingUp" >Sign up</button>--%> <button type="submit" class="w-50 btn btn-lg btn-primary">Sign up</button> </form> </main> </body> </html>
반응형'프로젝트 > 도서 관리 시스템' 카테고리의 다른 글
[Spring Boot] 2-6. Validation을 이용한 회원가입 구현 (jsp) (0) 2023.07.05 [Spring Boot] 2-5. 스프링 시큐리티, 로그인 실패시 메시지 출력 (0) 2023.07.05 [Spring Boot] 2-3. 스프링 시큐리티, 로그인/로그아웃 구현 (0) 2023.07.05 [Spring Boot] 2-2. 세션을 사용하는 회원 가입, 회원 정보 수정 (1) 2023.06.19 [Spring Boot] 2-1. 세션을 사용하는 회원 로그인, 로그아웃 (0) 2023.06.19