728x90
반응형
SMALL
리스너 오너 는 서비스 홈페이지에서 로그인을 요구하는 사용자이다.
홈페이지의 로그인 요청을 하면 그 요청은 서비스 서버로, 서버는 로그인 페이지를 전달해준다.
리스너 오너에서 네이버 로그인을 요청하면 네이버 로그인 페이지로 이동이 된다.
동의 절차 창이 뜰것이고, 인증이 통과되었으면 네아로API 신청에서 등록한 서버의 URL 주소로 인증이 완료되었다는 의미로서 Code가 보내질것이다.
인증완료 후 네이버서버에 있는 로그인 사용자의 정보를 가져오기 위해 http://xxx.naver.com/oauth2 ~~ 의 주소로 다시 이동한다.(주소값에는 클라이언트의 id,비번값 같은것이 추가로 들어가있다.)
주소값이 정확하면 네이버 OAuth 서버는 유저정보를 가져올수있는 AccessToken을 부여한다.
클라이언트는 이제 네이버의 유저정보를 가져올수 있고 이 정보를 클라이언트의 db서버에 담아 로그인을 최종 진행할수있다.
1.네아로 신청하기
https://developers.naver.com/docs/login/overview/overview.md
해당 주소로 이동하여 오픈API 이용 신청을 누르자.
사용 API 에서 가져오고 싶은 유저정보를 선택한다.
로그인 오픈 API 서비스 환경 에서 서비스 URL은 네이버로그인 을 하는 시작페이지를 넣고
Callback URL은 CODE 를 받을 주소를 넣자
애플리케이션이 등록된것을 확인할 수 있다
애플리케이션을 눌러보면 ClientID 와 Secret 값 을 확인 할 수 있으며
로고 이미지는 네이버 로그인 동의 창에서 떠야 될 이미지이기 때문에 원하는 이미지를 넣어주자
2.POM.XML 의존성 설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<!-- 소셜 로그인(네이버) -->
<dependency>
<groupId>com.github.scribejava</groupId>
<artifactId>scribejava-apis</artifactId>
<version>2.8.1</version>
</dependency>
<!-- naver 로그인 -->
<dependency>
<groupId>com.github.scribejava</groupId>
<artifactId>scribejava-core</artifactId>
<version>2.8.1</version>
</dependency>
<!-- 제이슨 파싱 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<!-- 제이슨 파싱 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.5</version>
|
cs |
네이버 로그인 개발 가이드를 보면
유저 정보를 가져올때 JSON 형식으로 가져오는걸 확인할수 있다.
JSON 관련 의존성 설정과 네아로 에 필요한 설정을 추가해주자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<!-- 스프링 시큐리티 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!-- 스프링 시큐리티 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!-- 스프링 시큐리티 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!-- 스프링 시큐리티 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>5.0.6.RELEASE</version>
|
cs |
필자는 시큐리티를 쓰고있기 때문에 시큐리티 의존성 또한 추가해주자.
3.클래스 파일 만들기
네이버 서버에서 정보를 가져오는 서비스코드 2개와
컨트롤러 단 1개를 작성할 것이다.
NaverLoginApi.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package org.yoon.auth.Naver;
import com.github.scribejava.core.builder.api.DefaultApi20;
public class NaverLoginApi extends DefaultApi20{
protected NaverLoginApi() {}
private static class InstanceHolder{
private static final NaverLoginApi INSTANCE = new NaverLoginApi();
}
public static NaverLoginApi instance() {
return InstanceHolder.INSTANCE;
}
//사용자 접근권한을 받는 주소
@Override
public String getAccessTokenEndpoint() {
return "https://nid.naver.com/oauth2.0/token?grant_type=authorization_code";
}
//네이버 사용자 동의란 뜨는 주소
@Override
protected String getAuthorizationBaseUrl() {
return "https://nid.naver.com/oauth2.0/authorize";
}
}
|
cs |
scribejava 에서 지원하는 DefaultApi20을 구현해서 쓸것이다.
NaverLogin.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
package org.yoon.auth.Naver;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth20Service;
import org.yoon.auth.Naver.*;
public class NaverLogin {
/* 인증 요청문을 구성하는 파라미터 */
//client_id: 애플리케이션 등록 후 발급받은 클라이언트 아이디
//response_type: 인증 과정에 대한 구분값. code로 값이 고정돼 있습니다.
//redirect_uri: 네이버 로그인 인증의 결과를 전달받을 콜백 URL(URL 인코딩). 애플리케이션을 등록할 때 Callback URL에 설정한 정보입니다.
//state: 애플리케이션이 생성한 상태 토큰
private final static String CLIENT_ID = "클라이언트 아이디";
private final static String CLIENT_SECRET = "클라이언트 비밀번호";
private final static String REDIRECT_URI = "http://localhost:8033/member/logincallback";
private final static String SESSION_STATE = "oauth_state";
/* 프로필 조회 API URL */
private final static String PROFILE_API_URL = "https://openapi.naver.com/v1/nid/me";
/* 네이버 아이디로 인증 URL 생성 Method */
public String getAuthorizationUrl(HttpSession session) {
/* 세션 유효성 검증을 위하여 난수를 생성 */
String state = generateRandomString();
/* 생성한 난수 값을 session에 저장 */
setSession(session, state);
/* Scribe에서 제공하는 인증 URL 생성 기능을 이용하여 네아로 인증 URL 생성 */
OAuth20Service oauthService = new ServiceBuilder().apiKey(CLIENT_ID).apiSecret(CLIENT_SECRET)
.callback(REDIRECT_URI).state(state) // 앞서 생성한 난수값을 인증 URL생성시 사용함
.build(NaverLoginApi.instance());
return oauthService.getAuthorizationUrl();
}
/* 네이버아이디로 Callback 처리 및 AccessToken 획득 Method */
public OAuth2AccessToken getAccessToken(HttpSession session, String code, String state) throws IOException {
/* Callback으로 전달받은 세선검증용 난수값과 세션에 저장되어있는 값이 일치하는지 확인 */
String sessionState = getSession(session);
if (StringUtils.pathEquals(sessionState, state)) {
OAuth20Service oauthService = new ServiceBuilder().apiKey(CLIENT_ID).apiSecret(CLIENT_SECRET)
.callback(REDIRECT_URI).state(state).build(NaverLoginApi.instance());
/* Scribe에서 제공하는 AccessToken 획득 기능으로 네아로 Access Token을 획득 */
OAuth2AccessToken accessToken = oauthService.getAccessToken(code);
return accessToken;
}
return null;
}
/* 세션 유효성 검증을 위한 난수 생성기 */
private String generateRandomString() {
return UUID.randomUUID().toString();
}
/* http session에 데이터 저장 */
private void setSession(HttpSession session, String state) {
session.setAttribute(SESSION_STATE, state);
}
/* http session에서 데이터 가져오기 */
private String getSession(HttpSession session) {
return (String) session.getAttribute(SESSION_STATE);
}
/* Access Token을 이용하여 네이버 사용자 프로필 API를 호출 */
public String getUserProfile(OAuth2AccessToken oauthToken) throws IOException {
OAuth20Service oauthService = new ServiceBuilder().apiKey(CLIENT_ID).apiSecret(CLIENT_SECRET)
.callback(REDIRECT_URI).build(NaverLoginApi.instance());
OAuthRequest request = new OAuthRequest(Verb.GET, PROFILE_API_URL, oauthService);
oauthService.signRequest(oauthToken, request);
Response response = request.send();
return response.getBody();
}
}
|
cs |
NaverLoginApi.java 는 DefaultApi20 을 상속하고 있는것이며
여기는 인증//접근을 하기 위한 주소default 값이 명시되어있다.
NaverLogin.java 파일에서는 실제 네아로API 신청 당시 정했던 클라이언트 아이디, 비번, REDIRECT 주소 와 같은 정보가 있으며 NaverLoginApi.java 기존 주소값을 불러와서 같이 합처주는 작업을 진행하게 된다.
OAuth20Service 로 불러오는데 NaverLoginApi.java 에서의 디폴트 주소값을 불러올수 있게된다.
NaverLoginApi.java는 DefaultApi20을 상속하고 있으며 DefaultApi20는 OAuth20Service 필드변수로서 불러올수 있게된다.
ServiceBuilder 받은 값들을 합처서 주소화하는 과정이라 이해하면 편하겠다.
위의 사진을 보면 네이버 서버 설정과 관련된 정보들이 있는것을 확인할 수 있으며
작동의 이해를 위해선 클래스간의 관계도를 유심히 보는것이 도움이 될꺼같다.
CommonController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
@Log4j
@Controller
@RequiredArgsConstructor
@RequestMapping("/member/*")
public class CommonController {
@NonNull
private MemberService service;
@NonNull
private CustomUserDetailsService cusd;
// 이메일 인증-
@NonNull
private JavaMailSender mailSender;
//네이버
@NonNull
private NaverLogin naverlogin;
private String apiResult = null;
// 네이버 로그인 성공시 callback호출
@RequestMapping(value="/logincallback", method= {RequestMethod.GET,RequestMethod.POST})
public String callBack(Model model, @RequestParam String code, @RequestParam String state, HttpSession session, HttpServletRequest request) throws Exception{
OAuth2AccessToken oauthToken;
oauthToken = naverlogin.getAccessToken(session, code, state);
//로그인 사용자 정보 읽어오기
// String형식의 json데이터
apiResult = naverlogin.getUserProfile(oauthToken);
/**
* apiResult json 구조 {"resultcode":"00", "message":"success",
* "response":{"id":"33666449","nickname":"shinn****","age":"20-29","gender":"M","email":"sh@naver.com","name":"\uc2e0\ubc94\ud638"}}
**/
//2. String 형식인 apiResult를 json형태로 바꿈
JSONParser parser = new JSONParser();
Object obj = parser.parse(apiResult);
JSONObject jsonObj = (JSONObject)obj;
//3.데이터 파싱
// Top레벨 단계 _response 파싱
JSONObject response_obj = (JSONObject)jsonObj.get("response");
// 네이버에서 주는 고유 ID
String naverId = (String) response_obj.get("id");
// 네이버에서 설정된 사용자 이름
String naverNickname = (String) response_obj.get("nickname");
// 네이버에서 설정된 이메일
String naverEmail = (String) response_obj.get("email");
//네이버 같은경우 진짜ID+@ 형식이기 때문에 문자열 잘라서 id값을 추출하는 작업을 펼친다.
String target ="@";
int target_num = naverEmail.indexOf(target);
//네이버 진짜 ID
String newId=(String) naverEmail.substring(0,target_num);
System.out.println(newId);
MemberVO member = new MemberVO();
member.setUserid(newId); //아이디
member.setUsername(naverNickname); //닉네임
member.setUseremail(naverEmail); //이메일
member.setNaverLogin(naverId); //네이버 고유id번호
//네이버로 연동된 회원정보 찾기 =>[가입된 이메일] 또는 [네이버 고유번호id]를 조회하여 비교
MemberVO naverIdChk = service.naverChk(member);
//1.쌩판 홈페이지에 연동된 정보가 없는경우=>등록된 네이버 이메일x,네이버고유번호idx , 회원 가입절차 시작
if (naverIdChk == null) {
session.setAttribute("user", member);
return "member/customRegister";
//2.가입된 이메일은 있으나 네이버와의 연동이 안된경우
}else if(naverIdChk.getNaverLogin() == null && naverIdChk.getUseremail() !=null) {
//2-1 가입된 계정에 네이버 연동 진행
service.updateN(member);
//2-2 연동 끝났으면 자동 로그인
//네이버 고유id번호를 가진 계정의 id값을 추출하여 자동로그인 설정
String id=service.naverChk(member).getUserid();
UserDetails navervo = (UserDetails)cusd.loadUserByUsername(id);
System.out.println(id);
Authentication authentication = new UsernamePasswordAuthenticationToken(navervo, navervo.getPassword(),
navervo.getAuthorities());
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
return "redirect:/board/list";
//3.둘다 아니라면 네이버로 가입 된 상태임. 네이버 로그인시 바로 로그인됨
}else {
//가입된 상태에서 네이버아이디=웹사이트아이디 인지, 혹은 다른아이디인지(네이버 고유번호로 id찾음)
//시큐리티의 파라미터로 id값을 결정해서 로그인처리를 한다.
String id=service.naverChk(member).getUserid()==null?newId:service.naverChk(member).getUserid();
UserDetails navervo = (UserDetails)cusd.loadUserByUsername(id);
System.out.println(newId);
Authentication authentication = new UsernamePasswordAuthenticationToken(navervo, navervo.getPassword(),
navervo.getAuthorities());
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
return "redirect:/board/list";
}
// 회원가입 창으로 이동
@GetMapping("customRegister")
public void Register(Model model, HttpSession session) {
log.info("회원가입 창으로 이동");
/* 네이버아이디로 인증 URL을 생성하기 위하여 naverLoginBO클래스의 getAuthorizationUrl메소드 호출 */
//String naverAuthUrl = naverLoginBO.getAuthorizationUrl(session);
// https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=sE***************&
// redirect_uri=http%3A%2F%2F211.63.89.90%3A8090%2Flogin_project%2Fcallback&state=e68c269c-5ba9-4c31-85da-54c16c658125
//log.info("네이버: " + naverAuthUrl);
// 네이버
//model.addAttribute("url", naverAuthUrl);
}
// 로그인 창으로 이동
@GetMapping("/customLogin")
public void login(String error, String logout, Model model, HttpSession session) {
log.info("error: " + error);
log.info("logout: " + logout);
String naverUrl = naverlogin.getAuthorizationUrl(session);
model.addAttribute("naverUrl",naverUrl);
}
// 회원 등록 @PostMapping("/customRegister") public String Register(MemberVO vo, RedirectAttributes rttr) { log.info("회원등록 post"); service.insert(vo); return "redirect:/member/customLogin"; } }
|
cs |
네이버 연동 검증에서 사용되는 Mybatis mapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<!-- 네이버전용 회원가입 -->
<insert id="insertN">
INSERT ALL
INTO member(mno, userid, userpw, username, useremail,naverLogin) values(member_sequence.nextval, #{userid}, #{userpw}, #{username}, #{useremail}, #{naverLogin})
INTO member_auth(userid, auth) values(#{userid}, 'ROLE_USER')
SELECT * FROM DUAL
</insert>
<!-- 네이버 연동 여부 -->
<select id="naverChk" resultType="org.yoon.domain.MemberVO">
select userid,useremail, username, naverLogin from member where naverLogin =#{naverLogin} OR useremail = #{useremail}
</select>
<!-- 네이버연동 회원으로 정보 수정 -->
<update id="updateN">
update member set naverLogin = #{naverLogin} where useremail=#{useremail}
</update>
|
cs |
CommonController.java 흐름도
기존 사이트에 네이버 정보의 회원이 없다면 회원가입 절차를 진행하게 설계 하였음.
회원db관련
MemberVO.java
시연영상
728x90
반응형
LIST
'SPRING > 스프링-시큐리티' 카테고리의 다른 글
스프링-시큐리티 인증 절차 정리 (0) | 2021.06.30 |
---|---|
스프링-시큐리티 CSRF 토큰설정을 통한 CRUD 처리(3) (0) | 2021.05.08 |
스프링 시큐리티- 인증 절차 MemberVO 를 UsersDetails로 변환(2) (0) | 2021.05.07 |
스프링 시큐리티- 인증 절차 인터페이스 구현 UserDetails, UserDetailsService(1) (0) | 2021.05.07 |