Skip to content

Commit

Permalink
[feat] jwt 기능 구현 (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
youngniw committed Apr 16, 2022
1 parent 09bc871 commit 979bf64
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 7 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'

implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.2'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.nadoyagsa.pillaroid.configuration;

import com.nadoyagsa.pillaroid.jwt.AuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SecurityConfiguration implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;

@Autowired
public SecurityConfiguration(AuthInterceptor authInterceptor) {
this.authInterceptor = authInterceptor;
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
//토큰 검사 안하는 경로 설정함(/login/**, 알약 검색 등)
//TODO: 알약 검색 시 patterns 사용해서 경로 추가해야 함
registry.addInterceptor(authInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(new String[]{"/login/**"});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nadoyagsa.pillaroid.entity.User;
import com.nadoyagsa.pillaroid.jwt.AuthTokenProvider;
import com.nadoyagsa.pillaroid.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
Expand All @@ -19,10 +20,12 @@
@RequestMapping(value = "/login")
public class LoginController {
private final UserService userService;
private final AuthTokenProvider authTokenProvider;

@Autowired
public LoginController(UserService userService) {
public LoginController(UserService userService, AuthTokenProvider authTokenProvider) {
this.userService = userService;
this.authTokenProvider = authTokenProvider;
}

// 카카오 로그인 (Input: access token)
Expand All @@ -49,7 +52,7 @@ public ResponseEntity<Map<String, Object>> kakaoLogin(@RequestBody Map<String, S
);
} catch (Exception e) { // 카카오로 요청 실패
response.put("success", false);
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); //Status Code=400
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); // Status Code=400
}

ObjectMapper objectMapper = new ObjectMapper();
Expand All @@ -59,23 +62,28 @@ public ResponseEntity<Map<String, Object>> kakaoLogin(@RequestBody Map<String, S
kakaoUserId = userInfo.path("id").asLong();
} catch (JsonProcessingException e) {
response.put("success", false);
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); //Status Code=500
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); // Status Code=500
}

String authToken = authTokenProvider.createAuthToken(kakaoUserId);
Optional<User> user = userService.findUserByKakaoAccountId(kakaoUserId);
// 클라이언트의 로그인 경험 있음
if (user.isPresent()) {
response.put("success", true);
response.put("user", user);
return new ResponseEntity<>(response, HttpStatus.OK); //Status Code=200
response.put("authToken", authToken);
//response.put("user", user);
return new ResponseEntity<>(response, HttpStatus.OK); // Status Code=200
}
// 클라이언트의 로그인 경험 없음(DB에 사용자 추가)
else {
User newUser = userService.signUp(new User(kakaoUserId));

response.put("success", true);
response.put("user", newUser);
return new ResponseEntity<>(response, HttpStatus.CREATED); //Status Code=201
response.put("authToken", authToken);
//response.put("user", newUser);
return new ResponseEntity<>(response, HttpStatus.CREATED); // Status Code=201
}
}

// 자동로그인은 시각장애인을 위해 프론트에서 authToken값 존재 여부에 따라 수행됨
}
30 changes: 30 additions & 0 deletions src/main/java/com/nadoyagsa/pillaroid/jwt/AuthInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.nadoyagsa.pillaroid.jwt;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class AuthInterceptor implements HandlerInterceptor {
private final AuthTokenProvider authTokenProvider;

@Autowired
public AuthInterceptor(AuthTokenProvider authTokenProvider) {
this.authTokenProvider = authTokenProvider;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("authorization");
if (token != null && authTokenProvider.validateToken(token)) {
return true;
} else {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
}
}
70 changes: 70 additions & 0 deletions src/main/java/com/nadoyagsa/pillaroid/jwt/AuthTokenProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.nadoyagsa.pillaroid.jwt;

import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;

@Component
public class AuthTokenProvider implements InitializingBean {
private final Logger logger = LoggerFactory.getLogger(AuthTokenProvider.class);

private final String secretKey;
private Key key;

public AuthTokenProvider(@Value("${jwt.secret-key}") String secretKey) {
this.secretKey = secretKey;
}

@Override
public void afterPropertiesSet() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
this.key = Keys.hmacShaKeyFor(keyBytes);
}

// 토큰의 payload에 카카오 회원번호를 삽입
public String createAuthToken(Long kakaoAccountId) {
return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setIssuer("pillaroid")
.setIssuedAt(new Date())
.claim("accountId", kakaoAccountId)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}

// 토큰으로부터 payload를 추출하는 메서드
public Claims getClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
}

public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);

return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
logger.info("잘못된 JWT 서명입니다.");
} catch (ExpiredJwtException e) {
logger.info("만료된 JWT 토큰입니다.");
} catch (UnsupportedJwtException e) {
logger.info("지원되지 않는 JWT 토큰입니다.");
} catch (IllegalArgumentException e) {
logger.info("JWT 토큰이 잘못되었습니다.");
} catch (Exception e) {
logger.info("서비스에 접근할 수 없는 토큰입니다.");
}
return false;
}
}

0 comments on commit 979bf64

Please sign in to comment.