728x90
구글, 페이스북, 네이버 로그인을 적용해보려고 한다.
지난 포스팅에 사용했던 코드에 이어서 작업하려한다.
https://devhooney.tistory.com/99
1. 구글 api console
https://console.cloud.google.com/apis/dashboard
- 프로젝트를 만들어준다.
- 사용자 인증정보 클릭하면 클라이언트 아이디와 시크릿 키(보안 비밀)가 있다.
- 승인된 리디렉션URI에는 꼭 저렇게 넣어주어야 한다. 만약 고유의 URL이 있을 경우 localhost:8080 대신 고유 값을 넣어주면 된다.
2. application.yml 수정
spring:
security:
oauth2:
client:
registration:
google:
client-id: 넣어준다.
client-secret: 넣어준다.
scope: email, profile
3. SecurityConfig 수정
@Autowired
private PrincipalOauth2UserService principalOauth2UserService;
// 해당 메소드의 리턴되는 오브젝트를 IoC로 등록해준다.
@Bean
public BCryptPasswordEncoder encodePwd() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated() // 인증만 도면 접근 가능
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/loginForm")
.loginProcessingUrl("/login") // /login 주소가 호출이 되면 시큐리티가 낚아채서 대신 로그인을 진행해준다.
.defaultSuccessUrl("/")
// 여기부터 추가됨
.and()
.oauth2Login()
.loginPage("/loginForm")
.userInfoEndpoint()
.userService(principalOauth2UserService)
;
return http.build();
}
4. 일반 로그인과 OAuth 로그인 구분
- 폴더구조 변경
- oauth 패키지 안에 PrincipalOauth2UserService 생성
- 여기서 OAuth 로그인 후 회원가입 진행
- DefaultOAuth2UserService 상속받아 메소드 재정의
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
private UserRepository userRepository;
// 구글로부터 받은 userRequest 데이터에 대한 후처리되는 함수
// 함수 종료 시 @AuthenticationPrincipal 어노테이션이 만들어진다.
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
// 구글 로그인 버튼 클릭 -> 구글 로그인창 -> 로그인 완료 -> 코드 리턴 -> 액세스 토큰 요청
// userRequest 정보 -> loadUser함수 호출 -> 구글로부터 회원 프로필을 받아준다.
OAuth2User oAuth2User = super.loadUser(userRequest);
OAuth2UserInfo oAuth2UserInfo = null;
if (userRequest.getClientRegistration().getRegistrationId().equals("google")) {
oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes());
} else {
System.out.println("Only Google");
}
String provider = Objects.requireNonNull(oAuth2UserInfo).getProvider();
String providerId = oAuth2UserInfo.getProviderId();
String username = provider + "_" + providerId;
String password = bCryptPasswordEncoder.encode("비밀번호");
String email = oAuth2UserInfo.getEmail();
String role = "RULE_USER";
User userEntity = userRepository.findByUsername(username);
if (userEntity == null) {
userEntity = User.builder()
.username(username)
.password(password)
.email(email)
.role(role)
.provider(provider)
.providerId(providerId)
.build();
userRepository.save(userEntity);
}
return new PrincipalDetails(userEntity, oAuth2User.getAttributes());
}
}
- User 클래스에 빌드패턴과 OAuth 회원가입 구분을 위한 수정
@Entity
@Getter
@Setter
// 추가됨
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private String email;
private String role; // ROLE_ADMIN, ROLE_USER
// 추가됨
private String provider;
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;
}
}
- oauth 패키지 안에 provider 패키지 추가 후 OAuth2UserInfo 인터페이스 추가
public interface OAuth2UserInfo {
String getProviderId();
String getProvider();
String getEmail();
String getName();
}
- GoogleUserInfo 클래스 추가
- OAUth2UserInfo 상속받아 메소드 재정의해준다.
- 구글 로그인 정보는 attributes()에 들어있다.(userRequest에 있는 것을 받음)
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");
}
}
- PrincipalDetails에 OAuth 관련 내용 추가
@Data
public class PrincipalDetails implements UserDetails, OAuth2User { // OAuth2User 추가
private User user; // 컴포지션
private Map<String, Object> attributes; // 추가됨
// 일반 로그인
public PrincipalDetails(User user) {
this.user = user;
}
// OAuth 로그인 // 추가됨
public PrincipalDetails(User user, Map<String, Object> attributes) {
this.user = user;
this.attributes = attributes;
}
@Override // 추가됨
public Map<String, Object> getAttributes() {
return attributes;
}
// 해당 User의 권한을 리턴하는 곳!
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add((GrantedAuthority) () -> user.getRole());
return collect;
}
@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() {
// 사이트에서 1년동안 회원이 로그인을 안했을 경우 휴면 계정으로 하기로 함.
// 현재 시간 - 로그인 시간 => 1년을 초과하면 return false;
return true;
}
@Override // 추가됨 -- 사용은 X
public String getName() {
return null;
}
}
- 실행 후 순환참조 오류가 발생한다면 SecurityConfig 수정
@Autowired
private PrincipalOauth2UserService principalOauth2UserService;
5. html 수정 후 테스트
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>로그인 페이지</title>
</head>
<body>
<h1>로그인 페이지</h1>
<hr>
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username"><br>
<input type="password" name="password" placeholder="Password"><br>
<button>로그인</button>
</form>
<a href="/oauth2/authorization/google">구글 로그인</a> <!-- 추가됨 -->
<a href="/joinForm">회원가입을 아직 하지 않으셨나요?</a>
</body>
</html>
- DB 조회
728x90
'개발 > Java&Kotlin' 카테고리의 다른 글
[Spring] 스프링시큐리티 OAuth2.0 적용해보기(Naver) (0) | 2022.09.02 |
---|---|
[Spring] 스프링시큐리티 OAuth2.0 적용해보기(Facebook) (0) | 2022.09.01 |
[Spring] 스프링시큐리티 예제 (2) | 2022.08.29 |
[JPA] LIKE 정리 (0) | 2022.08.24 |
[Java] 예외처리 (0) | 2022.08.23 |