-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
756 additions
and
8 deletions.
There are no files selected for viewing
32 changes: 32 additions & 0 deletions
32
cakey-api/src/main/java/com/cakey/common/auth/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,32 @@ | ||
package com.cakey.common.auth; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.access.AccessDeniedException; | ||
import org.springframework.security.web.access.AccessDeniedHandler; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class CustomAccessDeniedHandler implements AccessDeniedHandler { | ||
|
||
private final ObjectMapper mapper = new ObjectMapper(); | ||
|
||
@Override | ||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { | ||
setResponse(response); | ||
} | ||
|
||
private void setResponse(HttpServletResponse response) throws IOException { | ||
response.setStatus(HttpServletResponse.SC_FORBIDDEN); | ||
response.setContentType("application/json"); | ||
response.setCharacterEncoding("UTF-8"); | ||
|
||
ResponseEntity responseEntity = new ResponseEntity(HttpStatus.NOT_FOUND); | ||
|
||
response.getWriter().write(mapper.writeValueAsString(responseEntity)); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
cakey-api/src/main/java/com/cakey/common/auth/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,20 @@ | ||
package com.cakey.common.auth; | ||
|
||
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); | ||
} | ||
} | ||
|
92 changes: 92 additions & 0 deletions
92
cakey-api/src/main/java/com/cakey/common/auth/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,92 @@ | ||
package com.cakey.common.auth; | ||
|
||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.ExpiredJwtException; | ||
import io.jsonwebtoken.Header; | ||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.MalformedJwtException; | ||
import io.jsonwebtoken.UnsupportedJwtException; | ||
import io.jsonwebtoken.security.Keys; | ||
import jakarta.annotation.PostConstruct; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Base64; | ||
import java.util.Date; | ||
import javax.crypto.SecretKey; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Component | ||
public class JwtTokenProvider { | ||
|
||
private static final String USER_ID = "userId"; | ||
private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 2 * 60 * 60 * 1000L; //2시간 | ||
private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 14* 24 * 60 * 60 * 1000L; //2주 | ||
|
||
@Value("${jwt.secret}") | ||
private String JWT_SECRET; | ||
|
||
@PostConstruct | ||
protected void init() { | ||
JWT_SECRET = Base64.getEncoder().encodeToString(JWT_SECRET.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
public String issueAccessToken(final Authentication authentication) { | ||
return issueToken(authentication, ACCESS_TOKEN_EXPIRATION_TIME); | ||
} | ||
|
||
public String issueRefreshToken(final Authentication authentication) { | ||
return issueToken(authentication, REFRESH_TOKEN_EXPIRATION_TIME); | ||
} | ||
|
||
private String issueToken(final Authentication authentication, final Long expireTime) { | ||
final Date now = new Date(); | ||
|
||
final Claims claims = Jwts.claims() | ||
.setIssuedAt(now) | ||
.setExpiration(new Date(now.getTime() + expireTime)); | ||
|
||
claims.put(USER_ID, authentication.getPrincipal()); | ||
return Jwts.builder() | ||
.setHeaderParam(Header.TYPE, Header.JWT_TYPE) | ||
.setClaims(claims) | ||
.signWith(getSigningKey()) | ||
.compact(); | ||
} | ||
|
||
private SecretKey getSigningKey() { | ||
String encodedKey = Base64.getEncoder().encodeToString(JWT_SECRET.getBytes()); | ||
return Keys.hmacShaKeyFor(encodedKey.getBytes()); | ||
} | ||
|
||
public JwtValidationType validateToken(final String token) { | ||
try { | ||
final Claims claims = getBody(token); | ||
return JwtValidationType.VALID_JWT; | ||
} catch (MalformedJwtException e) { | ||
return JwtValidationType.INVALID_JWT_TOKEN; | ||
} catch (ExpiredJwtException e) { | ||
return JwtValidationType.EXPIRED_JWT_TOKEN; | ||
} catch (UnsupportedJwtException e) { | ||
return JwtValidationType.UNSUPPORTED_JWT_TOKEN; | ||
} catch (IllegalArgumentException e) { | ||
return JwtValidationType.EMPTY_JWT; | ||
} | ||
|
||
} | ||
|
||
private Claims getBody(final String token) { | ||
return Jwts.parserBuilder() | ||
.setSigningKey(getSigningKey()) | ||
.build() | ||
.parseClaimsJws(token) | ||
.getBody(); | ||
} | ||
|
||
public Long getUserFromJwt(final String token) { | ||
Claims claims = getBody(token); | ||
return Long.valueOf(claims.get(USER_ID).toString()); | ||
} | ||
|
||
} |
16 changes: 16 additions & 0 deletions
16
cakey-api/src/main/java/com/cakey/common/auth/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,16 @@ | ||
package com.cakey.common.auth; | ||
|
||
import lombok.AllArgsConstructor; | ||
|
||
@AllArgsConstructor | ||
public enum JwtValidationType { | ||
VALID_JWT("VALID_JWT"), | ||
INVALID_JWT_SIGNATURE("INVALID_JWT_SIGNATURE"), | ||
INVALID_JWT_TOKEN("INVALID_JWT_TOKEN"), | ||
EXPIRED_JWT_TOKEN("EXPIRED_JWT_TOKEN"), | ||
UNSUPPORTED_JWT_TOKEN("UNSUPPORTED_JWT_TOKEN"), | ||
EMPTY_JWT("EMPTY_JWT") | ||
; | ||
|
||
private String valdationType; | ||
} |
11 changes: 11 additions & 0 deletions
11
cakey-api/src/main/java/com/cakey/common/auth/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,11 @@ | ||
package com.cakey.common.auth; | ||
|
||
import java.util.Collection; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.GrantedAuthority; | ||
|
||
public class UserAuthentication extends UsernamePasswordAuthenticationToken { | ||
public UserAuthentication(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { | ||
super(principal, credentials, authorities); | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
cakey-api/src/main/java/com/cakey/common/auth/filter/CustomAuthenticationFilter.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,55 @@ | ||
package com.cakey.common.auth.filter; | ||
|
||
import com.cakey.common.auth.JwtTokenProvider; | ||
import com.cakey.common.auth.UserAuthentication; | ||
import com.cakey.jwt.service.TokenService; | ||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.Cookie; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import lombok.NonNull; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class CustomAuthenticationFilter extends OncePerRequestFilter { | ||
|
||
private final JwtTokenProvider jwtTokenProvider; | ||
// private final TokenService tokenService; | ||
|
||
@Override | ||
protected void doFilterInternal( | ||
@NonNull HttpServletRequest request, | ||
@NonNull HttpServletResponse response, | ||
@NonNull FilterChain filterChain | ||
) throws ServletException, IOException { | ||
try { | ||
final String accessToken = getAccessTokenFromCookie(request); | ||
if (accessToken != null) { | ||
final Long userId = jwtTokenProvider.getUserFromJwt(accessToken); | ||
SecurityContextHolder.getContext().setAuthentication(new UserAuthentication(userId, null, null)); | ||
} else { | ||
SecurityContextHolder.clearContext(); | ||
} | ||
} catch (Exception e) { | ||
} | ||
filterChain.doFilter(request, response); | ||
} | ||
|
||
public String getAccessTokenFromCookie(@NonNull HttpServletRequest request) { | ||
Cookie[] cookies = request.getCookies(); | ||
if (cookies != null) { | ||
for (Cookie cookie : cookies) { | ||
if (cookie.getName().equals("access_token")) { | ||
return cookie.getValue(); | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
cakey-api/src/main/java/com/cakey/common/auth/filter/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,62 @@ | ||
package com.cakey.common.auth.filter; | ||
|
||
import com.cakey.common.auth.JwtTokenProvider; | ||
import com.cakey.common.auth.UserAuthentication; | ||
import com.cakey.common.auth.JwtValidationType; | ||
import com.cakey.exception.CakeyException; | ||
import com.cakey.exception.ErrorCode; | ||
import com.cakey.jwt.service.TokenService; | ||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.Cookie; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import lombok.NonNull; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
@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 { | ||
try { | ||
final String token = getAccessTokenFromCookie(request); | ||
if (token == null) { | ||
//todo: exception 처리 | ||
} | ||
|
||
if (jwtTokenProvider.validateToken(token) == JwtValidationType.INVALID_JWT_TOKEN) { | ||
//todo: exception 처리 | ||
} | ||
|
||
final Long userId = jwtTokenProvider.getUserFromJwt(token); | ||
SecurityContextHolder.getContext().setAuthentication(new UserAuthentication(userId, null, null)); | ||
|
||
} catch (Exception e) { | ||
} | ||
filterChain.doFilter(request, response); | ||
} | ||
|
||
private String getAccessTokenFromCookie(final HttpServletRequest request) { | ||
Cookie[] cookies = request.getCookies(); | ||
if (cookies != null) { | ||
for(Cookie cookie : cookies) { | ||
if (cookie.getName().equals("accessToken")) { | ||
return cookie.getValue(); | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
cakey-api/src/main/java/com/cakey/user/controller/UserController.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,37 @@ | ||
package com.cakey.user.controller; | ||
|
||
import com.cakey.client.dto.LoginReq; | ||
import com.cakey.common.resolver.user.UserId; | ||
import com.cakey.common.response.ApiResponseUtil; | ||
import com.cakey.common.response.BaseResponse; | ||
import com.cakey.common.response.SuccessCode; | ||
import com.cakey.jwt.service.TokenService; | ||
import com.cakey.user.dto.LoginSuccessRes; | ||
import com.cakey.user.service.UserService; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestHeader; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequestMapping("/api/v1/user") | ||
@RequiredArgsConstructor | ||
public class UserController { | ||
|
||
private final UserService userService; | ||
private final TokenService tokenService; | ||
|
||
@PostMapping("/login") | ||
public ResponseEntity<BaseResponse<?>> login(@RequestHeader(value = "Authorization") final String authorization, | ||
@RequestBody final LoginReq loginReq, | ||
HttpServletResponse response) { | ||
LoginSuccessRes loginSuccessRes = userService.create(authorization, loginReq); | ||
response.addHeader("Set-Cookie", userService.accessCookie(loginSuccessRes).toString()); | ||
response.addHeader("Set-Cookie", userService.refreshCookie(loginSuccessRes).toString()); | ||
return ApiResponseUtil.success(SuccessCode.OK); | ||
} | ||
} |
Oops, something went wrong.