-
Notifications
You must be signed in to change notification settings - Fork 2
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
1 parent
d069275
commit 38177be
Showing
11 changed files
with
469 additions
and
1 deletion.
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,11 @@ | ||
package com.example.rework.auth; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public enum MemberRole { | ||
MEMBER("ROLE_MEMBER"),MANAGER("ROLE_MANAGER"),ADMIN("ROLE_ADMIN"); | ||
private final String key; | ||
} |
38 changes: 38 additions & 0 deletions
38
src/main/java/com/example/rework/auth/cookie/CookieUtil.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,38 @@ | ||
package com.example.rework.auth.cookie; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseCookie; | ||
import org.springframework.stereotype.Component; | ||
@Component | ||
@RequiredArgsConstructor | ||
public class CookieUtil { | ||
public static void addCookie(HttpServletResponse response, String name, String value, long maxAge) { | ||
ResponseCookie cookie = ResponseCookie.from(name, value) | ||
.path("/") | ||
.sameSite("None") // None, Lax, Strict | ||
.httpOnly(true) | ||
// .secure(true) //TODO: secure true로 변경 | ||
.maxAge(maxAge) | ||
.build(); | ||
|
||
response.addHeader("Set-Cookie", cookie.toString()); | ||
} | ||
|
||
public static String getRefreshTokenCookie(HttpServletRequest request) { | ||
jakarta.servlet.http.Cookie[] cookies = request.getCookies(); | ||
|
||
String cookieRefreshToken = ""; | ||
for (jakarta.servlet.http.Cookie cookie : cookies) { | ||
if (cookie.getName().equals("refreshToken")) { | ||
cookieRefreshToken = cookie.getValue(); | ||
} | ||
} | ||
return cookieRefreshToken; | ||
} | ||
public static void deleteRefreshTokenCookie(HttpServletRequest request, HttpServletResponse response) { | ||
CookieUtil.addCookie(response,"refreshToken", null,0); | ||
} | ||
|
||
} |
18 changes: 18 additions & 0 deletions
18
src/main/java/com/example/rework/auth/entity/RefreshToken.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,18 @@ | ||
package com.example.rework.auth.entity; | ||
|
||
import lombok.*; | ||
import org.springframework.data.annotation.Id; | ||
import org.springframework.data.redis.core.RedisHash; | ||
|
||
|
||
@RedisHash(value = "refreshToken", timeToLive = 60 * 60 * 24 * 14) // 2주 동안의 TTL 설정 (초 단위) | ||
@Getter | ||
@Builder | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@ToString | ||
public class RefreshToken { | ||
@Id | ||
private String id; // member username | ||
private String token; | ||
} |
88 changes: 88 additions & 0 deletions
88
src/main/java/com/example/rework/auth/jwt/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,88 @@ | ||
package com.example.rework.auth.jwt; | ||
|
||
import com.example.rework.auth.cookie.CookieUtil; | ||
import com.example.rework.auth.service.RefreshTokenService; | ||
import com.example.rework.member.application.dto.MemberResponseDto; | ||
import com.example.rework.member.application.dto.MemeberRequestDto; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
import java.io.IOException; | ||
|
||
@Slf4j | ||
@RequiredArgsConstructor | ||
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter { | ||
private final RefreshTokenService refreshTokenService; | ||
private final JwtProvider jwtProvider; | ||
|
||
public JwtAuthenticationFilter(AuthenticationManager authenticationManager, RefreshTokenService refreshTokenService, JwtProvider jwtProvider) { | ||
super(authenticationManager); | ||
this.refreshTokenService = refreshTokenService; | ||
this.jwtProvider = jwtProvider; | ||
setFilterProcessesUrl("/api/v1/members/login"); | ||
} | ||
|
||
@Override | ||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { | ||
log.info("JwtAuthenticationFilter.attemptAuthentication"); | ||
|
||
ObjectMapper objectMapper = new ObjectMapper(); | ||
MemeberRequestDto.MemberLoginRequestDto memberLoginRequestDto = null; | ||
try { | ||
memberLoginRequestDto = objectMapper.readValue(request.getInputStream(), MemeberRequestDto.MemberLoginRequestDto.class); | ||
} catch (Exception e) { | ||
// no login request dto | ||
log.info("no login request dto"); | ||
throw new UsernameNotFoundException("계정이 존재하지 않습니다."); | ||
} | ||
|
||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(memberLoginRequestDto.getUserId(), memberLoginRequestDto.getPassword()); | ||
Authentication authentication = null; | ||
authentication = this.getAuthenticationManager().authenticate(usernamePasswordAuthenticationToken); | ||
|
||
return authentication; | ||
} | ||
|
||
@Override | ||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException { | ||
log.info("JwtAuthenticationFilter.successfulAuthentication"); | ||
|
||
MemberDetails memberDetails = (MemberDetails) authentication.getPrincipal(); | ||
|
||
String accessToken = jwtProvider.generateAccessToken(memberDetails.getUsername(),authentication); | ||
String refreshToken = jwtProvider.generateRefreshToken(memberDetails.getUsername(),authentication); | ||
|
||
refreshTokenService.setRefreshToken(memberDetails.getUsername(), refreshToken); | ||
|
||
response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
response.setStatus(HttpServletResponse.SC_OK); | ||
CookieUtil.addCookie(response, "refreshToken", refreshToken, jwtProvider.REFRESH_TOKEN_EXPIRATION_TIME); | ||
|
||
// token body comment | ||
response.getWriter().write( | ||
new ObjectMapper().writeValueAsString( | ||
MemberResponseDto.MemberLoginResponseDto.builder() | ||
.accessToken(accessToken) | ||
.build() | ||
) | ||
); | ||
} | ||
|
||
@Override | ||
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { | ||
log.info("JwtAuthenticationFilter.unsuccessfulAuthentication"); | ||
throw new UsernameNotFoundException("계정이 존재하지 않습니다."); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/main/java/com/example/rework/auth/jwt/JwtAuthorizationFilter.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,51 @@ | ||
package com.example.rework.auth.jwt; | ||
|
||
import com.example.rework.member.application.MemberService; | ||
import com.example.rework.member.domain.Member; | ||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; | ||
|
||
import java.io.IOException; | ||
|
||
@Slf4j | ||
public class JwtAuthorizationFilter extends BasicAuthenticationFilter { | ||
private final MemberService memberService; | ||
private final JwtProvider jwtProvider; | ||
|
||
|
||
public JwtAuthorizationFilter(AuthenticationManager authenticationManager, MemberService memberService, JwtProvider jwtProvider) { | ||
super(authenticationManager); | ||
this.memberService = memberService; | ||
this.jwtProvider = jwtProvider; | ||
} | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { | ||
log.info("JwtAuthorizationFilter.doFilterInternal"); | ||
String path = request.getServletPath(); | ||
System.out.println("path = " + path); | ||
String header = jwtProvider.getHeader(request); | ||
if (header == null) { | ||
chain.doFilter(request, response); | ||
return; | ||
} | ||
|
||
String username = null; | ||
username = jwtProvider.getUserId(request); | ||
if (username != null) { | ||
Member member = memberService.findMemberByUserId(username); | ||
MemberDetails memberDetails = new MemberDetails(member); | ||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(memberDetails, null, null); | ||
SecurityContextHolder.getContext().setAuthentication(authentication); | ||
} | ||
chain.doFilter(request, response); | ||
} | ||
|
||
} |
127 changes: 127 additions & 0 deletions
127
src/main/java/com/example/rework/auth/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,127 @@ | ||
package com.example.rework.auth.jwt; | ||
|
||
|
||
import com.auth0.jwt.JWT; | ||
import com.auth0.jwt.algorithms.Algorithm; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.security.core.userdetails.User; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.Date; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
@Slf4j | ||
@Component | ||
public class JwtProvider { | ||
|
||
@Value("${jwt.secret-key}") | ||
private String SECRET_KEY; | ||
|
||
public final long ACCESS_TOKEN_EXPIRATION_TIME = 1000L * 60 * 60; //1 hours | ||
public final long REFRESH_TOKEN_EXPIRATION_TIME = 1000L * 60 * 60 * 24 * 14; // 14 week | ||
|
||
private final String HEADER_NAME = "Authorization"; | ||
private final String TOKEN_PREFIX = "Bearer "; | ||
|
||
public String generateAccessToken(String username, Authentication authentication) { | ||
|
||
|
||
List<String> roles = authentication.getAuthorities().stream() | ||
.map(GrantedAuthority::getAuthority) | ||
.collect(Collectors.toList()); | ||
return JWT.create() | ||
.withSubject(username) | ||
.withExpiresAt(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_TIME)) | ||
.withClaim("username", username) | ||
.withClaim("ROLE",roles) | ||
.sign(Algorithm.HMAC512(SECRET_KEY)); | ||
} | ||
|
||
|
||
public String generateRefreshToken(String username, Authentication authentication) { | ||
List<String> roles = authentication.getAuthorities().stream() | ||
.map(GrantedAuthority::getAuthority) | ||
.collect(Collectors.toList()); | ||
|
||
return JWT.create() | ||
.withSubject(username) | ||
.withExpiresAt(new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRATION_TIME)) | ||
.withClaim("username", username) | ||
.withClaim("ROLE",roles) | ||
.sign(Algorithm.HMAC512(SECRET_KEY)); | ||
} | ||
|
||
public boolean verifyToken(String token) { | ||
try { | ||
JWT.require(Algorithm.HMAC512(SECRET_KEY)).build().verify(token); | ||
} catch (Exception e) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
public String getHeader(HttpServletRequest request) { | ||
String header = request.getHeader(HEADER_NAME); | ||
if (header != null && !header.startsWith(TOKEN_PREFIX)) header = null; | ||
return header; | ||
} | ||
|
||
public String getUserId(HttpServletRequest request) { | ||
String accessToken = getHeader(request).replace(TOKEN_PREFIX, ""); | ||
System.out.println("test=>"+accessToken); | ||
return JWT.require(Algorithm.HMAC512(SECRET_KEY)) | ||
.build() | ||
.verify(accessToken) | ||
.getClaim("username").asString(); | ||
} | ||
public String getUsernameFromToken(String token) { | ||
return JWT.require(Algorithm.HMAC512(SECRET_KEY)) | ||
.build() | ||
.verify(token) | ||
.getClaim("username").asString(); | ||
} | ||
public List<String> getRolesFromToken(String token) { | ||
return JWT.require(Algorithm.HMAC512(SECRET_KEY)) | ||
.build() | ||
.verify(token) | ||
.getClaim("ROLE") | ||
.asList(String.class); | ||
} | ||
public Authentication getAuthentication(String token) { | ||
String username = getUsernameFromToken(token); | ||
List<String> roles = getRolesFromToken(token); | ||
|
||
List<SimpleGrantedAuthority> authorities = roles.stream() | ||
.map(SimpleGrantedAuthority::new) | ||
.collect(Collectors.toList()); | ||
|
||
User principal = new User(username, "", authorities); | ||
|
||
return new UsernamePasswordAuthenticationToken(principal, "", authorities); | ||
} | ||
|
||
public Authentication validateAndSetAuthentication(String refreshToken) { | ||
Authentication authentication = this.getAuthentication(refreshToken); | ||
|
||
if (authentication == null || !authentication.isAuthenticated()) { | ||
throw new AuthenticationCredentialsNotFoundException("User not authenticated"); | ||
} | ||
|
||
this.verifyToken(refreshToken); | ||
return authentication; | ||
} | ||
|
||
public String renewRefreshToken(Authentication authentication) { | ||
return this.generateRefreshToken(authentication.getName(), authentication); | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.