์ด ๊ธ์ ์ธํ๋ฐ - ์ต์ฃผํธ ๊ฐ์ฌ์ ์คํ๋ง๋ถํธ ์ํ๋ฆฌํฐ & JWT ๊ฐ์์ ๋ด์ฉ์ ํฌํจํ๊ณ ์์ต๋๋ค.
์ ๋ฒ ํฌ์คํ ์์๋ spring-security๋ฅผ ์ฌ์ฉํ์ฌ, ์ผ๋ฐ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๊ฐ๋จํ๊ฒ ์์๋ณด์์ต๋๋ค.
์ด๋ฒ ํฌ์คํ ์์ , ์ ๋ฒ ๊ธฐ๋ฅ์ ์ด์ ๋ง๋ถ์ฌ์ ์์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.(์ ๋ฒ ํฌ์คํ ์ฐธ๊ณ )
๋จผ์ ๊ตฌ๊ธ ์ฐฝ์ google api ์ฝ์์ ์ ๋ ฅํด์ ๋ค์ด๊ฐ๋๋ค.
๋ค์, ์๋๊ทธ๋ฆผ์ฒ๋ผ ๋ณด์ด๋ ๊ณณ์ ์๋๋ฐฉํฅ ํ์ดํ๋ฅผ ํด๋ฆญํด ์ํ๋ก์ ํธ๋ฅผ ์์ฑํด์ค๋๋ค!
๋ง๋ค๊ธฐ๋ฅผ ํตํด ์์ฑํ ๋ค, ์๋ ๊ทธ๋ฆผ์ฒ๋ผ ์์ฑํ ํ๋ก์ ํธ๋ฅผ ์ ํํด์ค๋๋ค.
๋ค์ ์ผ์ชฝ์ OAuth ๋์ ํ๋ฉด ํญ์์ User Type์ ์ธ๋ถ๋ก ์ค์ ํด์ค๋๋ค.
๋ง๋ค๊ธฐ๋ฅผ ๋๋ฅด๋ฉด, ์๋์ฒ๋ผ OAuth ๋์ ํ๋ฉด์ด ๋จ๋๋ฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ด๋ฆ๋ง ์ ์ด์ฃผ๊ณ ํ๋จ์ ์ ์ฅ์ ๋๋ฌ์ค๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด์ ์ผ์ชฝ์ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ํญ์ ๋ค์ด๊ฐ์, ์๋จ์ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ๋ง๋ค๊ธฐ๋ฅผ ํด๋ฆญํฉ๋๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ์ค์ ํ๊ณ , ์ด๋ฆ๊ณผ ์น์ธ๋ ๋ฆฌ๋๋ ์ URI๋ฅผ ์ค์ ํฉ๋๋ค.
์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ด ๋ฆฌ๋๋ ์ URI ๊ฒฝ๋ก์ธ๋ฐ,
์น์ธ๋ ๋ฆฌ๋๋ ์ URI์๋ "http://localhost:8080/login/oauth2/code/google" ์ ์ ๋ ฅํ๋ค. (๋นจ๊ฐ์์ ์ ํด์ง ๋ด์ฉ์ ๋๋ค)
์คํ๋ง ๋ถํธ ๊ณต์๋ฌธ์์ ๋์์๋ ๋ด์ฉ์ ๋๋ค.
https://docs.spring.io/spring-security/reference/servlet/oauth2/login/core.html
๊ทธ๋ฆฌ๊ณ ๋ง๋ค๊ธฐ๋ฅผ ๋๋ฅด๋ฉด OAuthํด๋ผ์ด์ธํธ๊ฐ ์์ฑ๋ฉ๋๋ค!
๋ค์ ํ๋ก์ ํธ๋ก ๋์๊ฐ์, pom.xml์ OAuth2 ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํด์ค๋๋ค.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
๊ทธ ๋ค์, application.yml์์ ์๋๋ฅผ ์ถ๊ฐํด์ค๋๋ค(springํ์๋ก).
spring:
security:
oauth2:
client:
registration:
google:
client-id: ๋ฐ๊ธ๋ฐ์ ํด๋ผ์ด์ธํธID
client-secret: ๋ฐ๊ธ๋ฐ์ ํด๋ผ์ด์ธํธ ๋ณด์๋น๋ฐ ๋ฒํธ
scope:
- email
- profile
์ฌ๊ธฐ๊น์ง ๊ตฌ๊ธAPI์ ์ฐ๋์ด์๊ณ , ์ด์ ๋ฒํผ์ ํตํด ๊ธฐ๋ฅ ๊ตฌํ์ ํ๋๋ก ํด๋ณด๊ฒ ์ต๋๋ค.
Controller์ GetMapping์ผ๋ก ๋ฆฌ๋๋ ์ ์ฃผ์๋ฅผ ์ ์ด์คํ์์์(OAuth2๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์์ํด์ค, ๋์ ์ฃผ์๋ ๊ณ ์ )
loginForm.html์ ๋ฒํผ ํ๋๋ฅผ ์์ฑํด์ฃผ๊ณ (์ฃผ์!! oauth2 client ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฃผ์ ๋ฐ์๊ธฐ ๋๋ฌธ์, href ์ฃผ์๋ ๊ณ ์ !)
<a href="/oauth2/authorization/google">๊ตฌ๊ธ ๋ก๊ทธ์ธ</a>
๊ทธ๋ฆฌ๊ณ SecurityConfig ํด๋์ค์ ์ด๋ ๊ฒ ์์ ํด์ค๋๋ค(์ผ๋ฐ๋ก๊ทธ์ธ ์ค์ ๋ถ๋ถ์์ defaultSuccessUrl๋ฐ๋ถํฐ ์ถ๊ฐํ์)
์ง๊ธ principalOauth2UserService ํด๋์ค(๊ตฌ๊ธ ๋ก๊ทธ์ธ ํ, ํ์ฒ๋ฆฌ๋ฅผ ๋ด๋นํ๋ ํด๋์ค)๋ ์์ฑํ์ง ์์์ผ๋ ๋นจ๊ฐ์ค์ด ๋ ๋ ๋ฌด์ํฉ๋๋ค.
@Configuration
@EnableWebSecurity // ์คํ๋ง ์ํ๋ฆฌํฐ ํํฐ๊ฐ ์คํ๋ง ํํฐ์ฒด์ธ์ ๋ฑ๋ก์ด ๋จ
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) // secured ์ด๋
ธํ
์ด์
ํ์ฑํ, preAuthorize, postAuthorize ์ด๋
ธํ
์ด์
ํ์ฑํ
public class SecurityConfig {
// @Autowired์ ์์ฑ์ ์ฃผ์
๋ฐฉ์
private PrincipalOauth2UserService principalOauth2UserService;
@Autowired
public SecurityConfig(PrincipalOauth2UserService principalOauth2UserService) {
this.principalOauth2UserService = principalOauth2UserService;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated() // ์ธ์ฆ๋ง ๋๋ฉด ๋ค์ด๊ฐ ์ ์๋ ์ฃผ์
.antMatchers("/manager/**").access("hasAnyRole('ROLE_MANAGER','ROLE_ADMIN')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/loginForm")
.loginProcessingUrl("/login") // /login ์ฃผ์๊ฐ ํธ์ถ์ด ๋๋ฉด ์ํ๋ฆฌํฐ๊ฐ ๋์์ฑ์ ๋์ ๋ก๊ทธ์ธ์ ์งํํด์ค
.defaultSuccessUrl("/")
.and()
.oauth2Login()
.loginPage("/loginForm")
.userInfoEndpoint() //oauth2Login์ ์ฑ๊ณตํ๋ฉด principalOauth2UserService์์ ์ค์ ์ ์งํํ๊ฒ ๋ค๋ ์๋ฏธ
.userService(principalOauth2UserService); // ๊ตฌ๊ธ ๋ก๊ทธ์ธ์ด ์๋ฃ๋ ๋ค์ ํ์ฒ๋ฆฌ๊ฐ ํ์ํจ. 1. ์ฝ๋๋ฐ๊ธฐ(์ธ์ฆ), 2. ์์ธ์คํ ํฐ, 3. ์ฌ์ฉ์ํ๋กํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ณ 4. ๊ทธ ์ ๋ณด๋ฅผ ํ ๋๋ก ํ์๊ฐ์
์ ์๋์ผ๋ก ์งํ์ํค๊ธฐ๋ ํจ
return http.build(); // Tip. ๊ตฌ๊ธ๋ก๊ทธ์ธ์ด ์๋ฃ๋๋ฉด ์ฝ๋๋ฅผ ๋ฐ๋๊ฒ ์๋, (์์ธ์คํ ํฐ + ์ฌ์ฉ์ํ๋กํ์ ๋ณด๋ฅผ ๋ฐ์)
}
}
๋ค์ IndexController์ @GetMapping("/user")๋ถ๋ถ์ ์๋์ ๊ฐ์ด ์์ ํด์ค๋๋ค.
@GetMapping("/user")
public @ResponseBody String user(@AuthenticationPrincipal PrincipalDetails principalDetails) {
System.out.println("principalDetails : " + principalDetails.getUser());
return "user";
}
์ด์ principalOauth2UserService ํด๋์ค๋ฅผ ์์ฑํฉ๋๋ค!
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
public PrincipalOauth2UserService(BCryptPasswordEncoder bCryptPasswordEncoder) {
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Autowired
private UserRepository userRepository;
// ๊ตฌ๊ธ๋ก ๋ถํฐ ๋ฐ์ userReuqest ๋ฐ์ดํฐ์ ๋ํ ํ์ฒ๋ฆฌ๋๋ ํจ์
// ํจ์ ์ข
๋ฃ์ @AuthenticationPrincipal ์ด๋
ธํ
์ด์
์ด ๋ง๋ค์ด์ง๋ค.
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
System.out.println("getClientRegistration: " + userRequest.getClientRegistration()); // registrationId๋ก ์ด๋ค OAuth๋ก ๋ก๊ทธ์ธ ํ๋์ง ํ์ธ๊ฐ๋ฅ.
System.out.println("getAccessToken: " + userRequest.getAccessToken().getTokenValue());
OAuth2User oauth2User = super.loadUser(userRequest);
// ๊ตฌ๊ธ๋ก๊ทธ์ธ ๋ฒํผ ํด๋ฆญ -> ๊ตฌ๊ธ๋ก๊ทธ์ธ์ฐฝ -> ๋ก๊ทธ์ธ์ ์๋ฃ -> code๋ฅผ ๋ฆฌํด(OAuth-Client๋ผ์ด๋ธ๋ฌ๋ฆฌ) -> AccessToken์์ฒญ
// userRequest ์ ๋ณด -> loadUserํจ์ ํธ์ถ -> ๊ตฌ๊ธ๋ก๋ถํฐ ํ์ํ๋กํ ๋ฐ์์ค๋ค.
System.out.println("getAttributes: " + super.loadUser(userRequest).getAttributes());
// ํ์๊ฐ์
์งํ
OAuth2UserInfo oAuth2UserInfo = null;
if(userRequest.getClientRegistration().getRegistrationId().equals("google")) {
System.out.println("๊ตฌ๊ธ ๋ก๊ทธ์ธ ์์ฒญ");
oAuth2UserInfo = new GoogleUserInfo(oauth2User.getAttributes());
} else {
System.out.println("์ฐ๋ฆฌ๋ ๊ตฌ๊ธ๋ง ์ง์ํด์ ใ
ใ
ใ
");
}
String provider = oAuth2UserInfo.getProvider();
String providerId = oAuth2UserInfo.getProviderId();
String username = provider+"_"+providerId;
String password = bCryptPasswordEncoder.encode("๊ฒ์ธ๋ฐ์ด");
String email = oAuth2UserInfo.getEmail();
String role = "ROLE_USER";
// ๊ธฐ์กด์ ์๋ ํ์์ด๋ฉด ์งํ์ํ๊ฒ ํด์ผํจ
User userEntity = userRepository.findByUsername(username);
if(userEntity == null) {
System.out.println("๋ก๊ทธ์ธ์ด ์ต์ด์
๋๋ค.");
userEntity = User.builder()
.username(username)
.password(password)
.email(email)
.role(role)
.provider(provider)
.providerId(providerId)
.build();
userRepository.save(userEntity);
} else {
System.out.println("๋ก๊ทธ์ธ์ ์ด๋ฏธ ํ์ ์ด ์์ต๋๋ค. ๋น์ ์ ์๋ํ์๊ฐ์
์ด ๋์ด ์์ต๋๋ค.");
}
return new PrincipalDetails(userEntity, oauth2User.getAttributes());
}
}
์ฌ๊ธฐ์ ๋นจ๊ฐํ์๊ฐ ๋จ๋ ๋ถ๋ถ์ OAuth2UserInfo์ GoogleUserInfo, .builder()๋ถ๋ถ์ธ๋ฐ ํ๋์ฉ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค!
๋จผ์ .builder()๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Userํด๋์ค๋ฅผ ๋ค์ด๊ฐ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์๋์ ๊ฐ์ด ๊ธฐ์กด Userํด๋์ค๋ฅผ ์์ ํฉ๋๋ค.
@Entity
@Data
@NoArgsConstructor //Default Constructor
public class User {
@Id //primary key
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private String email;
private String role; //ROLE_USER, ROLE_MANAGER, ROLE_ADMIN
private String provider; // ์์
๋ก๊ทธ์ธ(Google, Naver, Facebook ๋ฑ)
private String providerId;
@CreationTimestamp
private Timestamp createDate;
@Builder
public User(String username, String password, String email, String role, String provider, String providerId,
Timestamp createDate) {
this.username = username;
this.password = password;
this.email = email;
this.role = role;
this.provider = provider;
this.providerId = providerId;
this.createDate = createDate;
}
}
๋ค์ OAuth2UserInfo ์ธํฐํ์ด์ค๋ฅผ ์์ฑํฉ๋๋ค.
public interface OAuth2UserInfo {
String getProviderId();
String getProvider();
String getEmail();
String getName();
}
๊ทธ๋ฆฌ๊ณ ๊ตฌ๊ธ์์ ๋ฐ์์จ ์ ์ ์ ๋ณด๋ฅผ ๋ด๊ธฐ ์ํ GoogleUserInfo ํด๋์ค๋ฅผ ์์ฑํฉ๋๋ค(OAuth2UserInfo ์ธํฐํ์ด์ค ๊ตฌํ).
public class GoogleUserInfo implements OAuth2UserInfo {
private Map<String, Object> attributes; // getAttributes()
public GoogleUserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public String getProviderId() {
return (String) attributes.get("sub");
}
@Override
public String getProvider() {
return "google";
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
์ฌ๊ธฐ๊น์ง ์์ฑํ๊ณ ๊ตฌ๊ธ๋ก๊ทธ์ธ์ ์งํํ๋ฉด, ์ฑ๊ณต์ ์ผ๋ก ํ์๊ฐ์ ์ด ๋๋ฉด์ ๋ก๊ทธ์ธ๋๊ณ DB์๋ ๊ฐ์ด ์ ๋๋ก ๋ค์ด๊ฐ๋ ๊ฑธ ํ์ธํ ์ ์์ต๋๋ค!
'Languages | Frameworks > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
@ControllerAdvice๋ฅผ ํตํ ์์ธ์ฒ๋ฆฌ ๋ถ๋ฆฌ, ํตํฉํ๊ธฐ (0) | 2022.12.20 |
---|---|
[IntelliJ] ์์ฃผ์ฐ๋ ๋จ์ถํค ์ ๋ฆฌ (0) | 2022.11.21 |
@Autowired, @Component, @Service, @Repository ๋ฑ ์คํ๋ง ์ด๋ ธํ ์ด์ ์ ๊ดํด.. (0) | 2022.11.02 |
[Springboot] Spring-security ์ ์ฉ๊ธฐ(1) - ์ผ๋ฐ๋ก๊ทธ์ธ (0) | 2022.10.25 |
[Spring] ์ธ์ ํ์์์ (0) | 2022.06.02 |