Jwt 실습
Jwt 실습
JWT 실습
최초 토큰 생성 및 로그인
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
const userLogin = async (loginUser) => {
await userConfirm(
loginUser,
(response) => {
if (response.status === httpStatusCode.CREATE) {
console.log("로그인 성공!!!!")
let { data } = response
let accessToken = data["access-token"]
let refreshToken = data["refresh-token"]
isLogin.value = true
isLoginError.value = false
isValidToken.value = true
sessionStorage.setItem("accessToken", accessToken)
sessionStorage.setItem("refreshToken", refreshToken)
}
},
(error) => {
console.log("로그인 실패!!!!")
isLogin.value = false
isLoginError.value = true
isValidToken.value = false
console.error(error)
}
)
}
async function userConfirm(param, success, fail) {
await local.post(`/user/login`, param).then(success).catch(fail);
}
- vue.js에서 로직 상으로 userLogin 호출
- 백엔드에 로그인 로직 처리
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
@Operation(summary = "로그인", description = "아이디와 비밀번호를 이용하여 로그인 처리.")
@PostMapping("/login")
public ResponseEntity<Map<String, Object>> login(
@RequestBody @Parameter(description = "로그인 시 필요한 회원정보(아이디, 비밀번호).", required = true) MemberDto memberDto) {
log.debug("login user : {}", memberDto);
Map<String, Object> resultMap = new HashMap<String, Object>();
HttpStatus status = HttpStatus.ACCEPTED;
try {
MemberDto loginUser = memberService.login(memberDto);
if(loginUser != null) {
String accessToken = jwtUtil.createAccessToken(loginUser.getUserId());
String refreshToken = jwtUtil.createRefreshToken(loginUser.getUserId());
log.debug("access token : {}", accessToken);
log.debug("refresh token : {}", refreshToken);
// 발급받은 refresh token 을 DB에 저장.
memberService.saveRefreshToken(loginUser.getUserId(), refreshToken);
// JSON 으로 token 전달.
resultMap.put("access-token", accessToken);
resultMap.put("refresh-token", refreshToken);
status = HttpStatus.CREATED;
} else {
resultMap.put("message", "아이디 또는 패스워드를 확인해 주세요.");
status = HttpStatus.UNAUTHORIZED;
}
} catch (Exception e) {
log.debug("로그인 에러 발생 : {}", e);
resultMap.put("message", e.getMessage());
status = HttpStatus.INTERNAL_SERVER_ERROR;
}
return new ResponseEntity<Map<String, Object>>(resultMap, status);
}
- 토큰 생성
- 토큰을 DB에 저장한다.
- 프론트에 전달한 Map 자료구조를 선언하고 토큰과 메세지를 저장해서 리턴한다.
1
2
3
4
5
if (response.status === httpStatusCode.CREATE) {
console.log("로그인 성공!!!!")
let { data } = response
let accessToken = data["access-token"]
let refreshToken = data["refresh-token"]
- Front에 저장 성공
로그인 체크
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const onlyAuthUser = async (to, from, next) => {
const memberStore = useMemberStore();
const { userInfo, isValidToken } = storeToRefs(memberStore);
const { getUserInfo } = memberStore;
let token = sessionStorage.getItem("accessToken");
if (userInfo.value != null && token) {
await getUserInfo(token);
}
if (!isValidToken.value || userInfo.value === null) {
next({ name: "user-login" });
} else {
next();
}
};
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
// memberStore의 getUserInfo
const getUserInfo = async (token) => {
let decodeToken = jwtDecode(token)
console.log(decodeToken)
await findById(
decodeToken.userId,
(response) => {
if (response.status === httpStatusCode.OK) {
userInfo.value = response.data.userInfo
} else {
console.log("유저 정보 없음!!!!")
}
},
async (error) => {
console.error(
"g[토큰 만료되어 사용 불가능.] : ",
error.response.status,
error.response.statusText
)
isValidToken.value = false
await tokenRegenerate()
}
)
}
- onlyAuthUser에서 memberStore의 getUserInfo를 가져온다.
- findById()에서 유효성을 검증하고 User 정보를 얻는다
- 이건 백엔드에서 처리
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
@Operation(summary = "회원인증", description = "회원 정보를 담은 Token 을 반환한다.") @GetMapping("/info/{userId}") public ResponseEntity<Map<String, Object>> getInfo( @PathVariable("userId") @Parameter(description = "인증할 회원의 아이디.", required = true) String userId, HttpServletRequest request) { // logger.debug("userId : {} ", userId); Map<String, Object> resultMap = new HashMap<>(); HttpStatus status = HttpStatus.ACCEPTED; if (jwtUtil.checkToken(request.getHeader("Authorization"))) { log.info("사용 가능한 토큰!!!"); try { // 로그인 사용자 정보. MemberDto memberDto = memberService.userInfo(userId); resultMap.put("userInfo", memberDto); status = HttpStatus.OK; } catch (Exception e) { log.error("정보조회 실패 : {}", e); resultMap.put("message", e.getMessage()); status = HttpStatus.INTERNAL_SERVER_ERROR; } } else { log.error("사용 불가능 토큰!!!"); status = HttpStatus.UNAUTHORIZED; } return new ResponseEntity<Map<String, Object>>(resultMap, status); }
- 토큰의 유효성 검증
- 토큰의 유효성 검증 함수 checkToken()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 전달 받은 토큰이 제대로 생성된 것인지 확인 하고 문제가 있다면 UnauthorizedException 발생. public boolean checkToken(String token) { try { // Json Web Signature? 서버에서 인증을 근거로 인증 정보를 서버의 private key 서명 한것을 토큰화 한것 // setSigningKey : JWS 서명 검증을 위한 secret key 세팅 // parseClaimsJws : 파싱하여 원본 jws 만들기 Jws<Claims> claims = Jwts.parser().setSigningKey(this.generateKey()).parseClaimsJws(token); // Claims 는 Map 구현체 형태 log.debug("claims: {}", claims); return true; } catch (Exception e) { log.error(e.getMessage()); return false; } }
로그인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const onlyAuthUser = async (to, from, next) => {
const memberStore = useMemberStore();
const { userInfo, isValidToken } = storeToRefs(memberStore);
const { getUserInfo } = memberStore;
let token = sessionStorage.getItem("accessToken");
if (userInfo.value != null && token) {
await getUserInfo(token);
}
if (!isValidToken.value || userInfo.value === null) {
next({ name: "user-login" });
} else {
next();
}
};
- getUserInfo()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const getUserInfo = async (token) => {
let decodeToken = jwtDecode(token)
console.log(decodeToken)
await findById(
decodeToken.userId,
(response) => {
if (response.status === httpStatusCode.OK) {
userInfo.value = response.data.userInfo
} else {
console.log("유저 정보 없음!!!!")
}
},
async (error) => {
console.error(
"g[토큰 만료되어 사용 불가능.] : ",
error.response.status,
error.response.statusText
)
isValidToken.value = false
await tokenRegenerate() //토큰 재 발급
}
)
}
- 백엔드에서 검증을 실시한다.
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
@Operation(summary = "회원인증", description = "회원 정보를 담은 Token 을 반환한다.") @GetMapping("/info/{userId}") public ResponseEntity<Map<String, Object>> getInfo( @PathVariable("userId") @Parameter(description = "인증할 회원의 아이디.", required = true) String userId, HttpServletRequest request) { // logger.debug("userId : {} ", userId); Map<String, Object> resultMap = new HashMap<>(); HttpStatus status = HttpStatus.ACCEPTED; if (jwtUtil.checkToken(request.getHeader("Authorization"))) { log.info("사용 가능한 토큰!!!"); try { // 로그인 사용자 정보. MemberDto memberDto = memberService.userInfo(userId); resultMap.put("userInfo", memberDto); status = HttpStatus.OK; } catch (Exception e) { log.error("정보조회 실패 : {}", e); resultMap.put("message", e.getMessage()); status = HttpStatus.INTERNAL_SERVER_ERROR; } } else { log.error("사용 불가능 토큰!!!"); status = HttpStatus.UNAUTHORIZED; } return new ResponseEntity<Map<String, Object>>(resultMap, status); }
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.