From 4f65595961d967e6d2b3a65c83f691ffee15317d Mon Sep 17 00:00:00 2001 From: jiiiiiw Date: Thu, 29 Feb 2024 16:07:36 +0900 Subject: [PATCH] =?UTF-8?q?:sparkles:=20feat:=20jwt=20=EB=94=94=EB=A0=89?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=9E=91=EC=84=B1=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwt/config/JwtAuthenticationFilter.java | 37 +++++++++ .../account/jwt/config/JwtProvider.java | 83 +++++++++++++++++++ .../domain/account/jwt/dto/JwtDto.java | 6 ++ 3 files changed, 126 insertions(+) create mode 100644 src/main/java/com/smunity/petition/domain/account/jwt/config/JwtAuthenticationFilter.java create mode 100644 src/main/java/com/smunity/petition/domain/account/jwt/config/JwtProvider.java create mode 100644 src/main/java/com/smunity/petition/domain/account/jwt/dto/JwtDto.java diff --git a/src/main/java/com/smunity/petition/domain/account/jwt/config/JwtAuthenticationFilter.java b/src/main/java/com/smunity/petition/domain/account/jwt/config/JwtAuthenticationFilter.java new file mode 100644 index 0000000..fd48d13 --- /dev/null +++ b/src/main/java/com/smunity/petition/domain/account/jwt/config/JwtAuthenticationFilter.java @@ -0,0 +1,37 @@ +package com.smunity.petition.domain.account.jwt.config; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.GenericFilterBean; + +import java.io.IOException; + +@RequiredArgsConstructor +@Slf4j +public class JwtAuthenticationFilter extends GenericFilterBean { + private final JwtProvider jwtProvider; + + @Override + public void doFilter(ServletRequest request, + ServletResponse response, + FilterChain filterChain) throws IOException, ServletException { + String token = jwtProvider.resolveToken((HttpServletRequest) request); + + // 검증 + log.info("[Verifying token]"); + log.info(((HttpServletRequest) request).getRequestURL().toString()); + + if (token != null && jwtProvider.validationToken(token)) { + Authentication authentication = jwtProvider.getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(request, response); + } +} diff --git a/src/main/java/com/smunity/petition/domain/account/jwt/config/JwtProvider.java b/src/main/java/com/smunity/petition/domain/account/jwt/config/JwtProvider.java new file mode 100644 index 0000000..d5937b7 --- /dev/null +++ b/src/main/java/com/smunity/petition/domain/account/jwt/config/JwtProvider.java @@ -0,0 +1,83 @@ +package com.smunity.petition.domain.account.jwt.config; + +import com.smunity.petition.domain.account.jwt.userdetails.CustomUserDetailService; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.util.Base64; +import java.util.Date; +import java.util.List; + +@RequiredArgsConstructor +@Component +public class JwtProvider { + @Value("${spring.jwt.secret}") + private String secretKey; + private String ROLES = "roles"; + + private long tokenValidMillisecond = 60 * 60 * 1000L; // 1 hour + + private final CustomUserDetailService userDetailService; + + private SecretKey key; + + @PostConstruct + protected void init() { + byte[] decodedKey = Base64.getDecoder().decode(secretKey); + key = Keys.hmacShaKeyFor(decodedKey); + } + + // Jwt 생성 + public String createToken(String userPk, List roles) { + + // user 구분을 위해 Claims 에 User Pk 값을 넣어줌 + Claims claims = Jwts.claims().subject(String.valueOf(userPk)).build(); +// claims.put(ROLES, roles); + // 생성 날짜, 만료 날짜를 위한 Date + Date now = new Date(); + + return Jwts.builder() + .claims(claims) + .issuedAt(now) + .expiration(new Date(now.getTime() + tokenValidMillisecond)) + .signWith(key) + .compact(); + } + + // Jwt 로 인증 정보 조회 + public Authentication getAuthentication (String token) { + UserDetails userDetails = userDetailService.loadUserByUsername(this.getUserPk(token)); + return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); + } + + // jwt 에서 회원 구분 pk 추출 + public String getUserPk(String token) { + return Jwts.parser().verifyWith(key).build().parseSignedClaims(token).getPayload().getSubject(); + } + + // HTTP Request 의 Header 에서 Token Parsing -> "X-AUTH-TOKEN: jwt" + public String resolveToken(HttpServletRequest request) { + return request.getHeader("X-AUTH-TOKEN"); + } + + // jwt 의 유효성 및 만료일자 확인 + public boolean validationToken(String token) { + try { + Jws claimsJws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token); + return !claimsJws.getPayload().getExpiration().before(new Date()); // 만료 날짜가 현재에 비해 전이면 false + } catch (Exception e) { + return false; + } + } +} diff --git a/src/main/java/com/smunity/petition/domain/account/jwt/dto/JwtDto.java b/src/main/java/com/smunity/petition/domain/account/jwt/dto/JwtDto.java new file mode 100644 index 0000000..903a85c --- /dev/null +++ b/src/main/java/com/smunity/petition/domain/account/jwt/dto/JwtDto.java @@ -0,0 +1,6 @@ +package com.smunity.petition.domain.account.jwt.dto; + +public record JwtDto( + String accessToken, + String refreshToken) { +}