728x90
320x100
⚡️ OAuth2-client 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
⚡️ facebook-developer에 등록
⚡️ application-oauth.yml 설정
spring:
security:
oauth2:
client:
registration:
facebook:
clientId:
client-secret:
scope:
- email
- public_profile
⚡️ securityConfig
package yerong.InstagramCloneCoding.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import yerong.InstagramCloneCoding.config.oauth.OAuth2DetailsService;
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfig{
private final OAuth2DetailsService oAuth2DetailsService;
@Bean
public BCryptPasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain (HttpSecurity http) throws Exception{
return http
.csrf(csrfConfig ->
csrfConfig.disable())
.authorizeHttpRequests(authorizationRequest ->
authorizationRequest
.requestMatchers(
AntPathRequestMatcher.antMatcher("/"),
AntPathRequestMatcher.antMatcher("/user/**"),
AntPathRequestMatcher.antMatcher("/image/**"),
AntPathRequestMatcher.antMatcher("/subscribe/**"),
AntPathRequestMatcher.antMatcher("/comment/**"),
AntPathRequestMatcher.antMatcher("/api/**")
).authenticated()
.anyRequest().permitAll()
)
.formLogin(formConfig ->
formConfig.loginPage("/auth/signin") //GET
.loginProcessingUrl("/auth/signin") //POST
.defaultSuccessUrl("/"))
.oauth2Login(oAuth -> oAuth
.userInfoEndpoint(userInfoEndpointConfig -> userInfoEndpointConfig
.userService(oAuth2DetailsService)))
.build();
}
}
- userInfoEndpoint() : oauth2로그인을 하면 최종 응답으로 회원 정보를 바로 받을 수 있음(코드 x)
⚡️ OAuth2DetailsService
package yerong.InstagramCloneCoding.config.oauth;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import yerong.InstagramCloneCoding.config.auth.PrincipalDetails;
import yerong.InstagramCloneCoding.domain.user.Role;
import yerong.InstagramCloneCoding.domain.user.User;
import yerong.InstagramCloneCoding.handler.exception.CustomException;
import yerong.InstagramCloneCoding.repository.user.UserRepository;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@Service
@Slf4j
@RequiredArgsConstructor
public class OAuth2DetailsService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
log.info("facebook 로그인");
OAuth2User oAuth2User = super.loadUser(userRequest);
System.out.println(oAuth2User.getAttributes());
Map<String, Object> userInfo = oAuth2User.getAttributes();
String username = "facebook_" + (String) userInfo.get("id");
String name = (String) userInfo.get("name");
String email = (String) userInfo.get("email");
String password = new BCryptPasswordEncoder().encode(UUID.randomUUID().toString());
Optional<User> findUserOptional = userRepository.findByUsername(username);
if(!findUserOptional.isPresent()){ //회원가입
User user = User.builder()
.username(username)
.name(name)
.email(email)
.password(password)
.role(Role.USER)
.build();
return new PrincipalDetails(userRepository.save(user), userInfo);
}
else{ //로그인
return new PrincipalDetails(findUserOptional.get(), userInfo);
}
}
}
- name, email은 페이스북에서 받아온 정보를 사용
- username은 페이스북으로 넘어온 id값을 이용해 중복되지 않게 랜덤으로 만들어줘야함
- password는 BCryptPasswordEncoder를 이용해 랜덤으로 만들어줘야 함
⚡️ PrincipalDetails
package yerong.InstagramCloneCoding.config.auth;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import yerong.InstagramCloneCoding.domain.user.User;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
@Getter
@Setter
public class PrincipalDetails implements UserDetails, OAuth2User {
private User user;
Map<String, Object> attributes;
public PrincipalDetails(User user){
this.user = user;
}
public PrincipalDetails(User user, Map<String, Object> attributes){
this.user = user;
this.attributes = attributes;
}
@Override
public Map<String, Object> getAttributes() {
return attributes;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collector = new ArrayList<>();
collector.add(() -> user.getRole().getKey());
return collector;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
- OAuth2User를 추가해줌
- JWT 방식이나 보안적으로는 약하지만, 기본적인 구현에는 성공하였당
- 추후에 jwt 방식으로 변환하는 방법으로 추가해보고 카카오로그인도 추가해볼 예정이다
PREV
728x90
반응형
'프로젝트 > 인스타그램 클론 코딩' 카테고리의 다른 글
[인스타그램 클론 코딩][Spring boot] 12. AOP 처리 (0) | 2024.03.01 |
---|---|
[인스타그램 클론 코딩][Spring boot] 11. 댓글 구현 (0) | 2024.03.01 |
[인스타그램 클론 코딩][Spring boot] 10. 좋아요 및 인기 페이지 구현 (0) | 2024.02.23 |
[인스타그램 클론 코딩][Spring boot] 9. 스토리(메인) 페이지 (0) | 2024.02.22 |
[인스타그램 클론 코딩][Spring boot] 8. 구독 정보 뷰 렌더링 (0) | 2024.02.21 |