Java/Spring Boot 게시판

spring boot 게시판 - 3 <패스워드 암호화, 회원 목록 조회>

요술공주밍키 2022. 11. 11. 16:54

https://magicmk.tistory.com/28

 

spring boot 게시판 - 2 <회원 중복 체크 및 유효성 검사>

https://magicmk.tistory.com/25 Spring boot 게시판 - 1 < 간단 회원 가입 구현> 시스템 구성 Spring boot 2.7.5 Gradle Java 11 Intellij Ultimate 라이브러리 thymeleaf jpa web lombok h2 DB 프로젝트 구조 프로젝트 구조는 위와 같

magicmk.tistory.com

전편에 이은 회원가입 시 패스워드 암호화 및 회원 목록 조회다.

사실 전편보다 이번 내용을 먼저 구현했지만 왜 3번째인지 모르겠다. 😝 


✅ 회원 목록 조회

우선 회원 목록 조회하는 내용부터 살펴보면

 

⏹️ MemberService

package com.practice.board.service;


import com.practice.board.dto.MemberSaveRequestDTO;
import com.practice.board.dto.MemberResponseDTO;
import org.springframework.validation.Errors;

import java.util.List;
import java.util.Map;

public interface MemberService {

    /**
     * 회원 목록 조회
     * @return 회원 정보 목록
     */
    List<MemberResponseDTO> findMembers();
}

인터페이스를 정의했으면 구현한다.

 

⏹️ MemberServiceImpl

package com.practice.board.service.Impl;

import com.practice.board.domain.Member;
import com.practice.board.dto.MemberSaveRequestDTO;
import com.practice.board.dto.MemberResponseDTO;
import com.practice.board.repository.MemberRepository;
import com.practice.board.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;

    @Override
    public List<MemberResponseDTO> findMembers() {
        List<Member> all = memberRepository.findAll();
        List<MemberResponseDTO> members = new ArrayList<>();

        for (Member member: all) {
            MemberResponseDTO build = MemberResponseDTO.builder()
                    .member(member)
                    .build();
            members.add(build);
        }

        return members;
    }
}

repository에서 모든 정보를 찾아와 바로 member를 보내줄 수 있지만 그렇게 되면 전달하고 싶지 않은 내용도

유저에게 전달하기 때문에 DTO를 새로 생성하고 한번 필터링한 것을 볼 수 있다.

 

⏹️ MemberResponseDTO

package com.practice.board.dto;

import com.practice.board.domain.Member;
import lombok.*;

@Getter
@NoArgsConstructor
public class MemberResponseDTO {

    private String email;
    private String username;

    @Builder
    public MemberResponseDTO(Member member) {
        this.email = member.getEmail();
        this.username = member.getUsername();
    }
}

해당 DTO에는 Entity를 DTO로 변환해야 하기 때문에 안에 Builder를 넣어줬다.

 

Service에서 가공을 마친 뒤 Controller에 데이터를 넘기면

⏹️ MemberController

package com.practice.board.controller;

import com.practice.board.dto.MemberResponseDTO;
import com.practice.board.dto.MemberSaveRequestDTO;
import com.practice.board.service.MemberService;
import com.practice.board.validator.CheckEmailValidator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;

import javax.validation.Valid;
import java.util.List;
import java.util.Map;

@Controller
@RequiredArgsConstructor
@Slf4j
public class MemberController {

    private final MemberService memberService;
    private final CheckEmailValidator checkEmailValidator;

    /**
     * 회원 목록 조회
     * @param model
     * @return 회원 목록 페이지
     */
    @GetMapping("/members")
    public String members(Model model) {
        List<MemberResponseDTO> members = memberService.findMembers();
        model.addAttribute("members", members);

        return "/members/memberList";
    }
}

service에서 바로 받아와 model을 통해 view 페이지로 넘겨주면 된다. 너무 간단하다.


✅ 패스워드 암호화

이전까지 패스워드 암호화를 하지 않아 DB에 패스워드가 평문으로 들어가고 있는데 절대 이렇게 놔두면 안 되기 때문에

Spring Security에 있는 BCryptPasswordEncoder를 통해 암호화를 진행할 것이다.

 

우선 security 관련 라이브러리를 가져와야 한다.

⏹️ build.gradle

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-test'

그냥 security를 적용만 하면 서버를 띄워도 이상한 로그인 페이지가 나오고 불편하기 때문에 설정을 한다.

 

⏹️ SecurityConfig

package com.practice.board.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors().disable()                               // cors 방지
                .csrf().disable()                           // csrf 방지
                .formLogin().disable()                      // 기본 로그인 페이지 없애기
                .headers().frameOptions().disable();

        return http.build();
    }
}

예전에는 WebSecurityConfigurerAdapter를 override 해서 코드를 작성했지만 현재는 지원하지 않기 때문에

상속을 받지 않고 SecurityFilterChain을 통해 작성한다.

 

이렇게 작성한 뒤 Service로 돌아가서 회원가입 로직을 작성하면 끝이다.

⏹️ MemberServiceImpl

package com.practice.board.service.Impl;

import com.practice.board.domain.Member;
import com.practice.board.dto.MemberSaveRequestDTO;
import com.practice.board.dto.MemberResponseDTO;
import com.practice.board.repository.MemberRepository;
import com.practice.board.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;

    @Override
    public Long join(MemberSaveRequestDTO memberSaveRequestDTO) {
        memberSaveRequestDTO.setPassword(passwordEncoder.encode(memberSaveRequestDTO.getPassword()));

        Member member = Member.builder()
                .email(memberSaveRequestDTO.getEmail())
                .username(memberSaveRequestDTO.getUsername())
                .password(memberSaveRequestDTO.getPassword())
                .build();

        return memberRepository.save(member).getId();
    }
}

원래는 PasswordEncoder의 생성자를 작성하고 해야한다.

 

@RequiredArgsConstructor의 엄청난 힘으로

private final PasswordEncoder passwordEncoder;

위처럼 작성해주면 사용할 수 있다.

 

끝.