- 회원 가입시 이메일을 인증받아 회원가입시키는 흐름 구성
- 프로젝트 셋팅
user
,role
,role_authority
,role
테이블 구성- 기초 데이터 셋팅을 위한 유틸 서비스 구성
- Email 서비스 연동
security
인증 단계에서 이메일 인증이 진행 중인 상태에 따라 회원가입 플로우 진행
프로젝트 셋팅 및 테이블과 Entity 구성
- 프로젝트 환경
java17
,spring boot 3.2.3
build.gradle
: 주요 설정DAO
:spring-boot-starter-data-jpa
DB 접근 기술Email
:spring-boot-starter-mail
이메일 서비스Security
:spring-boot-starter-security
보안 설정tamplate-engine
:spring-boot-starter-thymeleaf
템플릿 설정gateway
:spring-cloud-starter-openfeign
외부 서버와 통신을 위한 설정database
:com.mysql:mysql-connector-j
mysql
사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.3'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'org.spring.oauth2'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2023.0.0")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
tasks.named('test') {
useJUnitPlatform()
}
- 유저, 역할, 역활_권한, 권한 테이블 설계
MappedSuperclass, BaseEntity
- 공통 테이블 컬럼 정의
@MappedSuperclass
: 부모 클래스 정의@EntityListeners(AuditingEntityListener.class)
:@CreatedDate
,@LastModifiedDate
: 다음과 같은 어노테이션이 동작할수 있도록 하는 어노테이션
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Getter
@Setter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@Column(name = "created_by")
private String createdBy;
@CreatedDate
@Column(name = "created_date")
private LocalDateTime createdDate;
@Column(name = "last_modified_by")
private String lastModifiedBy;
@LastModifiedDate
@Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate;
@Column(name = "used")
private boolean used;
}
User
UserDetails
를 구현하여 실제 검증할때 해당Entity
를 사용할 수 있게 설정.- Security를 통한 인증, 인가 부분을 구현하면서 바뀌는 부분이 많아질 예정, 딱히 설명할부분은 없다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@Getter
@Entity
@Table(name = "users")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User extends BaseEntity implements UserDetails {
@Id
@Column(name = "user_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "user_email", nullable = false, unique = true)
private String userEmail;
@ColumnDefault("1")
@Column(name = "account_non_expired", nullable = false)
private boolean accountNonExpired;
@ColumnDefault("1")
@Column(name = "account_non_locked", nullable = false)
private boolean accountNonLocked;
@ColumnDefault("1")
@Column(name = "credentials_non_expired", nullable = false)
private boolean credentialsNonExpired;
@ColumnDefault("1")
@Column(name = "enabled", nullable = false)
private boolean enabled;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "role_id")
private Roles roles;
public User(String userEmail, String password) {
this.userEmail = userEmail;
this.password = password;
this.accountNonExpired = true;
this.accountNonLocked = true;
this.credentialsNonExpired = true;
this.enabled = true;
}
@Override
public String toString() {
return "User{" +
"password='" + password + '\'' +
", userEmail='" + userEmail + '\'' +
'}';
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.getRoleAuthorities().stream()
.map(a -> new SimpleGrantedAuthority(
a.getAuthorityId().getAuthorityName()
)).collect(Collectors.toList());
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.userEmail;
}
@Override
public boolean isAccountNonExpired() {
return this.accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return this.accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}
Roles
- 역할 테이블, 유저의 역할을 매핑하기 위함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@Getter
@Table(name = "roles")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Roles extends BaseEntity {
@Id
@Column(name = "role_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long roleId;
@Column(name = "role_name", unique = true, nullable = false)
private String roleName;
@OneToMany(mappedBy = "roles", fetch = LAZY)
private List<User> users = new ArrayList<>();
@OneToMany(mappedBy = "roleId", fetch = LAZY)
private List<RoleAuthority> roleAuthorities = new ArrayList<>();
}
RoleAuthority
- 역할과 권한을 중간에서 연결해주는 중간 테이블, 외래키를 복합키로 갖게되는 구조가 된다.
@EmbeddedId
복합키를 사용할 때 쓰이는 어노테이션- 자매품으로
@IdClass
가 있지만 여기서는@EmbeddedId
사용
- 자매품으로
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Entity
@Getter
@Table(name = "role_auth")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RoleAuthority extends BaseEntity {
@EmbeddedId
private RoleAuthorityId id;
@MapsId("roleId")
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "role_id")
private Roles roleId;
@MapsId("authorityId")
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "authority_id")
private Authority authorityId;
}
EmbeddedId
1
2
3
4
5
6
7
8
9
10
11
@Getter
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
public class RoleAuthorityId implements Serializable {
private Long roleId;
private Long authorityId;
}
Authority
- 권한들이 들어가게 되는 테이블 딱히 설명할게 없다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@Getter
@Table(name = "authorities")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Authority extends BaseEntity {
@Id
@Column(name = "authority_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long authorityId;
@Column(name = "authority_name", nullable = false, unique = true)
private String authorityName;
@Column(name = "end_point", nullable = false)
private String endPoint;
@OneToMany(mappedBy = "authorityId", fetch = LAZY)
private List<RoleAuthority> roleAuthorities = new ArrayList<>();
}
- 다음에 할것
- 기초 데이터 셋팅을 위한 유틸 서비스 구성