애플리케이션 추가

앱 키 확인

사용할 앱 키 및 관리자 키를 확인한다.
카카오 로그인 활성화 및 redirectUrl 설정
로그인을 활성화 하고 URI를 등록한다.
카카오 로그인시 필요한 정보를 동의받는 항목 설정

로그아웃 redirectURI 설정

<!-- Social Login API 추가 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
@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();
}
}
#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
/* URL 주소는 변경 불가 */
<a href="/oauth2/authorization/kakao">KakaoLogin</a>
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;
}
}
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;
}
}
해당 링크로 가면 카카오 서버에서 로그인 하여 로그인이 정상적으로 완료되면 완료 토큰을 받는다.

로그인 처리
-- 로그인 요청을 하면 Kakao Login 화면이 나오고 ID, PW 입력하면
-- DefaultOAuth2UserService의 loadUser가 호출 됨
-- 인증과정, 추가 정보등을 추가 호출 없이 모두 가져 옴
-- 개발자는 이 정보를 이용해서 회원가입인지, 로그인 인지 판단 해서
-- 회원가입이면 DB에 추가 하고, 로그인 이면 추가 정보를 조회 해야 함
OAuth2User를 리턴
로그아웃은 크게 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 수정 하면서 네이버 로그인 과 함께 구현할 계획