-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[REFACTOR] 카카오 로그인 피드백 반영
- Loading branch information
Showing
33 changed files
with
834 additions
and
42 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
GET http://localhost:8080/api/health/v2 | ||
|
||
POST http://localhost:8080/oauth/login | ||
Content-Type: application/json | ||
|
||
{ | ||
"id": 999, | ||
"value": "content" | ||
} |
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
23 changes: 23 additions & 0 deletions
23
src/main/java/sopt/org/motivooServer/domain/auth/config/CustomAccessDeniedHandler.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,23 @@ | ||
package sopt.org.motivooServer.domain.auth.config; | ||
|
||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.springframework.security.access.AccessDeniedException; | ||
import org.springframework.security.web.access.AccessDeniedHandler; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.io.IOException; | ||
|
||
@Component | ||
public class CustomAccessDeniedHandler implements AccessDeniedHandler { | ||
@Override | ||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { | ||
setResponse(response); | ||
} | ||
|
||
private void setResponse(HttpServletResponse response) { | ||
response.setStatus(HttpServletResponse.SC_FORBIDDEN); | ||
} | ||
|
||
} |
19 changes: 19 additions & 0 deletions
19
...ain/java/sopt/org/motivooServer/domain/auth/config/CustomJwtAuthenticationEntryPoint.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,19 @@ | ||
package sopt.org.motivooServer.domain.auth.config; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.AuthenticationEntryPoint; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class CustomJwtAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||
@Override | ||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) { | ||
setResponse(response); | ||
} | ||
|
||
private void setResponse(HttpServletResponse response) { | ||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
src/main/java/sopt/org/motivooServer/domain/auth/config/JwtAuthenticationFilter.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,52 @@ | ||
package sopt.org.motivooServer.domain.auth.config; | ||
|
||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.NonNull; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.util.StringUtils; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
import java.io.IOException; | ||
|
||
@Slf4j | ||
@Component | ||
@RequiredArgsConstructor | ||
public class JwtAuthenticationFilter extends OncePerRequestFilter { | ||
|
||
private final JwtTokenProvider jwtTokenProvider; | ||
|
||
@Override | ||
protected void doFilterInternal(@NonNull HttpServletRequest request, | ||
@NonNull HttpServletResponse response, | ||
@NonNull FilterChain filterChain) throws ServletException, IOException { | ||
|
||
final String token = getJwtFromRequest(request); | ||
jwtTokenProvider.validateToken(token); | ||
try { | ||
Long memberId = Long.parseLong(jwtTokenProvider.getPayload(token)); | ||
// authentication 객체 생성 -> principal에 유저정보를 담는다. | ||
UserAuthentication authentication = new UserAuthentication(memberId.toString(), null, null); | ||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | ||
SecurityContextHolder.getContext().setAuthentication(authentication); | ||
} catch (NumberFormatException e) { | ||
log.error("refresh token은 유저 아이디를 담고있지 않습니다."); | ||
} | ||
// 다음 필터로 요청 전달 | ||
filterChain.doFilter(request, response); | ||
} | ||
|
||
private String getJwtFromRequest(HttpServletRequest request) { | ||
String bearerToken = request.getHeader("Authorization"); | ||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { | ||
return bearerToken.substring("Bearer ".length()); | ||
} | ||
return null; | ||
} | ||
} |
111 changes: 111 additions & 0 deletions
111
src/main/java/sopt/org/motivooServer/domain/auth/config/JwtTokenProvider.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,111 @@ | ||
package sopt.org.motivooServer.domain.auth.config; | ||
|
||
import io.jsonwebtoken.*; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.security.oauth2.jwt.JwtException; | ||
import org.springframework.stereotype.Component; | ||
import sopt.org.motivooServer.domain.auth.dto.response.OauthTokenResponse; | ||
import sopt.org.motivooServer.domain.auth.repository.TokenRedisRepository; | ||
import sopt.org.motivooServer.domain.user.exception.UserException; | ||
|
||
import java.util.*; | ||
|
||
import static sopt.org.motivooServer.domain.user.exception.UserExceptionType.*; | ||
|
||
@Slf4j | ||
@Component | ||
public class JwtTokenProvider { | ||
private static final String BEARER_TYPE = "Bearer"; | ||
|
||
@Value("${jwt.access-token.expire-length}") | ||
private long accessTokenValidityInMilliseconds; | ||
|
||
@Value("${jwt.refresh-token.expire-length}") | ||
private long refreshTokenValidityInMilliseconds; | ||
|
||
@Value("${jwt.token.secret-key}") | ||
private String secretKey; | ||
|
||
private TokenRedisRepository tokenRedisRepository; | ||
|
||
public JwtTokenProvider(TokenRedisRepository tokenRedisRepository){ | ||
this.tokenRedisRepository = tokenRedisRepository; | ||
} | ||
|
||
public String createAccessToken(String payload) { | ||
return createToken(payload, accessTokenValidityInMilliseconds); | ||
} | ||
|
||
public String createRefreshToken() { | ||
byte[] array = new byte[7]; | ||
new Random().nextBytes(array); | ||
String generatedString = Base64.getEncoder().encodeToString(array); | ||
return createToken(generatedString, refreshTokenValidityInMilliseconds); | ||
} | ||
|
||
public String createToken(String payload, long expireLength) { | ||
Map<String, Object> claims = new HashMap<>(); | ||
claims.put("payload", payload); | ||
Date now = new Date(); | ||
Date validity = new Date(now.getTime() + expireLength); | ||
return Jwts.builder() | ||
.setClaims(claims) | ||
.setIssuedAt(now) | ||
.setExpiration(validity) | ||
.signWith(SignatureAlgorithm.HS256,secretKey) | ||
.compact(); | ||
} | ||
|
||
public String getPayload(String token){ | ||
try { | ||
Claims claims = Jwts.parserBuilder() | ||
.setSigningKey(secretKey) | ||
.build() | ||
.parseClaimsJws(token) | ||
.getBody(); | ||
|
||
Object subject = claims.get("payload"); | ||
|
||
return String.valueOf(subject); | ||
|
||
} catch (ExpiredJwtException e) { | ||
return e.getClaims().getSubject(); | ||
} catch (JwtException e){ | ||
throw new RuntimeException(String.valueOf(JwtValidationType.INVALID_JWT_TOKEN)); | ||
} | ||
} | ||
|
||
public void validateToken(String token) { | ||
try { | ||
token = token.replaceAll("\\s+", ""); | ||
token = token.replace(BEARER_TYPE, ""); | ||
Jwts.parserBuilder() | ||
.setSigningKey(secretKey) | ||
.build() | ||
.parseClaimsJws(token); | ||
} catch (MalformedJwtException ex){ | ||
throw new UserException(TOKEN_NOT_FOUND); | ||
} catch (ExpiredJwtException ex) { | ||
throw new UserException(TOKEN_EXPIRED); | ||
} catch (UnsupportedJwtException ex) { | ||
throw new UserException(TOKEN_UNSUPPORTED); | ||
} catch (IllegalArgumentException ex) { | ||
throw new UserException(TOKEN_NOT_FOUND); | ||
} | ||
} | ||
|
||
|
||
public OauthTokenResponse reissue(Long userId, String refreshToken) { | ||
validateToken(refreshToken); | ||
|
||
String reissuedAccessToken = createAccessToken(String.valueOf(userId)); | ||
String reissuedRefreshToken = createRefreshToken(); | ||
OauthTokenResponse tokenResponse = new OauthTokenResponse(reissuedAccessToken, reissuedAccessToken); | ||
|
||
tokenRedisRepository.saveRefreshToken(reissuedRefreshToken, String.valueOf(userId)); | ||
tokenRedisRepository.deleteRefreshToken(refreshToken); | ||
|
||
return tokenResponse; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
src/main/java/sopt/org/motivooServer/domain/auth/config/JwtValidationType.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,10 @@ | ||
package sopt.org.motivooServer.domain.auth.config; | ||
|
||
public enum JwtValidationType { | ||
VALID_JWT, // 유효한 JWT | ||
INVALID_JWT_SIGNATURE, // 유효하지 않은 서명 | ||
INVALID_JWT_TOKEN, // 유효하지 않은 토큰 | ||
EXPIRED_JWT_TOKEN, // 만료된 토큰 | ||
UNSUPPORTED_JWT_TOKEN, // 지원하지 않는 형식의 토큰 | ||
EMPTY_JWT // 빈 JWT | ||
} |
35 changes: 35 additions & 0 deletions
35
...a/sopt/org/motivooServer/domain/auth/config/OAuth2ClientRegistrationRepositoryConfig.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,35 @@ | ||
package sopt.org.motivooServer.domain.auth.config; | ||
|
||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConfiguredCondition; | ||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; | ||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter; | ||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Conditional; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.oauth2.client.registration.ClientRegistration; | ||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; | ||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
@Configuration | ||
@EnableConfigurationProperties(OAuth2ClientProperties.class) | ||
@Conditional(ClientsConfiguredCondition.class) | ||
public class OAuth2ClientRegistrationRepositoryConfig { | ||
private final OAuth2ClientProperties properties; | ||
|
||
OAuth2ClientRegistrationRepositoryConfig(OAuth2ClientProperties properties) { | ||
this.properties = properties; | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean(ClientRegistrationRepository.class) | ||
public InMemoryClientRegistrationRepository clientRegistrationRepository() { | ||
List<ClientRegistration> registrations = new ArrayList<>( | ||
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(this.properties).values()); | ||
return new InMemoryClientRegistrationRepository(registrations); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/sopt/org/motivooServer/domain/auth/config/RedisConfig.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 sopt.org.motivooServer.domain.auth.config; | ||
|
||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.redis.connection.RedisConnectionFactory; | ||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; | ||
import org.springframework.data.redis.core.RedisTemplate; | ||
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; | ||
|
||
@Configuration | ||
@EnableRedisRepositories | ||
public class RedisConfig { | ||
@Value("${data.redis.host}") | ||
private String redisHost; | ||
|
||
@Value("${data.redis.port}") | ||
private int redisPort; | ||
|
||
@Bean | ||
public RedisConnectionFactory redisConnectionFactory() { | ||
return new LettuceConnectionFactory(redisHost, redisPort); | ||
} | ||
|
||
@Bean | ||
public RedisTemplate<?, ?> redisTemplate() { | ||
RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>(); | ||
redisTemplate.setConnectionFactory(redisConnectionFactory()); | ||
return redisTemplate; | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/sopt/org/motivooServer/domain/auth/config/UserAuthentication.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,13 @@ | ||
package sopt.org.motivooServer.domain.auth.config; | ||
|
||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import java.util.Collection; | ||
|
||
public class UserAuthentication extends UsernamePasswordAuthenticationToken { | ||
|
||
// 사용자 인증 객체 생성 | ||
public UserAuthentication(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { | ||
super(principal, credentials, authorities); | ||
} | ||
} |
Oops, something went wrong.