포트폴리오/ohouseClone
9.일반 로그인 만들기
가끔개발
2023. 11. 2. 00:42
0. Kanbanboard

1. 회원가입 page

- 이메일
- 비밀번호
- 이름
- 닉네임
- 생년원일
을 입력받고 권한을 준 후 DB에저장한다.
2. Member CRUD controller
package com.portfolio.ohousev1.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.portfolio.ohousev1.entity.constant.RoleType;
import com.portfolio.ohousev1.entity.constant.RoleTypesConverter;
import jakarta.persistence.*;
import lombok.*;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@Entity
@Table(name = "member")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class Member extends AuditingFields{
@Id //보통 MemberNo 멤버 번호하고 autoincrement한다
@Column(name = "email", unique = true)
private String email;
@Column
private String Password;
@Column
private String nickname;
@Column
private String name;
@Column
private LocalDate birthday;
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Post> posts = new ArrayList<>();
@Convert(converter = RoleTypesConverter.class)
@Column(nullable = false)
private Set<RoleType> roleTypes = new LinkedHashSet<>();
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
public Member(String email, String password, String nickname,
Set<RoleType> roleTypes,String name,LocalDate birthday){
this.email =email;
this.Password =password;
this.roleTypes =roleTypes;
this.nickname = nickname;
this.name = name;
this.birthday = birthday;
}
public static Member of(String email, String password, Set<RoleType> roleTypes,String name, String nickname, LocalDate birthday) {
return new Member(email,password,name, nickname,birthday,null,roleTypes,null);
}
public Member updatePassword(String password){
this.Password = password;
return this;
}
public String updateNickname(String name){
this.nickname =name;
return name;
}
public Member Additional(String name, String nickname, LocalDate birthday){
this.name =name;
this.nickname = nickname;
this.birthday = birthday;
return this;
}
public static Member of( String email, String nickname, String password, Set<RoleType> roleTypes, String name, LocalDate birthday){
return new Member( email, nickname,password,name,birthday,null,roleTypes,null);
}
public static Member of( String email, String nickname, String password, Set<RoleType> roleTypes, String name, LocalDate birthday,List<Post> posts,List<Order> orders){
return new Member(email, nickname,password,name,birthday,posts,roleTypes,orders);
}
}
3. Member CRUD controller
@PostMapping("/signup")
public String createMember(@ModelAttribute MemberRequest request) {
memberService.saveMember(request.email(),request.Password(), request.dto().roleTypes(),request.name(),request.nickname(),request.birthday());
return "redirect:/";
}
// 업데이트
@PreAuthorize("isAuthenticated()")
@GetMapping("/edit")
public String UpdateMember(Authentication authentication, ModelMap map){
String email = authentication.getName();
MemberResponse Member = MemberResponse.from(memberService.getMember(email));
map.addAttribute("member", Member);
map.addAttribute("formStatus", FormStatus.UPDATE);
return "user/additional";
}
@PreAuthorize("isAuthenticated()")
@PostMapping("/edit")
public String UpdateMember(Authentication authentication, @ModelAttribute MemberUpdateRequest request) {
String email = authentication.getName();
memberService.updateMember(email,request.dto());
return "redirect:/";
}
@DeleteMapping("/{email}/delete")
public String DeleteMember( @PathVariable String email) {
memberService.deleteMember(email);
return "redirect:/";
}
4. Member CRUD Repository
@Repository
public interface MemberRepository extends JpaRepository<Member, String> {
Optional<Member> findByEmail(String email);
Optional<Member> findByNickname(String nickname);
void deleteByEmail(String email);
}
5. Member CRUD Service
@Transactional
public MemberDto saveMember(String email,String Password, Set<RoleType> roleTypes, String name, String nickname, LocalDate birthday) {
roleTypes = Set.of(RoleType.USER);
validateDuplicateMember(email);
String encodePassword = passwordEncoder.encode(Password); // 비밀번호 해싱
return MemberDto.from(memberRepository.save(Member.of(email,encodePassword, roleTypes,name, nickname, birthday)));
}
private void validateDuplicateMember(String email) {
// 이미 가입된 이메일 주소인지 확인
Optional<Member> existingMember = memberRepository.findByEmail(email);
if (existingMember.isPresent()) {
// 이미 가입된 회원이면 예외를 발생
throw new IllegalStateException("이미 가입된 회원입니다.");
}
}
@Transactional
public String updateMember(String email, MemberDto dto) {
Member member = memberRepository.findByEmail(email).orElseThrow(() -> new IllegalArgumentException("해당 맴버 없습니다. id" + email));
Optional<Member> existingNickanme = memberRepository.findByNickname(dto.nickname());
if (existingNickanme.isPresent()) {
throw new IllegalArgumentException("이미 존재하는 닉네임입니다. 다른 닉네임을 입력해주세요.");
} else { // 닉네임만 바꿀때
member.updateNickname(dto.nickname());
}
if (dto.Password() != null && !dto.Password().isEmpty()) { // 비밀번호만 바꿀때
String encodePassword = passwordEncoder.encode(dto.Password()); // 비밀번호 해싱
member.updatePassword(encodePassword);
}else { // oauth2 추가 사항
member.Additional(dto.name(), dto.nickname(), dto.birthday());
}
return dto.email();
}
@Transactional
public void deleteMember(String email) {
memberRepository.deleteByEmail(email);
}
6. MemberRequest
package com.portfolio.ohousev1.dto.member.request;
import com.portfolio.ohousev1.dto.member.MemberDto;
import com.portfolio.ohousev1.entity.constant.RoleType;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;
import java.time.LocalDate;
import java.util.Set;
public record MemberRequest(
@NotEmpty(message = "이메일 필수입니다.")
@Email(message = "올바른 이메일 주소를 입력하세요.")
String email,
Long MemberNo,
@NotEmpty(message = "비밀번호 필수입니다.")
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}$", message = "비밀번호는 최소 8자 이상이어야 하며, 대문자, 소문자, 숫자를 포함해야 합니다.")
String Password,
@NotEmpty(message = "이름 필수입니다.")
String name,
@NotEmpty(message = "닉네임 필수입니다.")
String nickname,
@NotEmpty(message = "생일은 필수입니다.")
LocalDate birthday
) {
public static MemberRequest of(String email, Long MemberNo,String Password, String name, String nickname, LocalDate birthday) {
return new MemberRequest(email,MemberNo, Password,name,nickname,birthday);
}
public MemberDto dto(){
Set<RoleType> roleTypes = Set.of(RoleType.USER);
return MemberDto.of(email,Password,roleTypes,name,nickname,birthday);
}
}
7.MemberUpdateRequest
package com.portfolio.ohousev1.dto.member.request;
import com.portfolio.ohousev1.dto.member.MemberDto;
import com.portfolio.ohousev1.entity.constant.RoleType;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;
import java.time.LocalDate;
import java.util.Set;
public record MemberUpdateRequest(
String email,
@NotEmpty(message = "비밀번호 필수입니다.")
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}$", message = "비밀번호는 최소 8자 이상이어야 하며, 대문자, 소문자, 숫자를 포함해야 합니다.")
String Password,
@NotEmpty(message = "이름 필수입니다.")
String name,
@NotEmpty(message = "닉네임 필수입니다.")
String nickname,
@NotEmpty(message = "생일은 필수입니다.")
LocalDate birthday
) {
public static MemberUpdateRequest of(String email,String Password,String name, String nickname, LocalDate birthday) {
return new MemberUpdateRequest(email,Password,name, nickname, birthday);
}
public MemberDto dto(){
Set<RoleType> roleTypes = Set.of(RoleType.USER);
return MemberDto.of(email,Password,roleTypes,name,nickname,birthday);
}
}
8. MemberDto
package com.portfolio.ohousev1.dto.member;
import com.portfolio.ohousev1.entity.Member;
import com.portfolio.ohousev1.entity.constant.RoleType;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Set;
public record MemberDto (
String email,
String Password,
Set<RoleType> roleTypes,
String name,
String nickname,
LocalDate birthday,
LocalDateTime createdAt,
LocalDateTime modifiedAt
)
{
public static MemberDto of(String email, String Password,Set<RoleType> roleTypes,String name, String nickname, LocalDate birthday){
return new MemberDto(email,Password, roleTypes,name,nickname,birthday,null,null);
}
public static MemberDto of(String email, String Password,Set<RoleType> roleTypes, String nickname){
return new MemberDto(email,Password,roleTypes,null,nickname,null,null,null);
}
public static MemberDto of(String email,String Password,Set<RoleType> roleTypes, String name, String nickname,LocalDate birthday, LocalDateTime createdAt, LocalDateTime modifiedAt){
return new MemberDto(email,Password,roleTypes,name,nickname,birthday,createdAt,modifiedAt);
}
public static MemberDto of(MemberDto memberDto ) {
return new MemberDto(memberDto.email, memberDto.Password,memberDto.roleTypes, memberDto.name, memberDto.nickname, memberDto.birthday, memberDto.createdAt, memberDto.modifiedAt);
}
public static MemberDto from(Member entity){
return new MemberDto(
entity.getEmail(),
entity.getPassword(),
entity.getRoleTypes(),
entity.getName(),
entity.getNickname(),
entity.getBirthday(),
entity.getCreatedAt(),
entity.getModifiedAt()
);
}
public Member toEntity() {
return Member.of(
email,
Password,
roleTypes,
name,
nickname,
birthday
);
}
}
9. Member
package com.portfolio.ohousev1.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.portfolio.ohousev1.entity.constant.RoleType;
import com.portfolio.ohousev1.entity.constant.RoleTypesConverter;
import jakarta.persistence.*;
import lombok.*;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@Entity
@Table(name = "member")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class Member extends AuditingFields{
@Id
@Column(name = "email", unique = true)
private String email;
@Column
private String Password;
@Column
private String nickname;
@Column
private String name;
@Column
private LocalDate birthday;
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Post> posts = new ArrayList<>();
@Convert(converter = RoleTypesConverter.class)
@Column(nullable = false)
private Set<RoleType> roleTypes = new LinkedHashSet<>();
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
public Member(String email, String password, String nickname,
Set<RoleType> roleTypes,String name,LocalDate birthday){
this.email =email;
this.Password =password;
this.roleTypes =roleTypes;
this.nickname = nickname;
this.name = name;
this.birthday = birthday;
}
public static Member of(String email, String password, Set<RoleType> roleTypes,String name, String nickname, LocalDate birthday) {
return new Member(email,password,name, nickname,birthday,null,roleTypes,null);
}
public Member updatePassword(String password){
this.Password = password;
return this;
}
public String updateNickname(String name){
this.nickname =name;
return name;
}
public Member Additional(String name, String nickname, LocalDate birthday){
this.name =name;
this.nickname = nickname;
this.birthday = birthday;
return this;
}
public static Member of( String email, String nickname, String password, Set<RoleType> roleTypes, String name, LocalDate birthday){
return new Member( email, nickname,password,name,birthday,null,roleTypes,null);
}
public static Member of( String email, String nickname, String password, Set<RoleType> roleTypes, String name, LocalDate birthday,List<Post> posts,List<Order> orders){
return new Member(email, nickname,password,name,birthday,posts,roleTypes,orders);
}
}
10. SecurityConfig
package com.portfolio.ohousev1.config;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity
public class SecurityConfig {
private final OAuth2UserService oAuth2UserService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.requestMatchers(new AntPathRequestMatcher("/**")).permitAll())
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(new AntPathRequestMatcher("/mysql/**")))
.headers((headers) -> headers
.addHeaderWriter(new XFrameOptionsHeaderWriter(
XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN)));
// login 설정
http
.oauth2Login(httpSecurityOAuth2LoginConfigurer -> httpSecurityOAuth2LoginConfigurer
.loginPage("/login")
.userInfoEndpoint()
.userService(oAuth2UserService));
http
.formLogin((formLogin) -> formLogin
.loginPage("/login")
.usernameParameter("email") // login에 필요한 id 값을 email로 설정 (default는 username)
.passwordParameter("password")// login에 필요한 password 값을 password(default)로 설정
.defaultSuccessUrl("/")); // login에 성공하면 /로 redirect
http
.logout((logout) -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/members/logout"))
.permitAll()
.logoutSuccessUrl("/")// logout에 성공하면 /로 redirect
.invalidateHttpSession(true));
return http.build();
}
@Bean
public UserDetailsService userDetailsService(MemberService memberService) {
return email -> memberService
.searchEmail(email)
.map(PostPrincipal::from)
.orElseThrow(() -> new UsernameNotFoundException("유저를 찾을 수 없습니다 - username: " + email));
}
}
11. SequenceDiagram

https://github.com/nodwon/OhouseV1
GitHub - nodwon/OhouseV1: 지금까지 공부한것을 기반으로 포트폴리오 제작
지금까지 공부한것을 기반으로 포트폴리오 제작. Contribute to nodwon/OhouseV1 development by creating an account on GitHub.
github.com