IT 프로젝트/쇼핑몰 만들기 [Spring Boot] 스프링 부트 프로젝트/쇼핑몰 만들기 - KakaoLogin(카카오 로그인)

김태현·2023년 10월 7일
post-thumbnail

전 시간에는 이메일을 본인인증을 구현하였다. 이번시간에는 소셜 로그인인 카카오 로그인을 구현하였다.

oauth2 권한 인증방식을 사용하여 카카오 로그인을 구현하였다.

SpringBoot Social Login(Kakao) 구현

1. 카카오 개발자 센터에서 필요한 애플리케이션 설정

  • 애플리케이션 추가

  • 앱 키 확인

    사용할 앱 키 및 관리자 키를 확인한다.

  • 카카오 로그인 활성화 및 redirectUrl 설정
    로그인을 활성화 하고 URI를 등록한다.

  • 카카오 로그인시 필요한 정보를 동의받는 항목 설정

  • 로그아웃 redirectURI 설정

2. pom.xml추가

<!-- Social Login API 추가 -->
<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
			

3. SecurityConfig 설정에 oauth2를 추가한다.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
	@Autowired
	private UserLogoutSucessHandler userLogoutSucessHandler;
	
	@Autowired
	private MemberSocialService memberSocialService;
	
	
	@Bean
	SecurityFilterChain fiterChain(HttpSecurity httpSecurity)throws Exception{
		
		중략...
		
				.and()
			.oauth2Login() //Social Login 설정
				.userInfoEndpoint()
				.userService(memberSocialService)
				
				;

		
		return httpSecurity.build();
	}	

}

4. application.properties 에 Social로그인 관련 코드추가한다.

#Social Login
# Rest Api key
spring.security.oauth2.client.registration.kakao.client-id=#{RestAPIKey}
# Secret Key
spring.security.oauth2.client.registration.kakao.client-#{AdminKey}
# Redirect URL 카카오에 설정한 redirectURL
spring.security.oauth2.client.registration.kakao.redirect-uri = #{baseUrl}/#{action}/oauth2/code/#{registrationId}
# 고정값
spring.security.oauth2.client.registration.kakao.authorization-grant-type=authorization_code
# 회원가입시 추가로 가져 오는 유저 정보 항목
spring.security.oauth2.client.registration.kakao.scope=profile_nickname,account_email
# Social Login 업체 명
spring.security.oauth2.client.registration.kakao.client-name=Kakao
# 인증 요청시 메서드 형식
spring.security.oauth2.client.registration.kakao.client-authentication-method=POST


## Provider
spring.security.oauth2.client.provider.kakao.authorization-uri=https://kauth.kakao.com/oauth/authorize
spring.security.oauth2.client.provider.kakao.token-uri= https://kauth.kakao.com/oauth/token
spring.security.oauth2.client.provider.kakao.user-info-uri=https://kapi.kakao.com/v2/user/me
spring.security.oauth2.client.provider.kakao.user-name-attribute=id

5. JSP에 카카오 인증 링크버튼 생성

/* URL 주소는 변경 불가 */
<a href="/oauth2/authorization/kakao">KakaoLogin</a>

6. SocialService

JSP에서 로그인 요청을 하면
kakao 로그인 창으로 연결 
ID와 PASSWORD를 입력하고 처음이면 추가 정보를 입력하고 진행
그 결과가 SocialService로 자동 실행 함



@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class MemberSocialService extends DefaultOAuth2UserService{
	
	@Autowired
	private MemberDAO memberDAO;

	@Override
	public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
	/* Social Client 정보 (Kakao, Google등 등록 정보 */
		oAuth2UserRequest.getClientRegistration()
		{
			registrationId='kakao', 
			clientId='유저정보가 아닌 개발자등록 정보', 
			clientSecret='등록한 Secret key', 
			clientAuthenticationMethod=org.springframework.security.oauth2.core.ClientAuthenticationMethod@2590a0, 
			authorizationGrantType=org.springframework.security.oauth2.core.AuthorizationGrantType@5da5e9f3, 
			redirectUri='{baseUrl}/{action}/oauth2/code/{registrationId}', 
			scopes=[profile, account_email], 
			providerDetails=org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails@50428c74, 
			clientName='Kakao'		
		}
		System.out.println(userRequest);
		ClientRegistration registration=userRequest.getClientRegistration();
		OAuth2User user = super.loadUser(userRequest);
		/* OAuth2User를 리턴하면 Spring Security에 들어감
			 기존에 사용하던 UserDetail(MemberVO)와 구성이 다르므로 사용할 때 Error 발생 확률이 높음
			 UserDetail과 비슷한 형식으로 UserVO(OAuth2User 상속)를 만들어서 리턴 해야 함
		 */
		return this.socialJoinCheck(userRequest);
	}
	private OAuth2User socialJoinCheck(OAuth2UserRequest auth2UserRequest) {
		//DB에서 조회 후 회원 추가 또는 회원정보(Role) 조회
		//Kakao에서 받은 정보를 MemberVO로 변경 
		//카카오의 동의항목을 나중에 추가시 코드 변경.
		//동의항목 현재 : 닉네임 -> 사업자 등록 후 : 생일,카카오계정(),성별 등
		//현재 임시방편으로 선택사항의 kakao_account에 있는 email을 id로 넣기, 
		//		비밀번호 랜덤uuid : 비밀번호 검증은 카카오 서버에서 검증 및 로그인
		OAuth2User user= super.loadUser(auth2UserRequest);
		Map<String, Object> map = user.getAttributes();

		//nickName만 있음, 나중에 카카오톡에서 권한 인가받고 코드 추가예정
		HashMap<String, Object> m =(HashMap<String, Object>)map.get("properties");
		MemberVO memberVO = new MemberVO();
		memberVO.setAttributes(map);
		memberVO.setName(m.get("nickname").toString());
		
		//임시방편 선택사항 카카오계정-email내역을 아이디로 설정
		m= (HashMap<String,Object>) map.get("kakao_account");
		memberVO.setAccountId(m.get("email").toString());
		memberVO.setEMail(m.get("email").toString());
		//DB 조회
		//같은 아이디의 계정이 있다면 
		MemberVO tempVO = new MemberVO();
		try {
			tempVO = memberDAO.getMemberLogin(memberVO);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if(tempVO!=null) {
			return tempVO;
		}
		List<RoleVO> roleVOs = new ArrayList<>();
		RoleVO roleVO = new RoleVO();
		roleVO.setName("ROLE_USER");
		roleVOs.add(roleVO);
		
		
		// 새로운 유저 DB에 넣기
		memberVO.setRoleVOs(roleVOs);
		memberVO.setStatus(true);
		memberVO.setJoinType("Kakao");
		//랜덤한 비번 넣기(Null Point Exception 방지) 검증은 카카오에서 하기 때문에 사실상 필요없는 비밀번호
		UUID uuid = UUID.randomUUID(); // 랜덤한 UUID 생성ㅜ
        String key = String.valueOf(uuid);
		memberVO.setPassword(key);
		try {
			memberDAO.setMemberJoin(memberVO);
			//멤버 ROLE 넣기
			Map<String, Object> roleMap = new HashMap<>();
			roleMap.put("roleId", 3);
			roleMap.put("memberId", memberVO.getId());
			memberDAO.setMemberRole(roleMap);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return memberVO;
	}
	
}

7. Oauth2User 인터페이스 구현

SecurityContext의 Authentication내에 저장할 수 있는 객체의 타입은
UserDetails 타입과 OAuth2User 타입으로 정해 져 있다

일반 로그인의 경우 UserDetails
Social 로그인의 경우 OAuth2User

어떤 형식으로 로그인 했는지 판별 후 2개 중 하나를 사용하지만 JSP에서 사용할 때 두개 구분하는 코드를 작성해야 함

번거로움을 방지하기 위해 UserDetails 와 OAuth2User 둘다 구현하는 VO를 만들어 사용

@Data
public class MemberVO implements UserDetails,OAuth2User{

	private Long id;
	@NotBlank
	private String accountId;
	@NotBlank
	private String password;
	
	@NotBlank
	private String passwordCheck;
	
	private String name;
	private String phone;
	private String eMail;
	private Boolean marketing;
	private Boolean status;
	private String joinType;
	private Timestamp regDate;
	private Timestamp updateDate;
	private Timestamp loginDate;
	private List<RoleVO> roleVOs;
	
	
	
	//OAuth2User, token 정보 저장
	private Map<String, Object> attributes;


	//OAuth2User
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		List<GrantedAuthority> authorities = new ArrayList<>();
		for(RoleVO roleVO:roleVOs) {
			authorities.add(new SimpleGrantedAuthority(roleVO.getName()));
		}
		
		return authorities;
	}


	@Override
	public boolean isAccountNonExpired() {
		// // TODO Auto-generated method stub
		// 계정의 만료 여부
		// true : 만료 안됨
		// false : 만료 됨, 로그인 안됨
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		// 계정 잠김 여부
				// true : 잠기지 않음
				// false : 잠김, 로그인 안됨
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		// password 만료 여부
				// true : 만료 안됨
				// false : 만료 됨, 로그인 안됨
		return true;
	}

	@Override
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		// 계정 사용 여부
				// true : 계정 활성화
				// false : 계정 비활성화, 로그인 안됨
		return this.status;
	}

	@Override
	public String getUsername() {
		// TODO Auto-generated method stub
		return this.accountId;
	}
		
}

7. 로그인 처리

  • 로그인 요청 경로는 고정(임의의 경로로 변경 불가)
    /oauth2/authorization/kakao

해당 링크로 가면 카카오 서버에서 로그인 하여 로그인이 정상적으로 완료되면 완료 토큰을 받는다.

  • 로그인 처리
    -- 로그인 요청을 하면 Kakao Login 화면이 나오고 ID, PW 입력하면
    -- DefaultOAuth2UserService의 loadUser가 호출 됨
    -- 인증과정, 추가 정보등을 추가 호출 없이 모두 가져 옴
    -- 개발자는 이 정보를 이용해서 회원가입인지, 로그인 인지 판단 해서
    -- 회원가입이면 DB에 추가 하고, 로그인 이면 추가 정보를 조회 해야 함

  • OAuth2User를 리턴

8. 로그아웃

로그아웃은 크게 2가지 종류
단순로그아웃은 토큰을 만료 시키지만, 사용자 정보가 웹브라우저에 남아 있어
다시 로그인을 할 때 ID,PW입력 없이 바로 로그인 됨
공공장소등에서 사용은 위험하기 때문에 서비스에서 로그아웃하고, 카카오계정도 로그아웃하거나
카카오계정과 함께 로그아웃 해야 함

  • 기본정보
    POST /v1/user/logout HTTP/1.1
    Host: kapi.kakao.com
    Authorization: Bearer ${ACCESS_TOKEN}/KakaoAK ${APP_ADMIN_KEY}

  • UserLogoutSucessHandler

@Slf4j
@Component
public class UserLogoutSucessHandler implements LogoutSuccessHandler{
	
	
	//개발자가 SecurityConfig 파일에서 new UserLogoutSucessHandler()로 직접 객체 생성히면 autowired는 먹지 X
	//스프링이 대신 해주는 기능이기 때문
	
	@Value("${spring.security.oauth2.client.registration.kakao.client-id}")
	private String restKey;
//	로그아웃 시 kakao의 정보도 로그아웃.
	@Override
	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
			throws IOException, ServletException {
		String redirectUrl="/";
		MemberVO memberVO= (MemberVO)authentication.getPrincipal();
		if(memberVO.getJoinType().equals("Kakao")) {
			redirectUrl="https://kauth.kakao.com/oauth/logout?client_id="+restKey+"&logout_redirect_uri=http://localhost/";
		}
		response.sendRedirect(redirectUrl);
	}
}



탈퇴 구현은 홈페이지의 다른 기능들 구현 후 최종적으로 Member 수정 하면서 네이버 로그인 과 함께 구현할 계획

의뢰자 : 아마이저 쇼핑몰(링크)

profile
주니어개발자

0개의 댓글