์คํ๋ง ์ํ๋ฆฌํฐ(Spring Security)๋ ๋ง๊ฐํ ์ธ์ฆ(Authentication)๊ณผ ์ธ๊ฐ(Authorization) (ํน์ ๊ถํ ๋ถ์ฌ) ๊ธฐ๋ฅ์ ๊ฐ์ง ํ๋ ์์ํฌ์ ๋๋ค. ์ฌ์ค์ ์คํ๋ง ๊ธฐ๋ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ๋ณด์์ ์ํ ํ์ค์ด๋ผ๊ณ ๋ณด๋ฉด ๋ฉ๋๋ค.
์ธํฐ์ ํฐ, ํํฐ ๊ธฐ๋ฐ์ ๋ณด์ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ๊ฒ๋ณด๋ค ์คํ๋ง ์ํ๋ฆฌํฐ๋ฅผ ํตํด ๊ตฌํํ๋ ๊ฒ์ ์ ๊ทน์ ์ผ๋ก ๊ถ์ฅํ๊ณ ์์ต๋๋ค.
์คํ๋ง์ ๋๋ถ๋ถ ํ๋ก์ ํธ๋ค(Mvc, Data, Batch ๋ฑ๋ฑ)์ฒ๋ผ ํ์ฅ์ฑ์ ๊ณ ๋ คํ ํ๋ ์์ํฌ๋ค ๋ณด๋ ๋ค์ํ ์๊ตฌ์ฌํญ์ ์์ฝ๊ฒ ์ถ๊ฐํ๊ณ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. ์ด๋ฐ ์์ฌ์ด ์ค์ ์ ํนํ๋ ์คํ๋ง ๋ถํธ 1.5์์ 2.0์ผ๋ก ๋์ด์ค๋ฉด์ ๋์ฑ ๋ ๊ฐ๋ ฅํด์ก์ต๋๋ค.
์ด๋ฒ ์ฅ์์๋ ์คํ๋ง ์ํ๋ฆฌํฐ์ OAuth 2.0์ ๊ตฌํํ ๊ตฌ๊ธ ๋ก๊ทธ์ธ์ ์ฐ๋ํ์ฌ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
5.1 ์คํ๋ง ์ํ๋ฆฌํฐ์ ์คํ๋ง ์ํ๋ฆฌํฐ Oauth2 ํด๋ผ์ด์ธํธ
5.2 ๊ตฌ๊ธ ์๋น์ค ๋ฑ๋ก
๋จผ์ ๊ตฌ๊ธ ์๋น์ค์ ์ ๊ท ์๋น์ค๋ฅผ ์์ฑํฉ๋๋ค.
์ฌ๊ธฐ์ ๋ฐ๊ธ๋ ์ธ์ฆ ์ ๋ณด(clientId์ clientSecret)๋ฅผ ํตํด์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ๊ณผ ์์ ์๋น์ค ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ผ๋ ๋ฌด์กฐ๊ฑด ๋ฐ๊ธ๋ฐ๊ณ ์์ํด์ผ ํฉ๋๋ค.
๊ตฌ๊ธ ํด๋ผ์ฐ๋ ํ๋ซํผ ์ฃผ์ (https://console.cloud.google.com/) ๋ก ์ด๋ํฉ๋๋ค
- ์ ํ๋ฆฌ์ผ์ด์ ์ด๋ฆ : ๊ตฌ๊ธ ๋ก๊ทธ์ธ ์ฌ์ฉ์์๊ฒ ๋ ธ์ถ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด๋ฆ์ ์ด์ผ๊ธฐํฉ๋๋ค
- ์ง์ ์ด๋ฉ์ผ : ์ฌ์ฉ์ ๋์ ํ๋ฉด์์ ๋ ธ์ถ๋ ์ด๋ฉ์ผ ์ฃผ์์ ๋๋ค. ๋ณดํต์ ์๋น์ค์ help ์ด๋ฉ์ผ ์ฃผ์๋ฅผ ์ฌ์ฉํ์ง๋ง, ์ฌ๊ธฐ์๋ ๋ ์์ ์ด๋ฉ์ผ ์ฃผ์๋ฅผ ์ฌ์ฉํ์๋ฉด ๋ฉ๋๋ค.
- Google API์ ๋ฒ์ : ์ด๋ฒ์ ๋ฑ๋กํ ๊ตฌ๊ธ ์๋น์ค์์ ์ฌ์ฉํ ๋ฒ์ ๋ชฉ๋ก์ ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ email/profile/openid์ด๋ฉฐ, ์ฌ๊ธฐ์๋ ๋ฑ ๊ธฐ๋ณธ ๋ฒ์๋ง ์ฌ์ฉํฉ๋๋ค. ์ด์ธ ๋ค๋ฅธ ์ ๋ณด๋ค๋ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด ๋ฒ์ ์ถ๊ฐ ๋ฒํผ์ผ๋ก ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
๋์ ํ๋ฉด ๊ตฌ์ฑ์ด ๋๋ฌ์ผ๋ฉด ํ๋ฉด ์ ์ผ ์๋์ ์ ์ฅ ๋ฒํผ์ ํด๋ฆญํ๊ณ ๋ค์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด OAuth ํด๋ผ์ด์ธํธ ID ๋ง๋ค๊ธฐ ํ๋ฉด์ผ๋ก ๋ฐ๋ก ์ด๋ํฉ๋๋ค. ํ๋ก์ ํธ ์ด๋ฆ์ ๋ฑ๋กํฉ๋๋ค.
ํ๋ฉด ์๋๋ก ๋ด๋ ค๊ฐ๋ฉด ๋ค์๊ณผ ๊ฐ์ด URL ์ฃผ์๋ฅผ ๋ฑ๋กํด์ผ ํฉ๋๋ค
์ฌ๊ธฐ์ [์น์ธ๋ ๋ฆฌ๋๋ ์ URI] ํญ๋ชฉ๋ง ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๊ฐ์ ๋ฑ๋กํฉ๋๋ค
์น์ธ๋ ๋ฆฌ๋๋ ์ URI
- ์๋น์ค์์ ํ๋ผ๋ฏธํฐ๋ก ์ธ์ฆ ์ ๋ณด๋ฅผ ์ฃผ์์ ๋ ์ธ์ฆ์ด ์ฑ๊ณตํ๋ฉด ๊ตฌ๊ธ์์ ๋ฆฌ๋ค์ด๋ ํธํ URL์ ๋๋ค
- ์คํ๋ง ๋ถํธ 2 ๋ฒ์ ์ ์ํ๋ฆฌํฐ์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก {๋๋ฉ์ธ}/login/oauth2/code/{์์ ์๋น์ค์ฝ๋}๋ก ๋ฆฌ๋ค์ด๋ ํธ URL์ ์ง์ํ๊ณ ์์ต๋๋ค
- ์ฌ์ฉ์๊ฐ ๋ณ๋๋ก ๋ฆฌ๋ค์ด๋ ํธ URL์ ์ง์ํ๋ Controller๋ฅผ ๋ง๋ค ํ์๊ฐ ์์ต๋๋ค. ์ํ๋ฆฌํฐ์์ ์ด๋ฏธ ๊ตฌํํด ๋์ ์ํ์ ๋๋ค
- ํ์ฌ๋ ๊ฐ๋ฐ ๋จ๊ณ์ด๋ฏ๋ก http://localhost:8080/login/oauth2/code/google๋ก๋ง ๋ฑ๋กํฉ๋๋ค
- AWS ์๋ฒ์ ๋ฐฐํฌํ๊ฒ ๋๋ฉด localhost ์ธ์ ์ถ๊ฐ๋ก ์ฃผ์๋ฅผ ์ถ๊ฐํด์ผํ๋ฉฐ, ์ด๊ฑด ์ดํ ๋จ๊ณ์์ ์งํํ๊ฒ ์ต๋๋ค
์์ฑ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ๋ค์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์์ฑ๋ ํด๋ผ์ด์ธํธ ์ ๋ณด๋ฅผ ๋ณผ ์ ์๊ณ ์์ฑ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํด๋ฆญํ๋ฉด ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ณผ ์ ์์ต๋๋ค
ํด๋ผ์ด์ธํธ ID์ ํด๋ผ์ด์ธํธ ๋ณด์ ๋น๋ฐ ์ฝ๋๋ฅผ ํ๋ก์ ํธ์์ ์ค์ ํ๊ฒ ์ต๋๋ค
- application-oauth ๋ฑ๋ก
4์ฅ์์ ๋ง๋ค์๋ application.properties๊ฐ ์๋ sec/main/resources/๋๋ ํ ๋ฆฌ์ application-oauth.properties ํ์ผ์ ์์ฑํฉ๋๋ค
5.3 ๊ตฌ๊ธ ๋ก๊ทธ์ธ ์ฐ๋ํ๊ธฐ
๊ตฌ๊ธ์ ๋ก๊ทธ์ธ ์ธ์ฆ์ ๋ณด๋ฅผ ๋ฐ๊ธ ๋ฐ์์ผ๋ ํ๋ก์ ํธ ๊ตฌํ์ ์งํํ์์ต๋๋ค.
๋จผ์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ด๋นํ ๋์์ธ์ธ User ํด๋์ค๋ฅผ ์์ฑํฉ๋๋ค.
ํจํค์ง๋ domain ์๋์ user ํจํค์ง๋ฅผ ์์ฑํฉ๋๋ค.
package study.springboot.domain.user;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import study.springboot.domain.BaseTimeEntity;
import javax.management.relation.Role;
@Getter
@NoArgsConstructor
@Entity
public class User extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String email;
@Column
private String picture;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role;
@Builder
public User(String name, String email, String picture, Role role){
this.name = name;
this.email = email;
this.picture = picture;
this.role = role;
}
public User update(String name, String picture){
this.name = name;
this.picture = picture;
return this;
}
public String getRoleKey(){
return this.role.getKey();
}
}
@Getter
@NoArgsConstructor
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private String picture
@Enumerated(EnumType.STRING)
@Builder
@Enumerated(EnumType.STRING)
- JPA๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ์ ์ฅํ ๋ Enum ๊ฐ์ ์ด๋ค ํํ๋ก ์ ์ฅํ ์ง๋ฅผ ๊ฒฐ์ ํฉ๋๋ค
- ๊ธฐ๋ณธ์ ์ผ๋ก๋ int๋ก ๋ ์ซ์๋ก ์ ์ฅ๋ฉ๋๋ค
- ์ซ์๋ก ์ ์ฅ๋๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ํ์ธํ ๋ ๊ทธ ๊ฐ์ด ๋ฌด์จ ์ฝ๋๋ฅผ ์๋ฏธํ๋์ง ์ ์๊ฐ ์์ต๋๋ค
- ๊ทธ๋์ ๋ฌธ์์ด (EnumType.STRING)๋ก ์ ์ฅ๋ ์ ์๋๋ก ์ ์ธํฉ๋๋ค
๊ฐ ์ฌ์ฉ์์ ๊ถํ์ ๊ด๋ฆฌํ Enum ํด๋์ค Role์ ์์ฑํฉ๋๋ค.
package study.springboot.domain.user;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum Role {
GUEST("ROLE_GUEST", "์๋"),
USER("ROLE_USER", "์ผ๋ฐ ์ฌ์ฉ์");
private final String key;
private final String title;
}
์คํ๋ง ์ํ๋ฆฌํฐ์์๋ ๊ถํ ์ฝ๋์ ํญ์ ROLE_์ด ์์ ์์ด์ผ๋ง ํฉ๋๋ค
๊ทธ๋์ ์ฝ๋๋ณ ํค ๊ฐ์ ROLE_GUEST, ROLE_USER ๋ฑ์ผ๋ก ์ง์ ํฉ๋๋ค
๋ง์ง๋ง์ผ๋ก User์ CRUD๋ฅผ ์ฑ ์์ง UserRepository๋ ์์ฑํฉ๋๋ค
package study.springboot.domain.user;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
findByEmail
- ์์ ๋ก๊ทธ์ธ์ผ๋ก ๋ฐํ๋๋ ๊ฐ ์ค email์ ํตํด ์ด๋ฏธ ์์ฑ๋ ์ฌ์ฉ์์ธ์ง ์ฒ์ ๊ฐ์ ํ๋ ์ฌ์ฉ์์ธ์ง ํ๋จํ๊ธฐ ์ํ ๋ฉ์๋์
User ์ํฐํฐ ๊ด๋ จ ์ฝ๋๋ฅผ ๋ชจ๋ ์์ฑํ์ผ๋ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ํ๋ฆฌํฐ ์ค์ ์ ์งํํ๊ฒ ์ต๋๋ค