프로젝트 구조
개발환경
- Intellij 2020.3.2
- Java 11
- JPA
- MySQL
- SpringBoot
의존성
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
compile 'javax.validation:validation-api:2.0.1.Final'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
}
application.properties
server.address=localhost
server.port=8100
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.database=mysql
spring.datasource.url=jdbc:mysql://localhost:3306/mygugu?useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false
spring.datasource.username=gugu
spring.datasource.password=1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
소스코드
SecurityConfig.java
package com.mygg.mygg.config;
import com.mygg.mygg.service.MemberService;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private MemberService memberService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception
{
// static 디렉터리의 하위 파일 목록은 인증 무시 ( = 항상통과 )
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 페이지 권한 설정
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/myinfo").hasRole("MEMBER")
.antMatchers("/**").permitAll()
.and() // 로그인 설정
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/user/login/result")
.permitAll()
.and() // 로그아웃 설정
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
.logoutSuccessUrl("/user/logout/result")
.invalidateHttpSession(true)
.and()
// 403 예외처리 핸들링
.exceptionHandling().accessDeniedPage("/user/denied");
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService).passwordEncoder(passwordEncoder());
}
}
MemberController.java
package com.mygg.mygg.controller;
import com.mygg.mygg.dto.MemberDto;
import com.mygg.mygg.service.MemberService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
@AllArgsConstructor
public class MemberController {
private MemberService memberService;
// 메인 페이지
@GetMapping("/")
public String index() {
return "/index";
}
// 회원가입 페이지
@GetMapping("/user/signup")
public String dispSignup() {
return "/signup";
}
// 회원가입 처리
@PostMapping("/user/signup")
public String execSignup(MemberDto memberDto) {
memberService.joinUser(memberDto);
return "redirect:/user/login";
}
// 로그인 페이지
@GetMapping("/user/login")
public String dispLogin() {
return "/login";
}
// 로그인 결과 페이지
@GetMapping("/user/login/result")
public String dispLoginResult() {
return "/loginSuccess";
}
// 로그아웃 결과 페이지
@GetMapping("/user/logout/result")
public String dispLogout() {
return "/logout";
}
// 접근 거부 페이지
@GetMapping("/user/denied")
public String dispDenied() {
return "/denied";
}
// 내 정보 페이지
@GetMapping("/user/info")
public String dispMyInfo() {
return "/myinfo";
}
// 어드민 페이지
@GetMapping("/admin")
public String dispAdmin() {
return "/admin";
}
}
MemberEntity.java
package com.mygg.mygg.domain.entity;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
@Table(name = "member")
public class MemberEntity {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
@Column(length = 20, nullable = false)
private String email;
@Column(length = 100, nullable = false)
private String password;
@Column(length = 100, nullable = false)
private String name;
@Column(length = 100, nullable = false)
private String nickname;
@Column(length = 100, nullable = false)
private String phone_number;
@Column(length = 100, nullable = false)
private String gender;
@Column(length = 100, nullable = false)
private int age;
@Column(length = 100, nullable = false)
private String location;
@Column(length = 100, nullable = true)
private String photo;
@Column(length = 100, nullable = true)
private String authority;
@Column(length = 100, nullable = true)
private String join_date;
@Column(length = 100, nullable = true)
private String role;
@Builder
public MemberEntity(Long id, String email, String password, String name, String nickname, String phone_number, String gender, int age, String location, String photo, String authority, String join_date, String role) {
this.id = id;
this.email = email;
this.password = password;
this.name = name;
this.nickname = nickname;
this.phone_number = phone_number;
this.gender = gender;
this.age = age;
this.location = location;
this.photo = photo;
this.authority = authority;
this.join_date = join_date;
this.role =role;
}
}
MemberRepository.java
package com.mygg.mygg.domain.repository;
import com.mygg.mygg.domain.entity.MemberEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface MemberRepository extends JpaRepository<MemberEntity, Long> {
Optional<MemberEntity> findByEmail(String userEmail);
}
Role.java
package com.mygg.mygg.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum Role {
ADMIN("ROLE_ADMIN"),
MEMBER("ROLE_MEMBER");
private String value;
}
MemberDto.java
package com.mygg.mygg.dto;
import com.mygg.mygg.domain.entity.MemberEntity;
import lombok.*;
import javax.persistence.Column;
import java.time.LocalDateTime;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class MemberDto {
private Long id;
private String email;
private String password;
private String name;
private String nickname;
private String phone_number;
private String gender;
private int age;
private String location;
private String photo;
private String authority;
private String join_date;
private String role;
private LocalDateTime createdDate;
private LocalDateTime modifiedDate;
public MemberEntity toEntity(){
return MemberEntity.builder()
.id(id)
.email(email)
.password(password)
.name(name)
.nickname(nickname)
.phone_number(phone_number)
.gender(gender)
.age(age)
.location(location)
.photo(photo)
.authority(authority)
.join_date(join_date)
.role(role)
.build();
}
@Builder
public MemberDto(Long id, String email, String password, String name, String nickname, String phone_number, String gender, int age, String location, String photo, String authority, String join_date, String role) {
this.id = id;
this.email = email;
this.password = password;
this.name = name;
this.nickname = nickname;
this.phone_number = phone_number;
this.gender = gender;
this.age = age;
this.location = location;
this.photo = photo;
this.authority = authority;
this.join_date = join_date;
this.role =role;
}
}
MemberService.java
package com.mygg.mygg.service;
import com.mygg.mygg.domain.Role;
import com.mygg.mygg.domain.entity.MemberEntity;
import com.mygg.mygg.domain.repository.MemberRepository;
import com.mygg.mygg.dto.MemberDto;
import lombok.AllArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
@AllArgsConstructor
public class MemberService implements UserDetailsService {
private MemberRepository memberRepository;
@Transactional
public Long joinUser(MemberDto memberDto) {
// 비밀번호 암호화
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
memberDto.setPassword(passwordEncoder.encode(memberDto.getPassword()));
return memberRepository.save(memberDto.toEntity()).getId();
}
@Override
public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException {
Optional<MemberEntity> userEntityWrapper = memberRepository.findByEmail(userEmail);
MemberEntity userEntity = userEntityWrapper.get();
List<GrantedAuthority> authorities = new ArrayList<>();
if (("admin@example.com").equals(userEmail)) {
authorities.add(new SimpleGrantedAuthority(Role.ADMIN.getValue()));
} else {
authorities.add(new SimpleGrantedAuthority(Role.MEMBER.getValue()));
}
return new User(userEntity.getEmail(), userEntity.getPassword(), authorities);
}
}
전체 소스코드는 GitHub 참고
'CodeSiri > Project' 카테고리의 다른 글
[Googoos🕊] 03. Spring | login Session & Join (0) | 2021.05.26 |
---|---|
[Googoos🕊] 02. Spring | 회원가입 - Profile photo & dropbox & authority & 가입 날짜 자동 설정 (0) | 2021.05.17 |
[Mini Project 🚴🏻♀️] 12. 게시글 수정 및 삭제 기능 구현 & 회고 (0) | 2021.02.22 |
[Mini Project 🚴🏻♀️] 11. 게시글 보기 기능 구현 (0) | 2021.02.22 |
[Mini Project 🚴🏻♀️] 10. 게시판 글 목록 기능 구현 (0) | 2021.02.22 |