1. SecurityConfig 클래스
config라는 패키지를 만들고 스프링시큐리티를 설정을 처리할 SecurityConfig 클래스
rememberMe(): 자동로그인 기능
formLogin: 로그인처리
authorizeRequests: 접권한 처리
PasswordEncoder passwordEncoder(): 비밀번호를 그대로 저장하지 않고 BCryptPasswordEncoder의 해시 함수를 이용하여 암호화처리
package com.example.moduleclient.config;
import com.example.moduleclient.member.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Log4j2
public class SecurityConfig {
private final DataSource dataSource;
private final MemberService memberService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
log.info("----------------configure-------------------------");
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().permitAll()
)
.formLogin(formLogin -> formLogin
.loginPage("/member/login")
.loginProcessingUrl("/member/login")
.defaultSuccessUrl("/boards")
.failureUrl("/member/login")
)
.csrf().disable()
.rememberMe(rememberMe ->
rememberMe
.key("1234678")
.tokenRepository(persistentTokenRepository())
.userDetailsService(memberService)
.tokenValiditySeconds(60*60*24*30)
);
return http.build();
}
//자동 로그인 쿠키 값 인코딩을 위한 키 값, 정보 저장 메서드
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl repo= new JdbcTokenRepositoryImpl();
repo.setDataSource(dataSource);
return repo;
}
@Bean
public PasswordEncoder PasswordEncoder () {
return new BCryptPasswordEncoder();
}
//정적자원 시큐리티 적용 제외(ex. css)
@Bean
public WebSecurityCustomizer webSecurityCustomizer(){
log.info("----------------web configure-----------------------------");
return (web) -> web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
}
2. Member 엔티티 클래스
회원가입 화면으로부터 넘어오는 가입정보를 담을 dto를 생성하고 회원정보를 저장하는 Member 엔티티
관리할 회원 정보는 이름, 이메일, 비밀번호, 닉네임, 역할,생성날짜
package com.example.moduleclient.member;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.time.LocalDateTime;
@Getter
@Setter
@ToString
@Table(name="member_tb")
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int mid;
//동일한 값이 들어올 수 없도록 unique속성 지정
@Column(nullable = false, unique = true, length=50)
private String username;
@Column(name = "password",nullable = false, length=200)
private String password;
@Column(nullable = false, length=50)
private String email;
@Column(nullable = false, length=50)
private String nickname;
private LocalDateTime createdAt;
@Enumerated(EnumType.STRING)
@Column(name = "member_role", nullable = false, length = 50)
private MemberRole memberRole; //등급 권한
//회원정보 생성 메서드
public static Member createMember(MemberJoinDTO memberJoinDTO, PasswordEncoder passwordEncoder){
Member member = new Member();
member.setUsername(memberJoinDTO.getUsername());
member.setEmail(memberJoinDTO.getEmail());
member.setNickname(memberJoinDTO.getNickname());
if (memberJoinDTO.getPassword() != null) {
String password = passwordEncoder.encode(memberJoinDTO.getPassword());
member.setPassword(password);
}
member.setCreatedAt(LocalDateTime.now());
member.setMemberRole(MemberRole.새싹회원); 등급은 새싹회원으로 고정
return member;
}
}
새싹회원과 우수회원을 구분한 enum클래스 생성
package com.example.moduleclient.member;
public enum MemberRole {
새싹회원, 우수회원
}
3. MemberJoinDTO 클래스
회원가입을 위한 DTO 클래스입니다.
입력 값에 대한 유효성 검증을 위해 @NotBlank와 @NotEmpty 어노테이션을 사용
package com.example.moduleclient.member;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import java.time.LocalDate;
@Getter
@Setter
@Data
public class MemberJoinDTO {
@NotBlank(message = "아이디는 필수 입력 값입니다.")
private String username;
@NotEmpty(message = "비밀번호는 필수 입력 값입니다.")
@Length(min=8, max=16, message = "비밀번호는 8자 이상, 16자 이하로 입력해주세요")
private String password;
@NotEmpty(message = "이메일은 필수 입력 값입니다.")
@Email(message = "이메일 형식으로 입력해주세요.")
private String email;
@NotEmpty(message = "닉네임은 필수 입력 값입니다.")
private String nickname;
private LocalDate created_at;
}
4. MemberRepository 인터페이스
Member 엔티티를 데이터베이스에 저장할 MemberRepository
findByUsername : 회원 가입시 중복된 회원이 있는지 검사하기 위해 username으로 회원을 검사하는 메소드
package com.example.moduleclient.member;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberRepository extends JpaRepository<Member,String> {
//usernaem 증복 체크
Member findByUsername(String username);
}
5. MemberService 클래스
회원 관리를 위한 서비스 클래스
회원가입 시 중복 아이디 검사와 실제 인증 처리를 담당
package com.example.moduleclient.member;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Log4j2
@Transactional
@Service
@RequiredArgsConstructor
public class MemberService implements UserDetailsService {
private final MemberRepository memberRepository;
//중복검사 후 회원 저장
public Member saveMember(Member member){
validateDuplicateMember(member);
return memberRepository.save(member);
}
//이미 가입된 회원의 경우 예외 처리
private void validateDuplicateMember(Member member){
Member findMember = memberRepository.findByUsername(member.getUsername());
if(findMember != null){
throw new IllegalStateException("이미 가입된 회원입니다.");
}
}
//실제 인증 처리할 때 호출되는 메서드
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("user name: " + username);
Member member = memberRepository.findByUsername(username);
if(member == null){
throw new UsernameNotFoundException(username);
}
return User.builder()
.username(member.getUsername())
.password(member.getPassword())
.roles(member.getMemberRole().toString())
.build();
}
}
6. MemberController 클래스
회원가입과 로그인 관련 요청을 처리하는 컨트롤러 -> 주석으로 설
package com.example.moduleclient.member;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.Valid;
@Controller
@RequestMapping("/member")
@Log4j2
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
private final PasswordEncoder passwordEncoder;
//로그인 페이지
@GetMapping("/login")
public String loginGET(@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "logout", required = false) String logout,
Model model){
log.info("login get.............");
log.info("logout: "+logout);
// ?error을 이용해 로그인 실패 여부 확인
if (error != null) {
log.info("login error...................");
model.addAttribute("loginErrorMsg", "아이디 또는 비밀번호를 확인해주세요");
}
//?logout을 이용해 로그아웃 여부 확인
if(logout != null){
log.info("user logout...................");
model.addAttribute("logoutMessage", "로그아웃되었습니다.");
}
return "member/login";
}
//회원가입 페이지
@GetMapping("/join")
public String joinGET(Model model){
log.info("join get.................................");
model.addAttribute("memberJoinDTO", new MemberJoinDTO());
return "member/join";
}
// 회원 가입
@PostMapping("/join")
public String joinPOST(@Valid MemberJoinDTO memberJoinDTO, BindingResult bindingResult, Model model){
log.info("join post..............");
//에러 발생 시 다시 회원가입 페이지로 이동
if (bindingResult.hasErrors()) {
return "member/join";
}
try {
Member member = Member.createMember(memberJoinDTO, passwordEncoder);
memberService.saveMember(member);
} catch (IllegalStateException e){
model.addAttribute("errorMessage", e.getMessage());
return "member/join";
}
//회원가입 성공 시 로그인 페이지로 이동
return "redirect:/member/login";
}
}




'프로젝트' 카테고리의 다른 글
[야구 관리 프로그램] - DAO 생성 (0) | 2023.06.26 |
---|---|
[야구 관리 프로젝트] - 모델 생성 (0) | 2023.06.26 |
[야구 관리 프로젝트] - 더미데이터 생성 (0) | 2023.06.26 |
[야구 관리 프로그램] - 테이블 설계(1) (0) | 2023.06.23 |
1. SecurityConfig 클래스
config라는 패키지를 만들고 스프링시큐리티를 설정을 처리할 SecurityConfig 클래스
rememberMe(): 자동로그인 기능
formLogin: 로그인처리
authorizeRequests: 접권한 처리
PasswordEncoder passwordEncoder(): 비밀번호를 그대로 저장하지 않고 BCryptPasswordEncoder의 해시 함수를 이용하여 암호화처리
package com.example.moduleclient.config;
import com.example.moduleclient.member.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Log4j2
public class SecurityConfig {
private final DataSource dataSource;
private final MemberService memberService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
log.info("----------------configure-------------------------");
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().permitAll()
)
.formLogin(formLogin -> formLogin
.loginPage("/member/login")
.loginProcessingUrl("/member/login")
.defaultSuccessUrl("/boards")
.failureUrl("/member/login")
)
.csrf().disable()
.rememberMe(rememberMe ->
rememberMe
.key("1234678")
.tokenRepository(persistentTokenRepository())
.userDetailsService(memberService)
.tokenValiditySeconds(60*60*24*30)
);
return http.build();
}
//자동 로그인 쿠키 값 인코딩을 위한 키 값, 정보 저장 메서드
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl repo= new JdbcTokenRepositoryImpl();
repo.setDataSource(dataSource);
return repo;
}
@Bean
public PasswordEncoder PasswordEncoder () {
return new BCryptPasswordEncoder();
}
//정적자원 시큐리티 적용 제외(ex. css)
@Bean
public WebSecurityCustomizer webSecurityCustomizer(){
log.info("----------------web configure-----------------------------");
return (web) -> web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
}
2. Member 엔티티 클래스
회원가입 화면으로부터 넘어오는 가입정보를 담을 dto를 생성하고 회원정보를 저장하는 Member 엔티티
관리할 회원 정보는 이름, 이메일, 비밀번호, 닉네임, 역할,생성날짜
package com.example.moduleclient.member;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.time.LocalDateTime;
@Getter
@Setter
@ToString
@Table(name="member_tb")
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int mid;
//동일한 값이 들어올 수 없도록 unique속성 지정
@Column(nullable = false, unique = true, length=50)
private String username;
@Column(name = "password",nullable = false, length=200)
private String password;
@Column(nullable = false, length=50)
private String email;
@Column(nullable = false, length=50)
private String nickname;
private LocalDateTime createdAt;
@Enumerated(EnumType.STRING)
@Column(name = "member_role", nullable = false, length = 50)
private MemberRole memberRole; //등급 권한
//회원정보 생성 메서드
public static Member createMember(MemberJoinDTO memberJoinDTO, PasswordEncoder passwordEncoder){
Member member = new Member();
member.setUsername(memberJoinDTO.getUsername());
member.setEmail(memberJoinDTO.getEmail());
member.setNickname(memberJoinDTO.getNickname());
if (memberJoinDTO.getPassword() != null) {
String password = passwordEncoder.encode(memberJoinDTO.getPassword());
member.setPassword(password);
}
member.setCreatedAt(LocalDateTime.now());
member.setMemberRole(MemberRole.새싹회원); 등급은 새싹회원으로 고정
return member;
}
}
새싹회원과 우수회원을 구분한 enum클래스 생성
package com.example.moduleclient.member;
public enum MemberRole {
새싹회원, 우수회원
}
3. MemberJoinDTO 클래스
회원가입을 위한 DTO 클래스입니다.
입력 값에 대한 유효성 검증을 위해 @NotBlank와 @NotEmpty 어노테이션을 사용
package com.example.moduleclient.member;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import java.time.LocalDate;
@Getter
@Setter
@Data
public class MemberJoinDTO {
@NotBlank(message = "아이디는 필수 입력 값입니다.")
private String username;
@NotEmpty(message = "비밀번호는 필수 입력 값입니다.")
@Length(min=8, max=16, message = "비밀번호는 8자 이상, 16자 이하로 입력해주세요")
private String password;
@NotEmpty(message = "이메일은 필수 입력 값입니다.")
@Email(message = "이메일 형식으로 입력해주세요.")
private String email;
@NotEmpty(message = "닉네임은 필수 입력 값입니다.")
private String nickname;
private LocalDate created_at;
}
4. MemberRepository 인터페이스
Member 엔티티를 데이터베이스에 저장할 MemberRepository
findByUsername : 회원 가입시 중복된 회원이 있는지 검사하기 위해 username으로 회원을 검사하는 메소드
package com.example.moduleclient.member;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberRepository extends JpaRepository<Member,String> {
//usernaem 증복 체크
Member findByUsername(String username);
}
5. MemberService 클래스
회원 관리를 위한 서비스 클래스
회원가입 시 중복 아이디 검사와 실제 인증 처리를 담당
package com.example.moduleclient.member;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Log4j2
@Transactional
@Service
@RequiredArgsConstructor
public class MemberService implements UserDetailsService {
private final MemberRepository memberRepository;
//중복검사 후 회원 저장
public Member saveMember(Member member){
validateDuplicateMember(member);
return memberRepository.save(member);
}
//이미 가입된 회원의 경우 예외 처리
private void validateDuplicateMember(Member member){
Member findMember = memberRepository.findByUsername(member.getUsername());
if(findMember != null){
throw new IllegalStateException("이미 가입된 회원입니다.");
}
}
//실제 인증 처리할 때 호출되는 메서드
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("user name: " + username);
Member member = memberRepository.findByUsername(username);
if(member == null){
throw new UsernameNotFoundException(username);
}
return User.builder()
.username(member.getUsername())
.password(member.getPassword())
.roles(member.getMemberRole().toString())
.build();
}
}
6. MemberController 클래스
회원가입과 로그인 관련 요청을 처리하는 컨트롤러 -> 주석으로 설
package com.example.moduleclient.member;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.Valid;
@Controller
@RequestMapping("/member")
@Log4j2
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
private final PasswordEncoder passwordEncoder;
//로그인 페이지
@GetMapping("/login")
public String loginGET(@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "logout", required = false) String logout,
Model model){
log.info("login get.............");
log.info("logout: "+logout);
// ?error을 이용해 로그인 실패 여부 확인
if (error != null) {
log.info("login error...................");
model.addAttribute("loginErrorMsg", "아이디 또는 비밀번호를 확인해주세요");
}
//?logout을 이용해 로그아웃 여부 확인
if(logout != null){
log.info("user logout...................");
model.addAttribute("logoutMessage", "로그아웃되었습니다.");
}
return "member/login";
}
//회원가입 페이지
@GetMapping("/join")
public String joinGET(Model model){
log.info("join get.................................");
model.addAttribute("memberJoinDTO", new MemberJoinDTO());
return "member/join";
}
// 회원 가입
@PostMapping("/join")
public String joinPOST(@Valid MemberJoinDTO memberJoinDTO, BindingResult bindingResult, Model model){
log.info("join post..............");
//에러 발생 시 다시 회원가입 페이지로 이동
if (bindingResult.hasErrors()) {
return "member/join";
}
try {
Member member = Member.createMember(memberJoinDTO, passwordEncoder);
memberService.saveMember(member);
} catch (IllegalStateException e){
model.addAttribute("errorMessage", e.getMessage());
return "member/join";
}
//회원가입 성공 시 로그인 페이지로 이동
return "redirect:/member/login";
}
}




'프로젝트' 카테고리의 다른 글
[야구 관리 프로그램] - DAO 생성 (0) | 2023.06.26 |
---|---|
[야구 관리 프로젝트] - 모델 생성 (0) | 2023.06.26 |
[야구 관리 프로젝트] - 더미데이터 생성 (0) | 2023.06.26 |
[야구 관리 프로그램] - 테이블 설계(1) (0) | 2023.06.23 |