-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #34 from kakao-tech-campus-2nd-step3/weekly
Weekly - 정기 병합
- Loading branch information
Showing
49 changed files
with
1,570 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.helpmeCookies.global.config; | ||
|
||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing; | ||
|
||
@Configuration | ||
@EnableJpaAuditing | ||
public class JpaConfig { | ||
} |
125 changes: 125 additions & 0 deletions
125
src/main/java/com/helpmeCookies/global/jwt/JwtProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package com.helpmeCookies.global.jwt; | ||
|
||
import java.security.Key; | ||
import java.util.Date; | ||
|
||
import javax.crypto.spec.SecretKeySpec; | ||
|
||
import org.springframework.beans.factory.InitializingBean; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.SignatureAlgorithm; | ||
|
||
@Component | ||
public class JwtProvider implements InitializingBean { | ||
@Value("${jwt.secret}") | ||
private String secret; | ||
@Value("${jwt.access-token-expire-time}") | ||
private long accessTokenExpireTime; | ||
@Value("${jwt.refresh-token-expire-time}") | ||
private long refreshTokenExpireTime; | ||
private Key secretKey; | ||
private static final String ROLE = "role"; | ||
private static final String IS_ACCESS_TOKEN = "isAccessToken"; | ||
private static final String HEADER_PREFIX = "Bearer "; | ||
|
||
public String parseHeader(String header) { | ||
if (header == null || header.isEmpty()) { | ||
throw new IllegalArgumentException("Authorization 헤더가 없습니다."); | ||
} else if (!header.startsWith(HEADER_PREFIX)) { | ||
throw new IllegalArgumentException("Authorization 올바르지 않습니다."); | ||
} else if (header.split(" ").length != 2) { | ||
throw new IllegalArgumentException("Authorization 올바르지 않습니다."); | ||
} | ||
|
||
return header.split(" ")[1]; | ||
} | ||
|
||
public JwtToken createToken(JwtUser jwtUser) { | ||
String accessToken = generateToken(jwtUser, true); | ||
String refreshToken = generateToken(jwtUser, false); | ||
return JwtToken.builder() | ||
.accessToken(accessToken) | ||
.refreshToken(refreshToken) | ||
.build(); | ||
} | ||
|
||
// 유요한 토큰인지 확인 | ||
public boolean validateToken(String rawToken, boolean isAccessToken) { | ||
try { | ||
// 엑세스 토큰인지 확인 | ||
Claims claims = extractClaims(rawToken); | ||
if (claims.get(IS_ACCESS_TOKEN, Boolean.class) != isAccessToken) { | ||
return false; | ||
} | ||
// 만료시간 확인 | ||
return !claims.getExpiration().before(new Date()); | ||
} catch (Exception e) { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* refreshToken을 통해, accessToken을 재발급하는 메서드. | ||
* refreshToken의 유효성을 검사하고, isAccessToken이 true일때만 accessToken을 재발급한다. | ||
* TODO: refreshToken을 저장하고, 저장된 refreshToken과 비교하는 로직 필요 | ||
*/ | ||
public String reissueAccessToken(String refreshToken) { | ||
Claims claims = extractClaims(refreshToken); | ||
if (claims.get(IS_ACCESS_TOKEN, Boolean.class)) { | ||
throw new IllegalArgumentException("리프레시 토큰이 아닙니다."); | ||
} | ||
JwtUser jwtUser = claimsToJwtUser(claims); | ||
return generateToken(jwtUser, true); | ||
} | ||
|
||
/** | ||
* [validateToken] 이후 호출하는 메서드. | ||
* rawToken을 통해 JwtUser를 추출한다. | ||
* [jwtUser]는 userId와 role을 가지고 있다. 즉 JWT에 저장된 정보를 추출한다. | ||
*/ | ||
public JwtUser getJwtUser(String rawToken) { | ||
Claims claims = extractClaims(rawToken); | ||
return claimsToJwtUser(claims); | ||
} | ||
|
||
private JwtUser claimsToJwtUser(Claims claims) { | ||
String userId = claims.getSubject(); | ||
return JwtUser.of(Long.parseLong(userId)); | ||
} | ||
|
||
/** | ||
* Jwt 토큰생성 | ||
* accessToken과 refreshToken의 다른점은 만료시간과, isAccessToken이다. | ||
*/ | ||
private String generateToken(JwtUser jwtUser, boolean isAccessToken) { | ||
long expireTime = isAccessToken ? accessTokenExpireTime : refreshTokenExpireTime; | ||
Date expireDate = new Date(System.currentTimeMillis() + expireTime); | ||
return Jwts.builder() | ||
.signWith(secretKey) | ||
.claim(IS_ACCESS_TOKEN, isAccessToken) | ||
.setSubject(jwtUser.getId().toString()) | ||
.setExpiration(expireDate) | ||
.compact(); | ||
} | ||
|
||
|
||
private Claims extractClaims(String rawToken) { | ||
return Jwts.parserBuilder() | ||
.setSigningKey(secretKey) | ||
.build() | ||
.parseClaimsJws(rawToken) | ||
.getBody(); | ||
} | ||
|
||
/** | ||
* HS256방식의 키를 생성한다. | ||
*/ | ||
@Override | ||
public void afterPropertiesSet() { | ||
secretKey = new SecretKeySpec(secret.getBytes(), SignatureAlgorithm.HS256.getJcaName()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.helpmeCookies.global.jwt; | ||
|
||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@Builder | ||
public class JwtToken { | ||
private String accessToken; | ||
private String refreshToken; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package com.helpmeCookies.global.jwt; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
|
||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
|
||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
@Builder | ||
@Getter | ||
public class JwtUser implements UserDetails { | ||
private Long id; | ||
private String username; | ||
private Collection<? extends GrantedAuthority> authorities; | ||
|
||
public static JwtUser of(Long id) { | ||
return JwtUser.builder() | ||
.id(id) | ||
.build(); | ||
} | ||
|
||
@Override | ||
// 임시 기본 권한을 USER로 설정 | ||
public Collection<? extends GrantedAuthority> getAuthorities() { | ||
return List.of(new SimpleGrantedAuthority("ROLE_" + "USER")); | ||
} | ||
|
||
@Override | ||
public String getPassword() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String getUsername() { | ||
return this.username; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonLocked() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isCredentialsNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isEnabled() { | ||
return true; | ||
} | ||
|
||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/com/helpmeCookies/global/security/JwtAccessDeniedHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.helpmeCookies.global.security; | ||
|
||
import java.io.IOException; | ||
import java.io.PrintWriter; | ||
|
||
import org.springframework.http.MediaType; | ||
import org.springframework.security.access.AccessDeniedException; | ||
import org.springframework.security.web.access.AccessDeniedHandler; | ||
import org.springframework.stereotype.Component; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
@Slf4j | ||
@Component | ||
@RequiredArgsConstructor | ||
public class JwtAccessDeniedHandler implements AccessDeniedHandler { | ||
private final ObjectMapper objectMapper; | ||
|
||
@Override | ||
public void handle(HttpServletRequest request, HttpServletResponse response, | ||
AccessDeniedException accessDeniedException) { | ||
log.error("Token : {}", request.getHeader("Authorization")); | ||
// TODO: 에러코드 추가 | ||
response.setStatus(403); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
src/main/java/com/helpmeCookies/global/security/JwtAuthenticationEntryPoint.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.helpmeCookies.global.security; | ||
|
||
import java.io.IOException; | ||
|
||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.AuthenticationEntryPoint; | ||
import org.springframework.stereotype.Component; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
@Slf4j | ||
@Component | ||
@RequiredArgsConstructor | ||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||
private final ObjectMapper objectMapper; | ||
|
||
@Override | ||
public void commence(HttpServletRequest request, HttpServletResponse response, | ||
AuthenticationException authException) throws IOException, ServletException { | ||
log.debug("Token : {}", request.getHeader("Authorization")); | ||
response.setStatus(401); | ||
} | ||
} |
Oops, something went wrong.