-
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.
- Loading branch information
Showing
5 changed files
with
226 additions
and
4 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
58 changes: 58 additions & 0 deletions
58
src/main/java/com/wedit/weditapp/domain/member/service/MemberService.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 |
---|---|---|
@@ -1,4 +1,62 @@ | ||
package com.wedit.weditapp.domain.member.service; | ||
|
||
import com.wedit.weditapp.domain.member.domain.Member; | ||
import com.wedit.weditapp.domain.member.domain.repository.MemberRepository; | ||
import com.wedit.weditapp.domain.member.dto.LoginRequestDto; | ||
import com.wedit.weditapp.domain.member.dto.MemberRequestDto; | ||
import com.wedit.weditapp.global.error.ErrorCode; | ||
import com.wedit.weditapp.global.error.exception.CommonException; | ||
import com.wedit.weditapp.global.security.jwt.JwtProvider; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.List; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class MemberService { | ||
private final MemberRepository memberRepository; | ||
private final JwtProvider jwtProvider; // JWT 발급을 위해 | ||
|
||
// [회원가입 관련] - 이미 존재하는 이메일인지 검사 + Member 엔티티 생성 + DB저장 및 반환 | ||
public Member createMember(MemberRequestDto requestDto) { | ||
if (memberRepository.findByEmail(requestDto.getEmail()).isPresent()) { | ||
throw new CommonException(ErrorCode.MEMBER_ALREADY_EXISTS); | ||
} | ||
|
||
Member member = Member.createUser( | ||
requestDto.getEmail(), | ||
requestDto.getPassword(), | ||
requestDto.getName() | ||
); | ||
|
||
return memberRepository.save(member); | ||
} | ||
|
||
public String login(LoginRequestDto loginRequest) { | ||
// 이메일로 회원 조회 | ||
Member member = memberRepository.findByEmail(loginRequest.getEmail()) | ||
.orElseThrow(() -> new CommonException(ErrorCode.USER_NOT_FOUND)); | ||
|
||
// 비밀번호 검증 (임시: 평문 비교) | ||
if (!member.getPassword().equals(loginRequest.getPassword())) { | ||
throw new CommonException(ErrorCode.LOGIN_FAIL); | ||
} | ||
|
||
// JWT Access Token 생성 | ||
return jwtProvider.createAccessToken(member.getEmail()); | ||
} | ||
|
||
// [모든 회원 조회] | ||
public List<Member> findAllMembers() { | ||
return memberRepository.findAll(); | ||
} | ||
|
||
// [단일 회원 조회] | ||
public Member findMemberById(Long userId) { | ||
return memberRepository.findById(userId) | ||
.orElseThrow(() -> new CommonException(ErrorCode.USER_NOT_FOUND) | ||
); | ||
} | ||
|
||
} |
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
58 changes: 58 additions & 0 deletions
58
src/main/java/com/wedit/weditapp/global/security/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,58 @@ | ||
package com.wedit.weditapp.global.security.jwt; | ||
|
||
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.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
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 JwtProvider jwtProvider; | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, | ||
HttpServletResponse response, | ||
FilterChain filterChain) | ||
throws ServletException, IOException { | ||
|
||
// 헤더에서 토큰 추출 | ||
String token = resolveToken(request); | ||
|
||
// 토큰 검증 | ||
if (token != null && jwtProvider.validateToken(token)) { | ||
// 토큰에서 사용자 정보 추출 | ||
String email = jwtProvider.getEmail(token); | ||
|
||
// 임시로 Role없이 Authentication 객체 생성 -> 추후 추가 예정 | ||
UsernamePasswordAuthenticationToken authentication = | ||
new UsernamePasswordAuthenticationToken(email, null, null); | ||
|
||
// SecurityContext에 저장 | ||
SecurityContextHolder.getContext().setAuthentication(authentication); | ||
} | ||
|
||
// 다음 필터로 진행 | ||
filterChain.doFilter(request, response); | ||
} | ||
|
||
// "Authorization: Bearer <token>" 헤더에서 token만 파싱 | ||
private String resolveToken(HttpServletRequest request) { | ||
String bearerToken = request.getHeader("Authorization"); | ||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { | ||
return bearerToken.substring(7); | ||
} | ||
return null; | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
src/main/java/com/wedit/weditapp/global/security/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,81 @@ | ||
package com.wedit.weditapp.global.security.jwt; | ||
|
||
import io.jsonwebtoken.*; | ||
import io.jsonwebtoken.security.Keys; | ||
import jakarta.annotation.PostConstruct; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.crypto.SecretKey; | ||
import java.nio.charset.StandardCharsets; | ||
import java.security.Key; | ||
import java.util.Date; | ||
|
||
@Slf4j | ||
@Component | ||
public class JwtProvider { | ||
|
||
@Value("${jwt.secretKey}") | ||
private String secretKey; | ||
|
||
@Value("${jwt.access.expiration}") | ||
private long accessTokenExpiry; // 만료 시간 : 3600000 (1시간) | ||
|
||
private Key key; // 실제 사용할 HMAC용 key 객체 | ||
|
||
// Bean 생성 직후 secretKey를 Key 객체로 변환 | ||
@PostConstruct | ||
protected void init() { | ||
this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
|
||
// JWT Access Token 생성 | ||
/* | ||
* - subject로 email (또는 userId 등) 지정 | ||
* - 만료시간 설정 | ||
* - 서명 알고리즘: HS256 | ||
*/ | ||
public String createAccessToken(String email) { | ||
Date now = new Date(); | ||
Date expiry = new Date(now.getTime() + accessTokenExpiry); | ||
|
||
return Jwts.builder() | ||
.subject(email) | ||
.issuedAt(now) | ||
.expiration(expiry) | ||
.signWith(key, SignatureAlgorithm.HS256) | ||
.compact(); | ||
} | ||
|
||
// JWT Access Token 유효성 검증 | ||
public boolean validateToken(String token) { | ||
try { | ||
// 토큰 파싱 -> 에러 없으면 유효 | ||
Jwts.parser() | ||
.verifyWith((SecretKey) key) | ||
.build() | ||
.parseSignedClaims(token); | ||
return true; | ||
} catch (SecurityException | MalformedJwtException e) { | ||
log.error("Invalid JWT signature: {}", e.getMessage()); | ||
} catch (ExpiredJwtException e) { | ||
log.error("Expired JWT token: {}", e.getMessage()); | ||
} catch (UnsupportedJwtException e) { | ||
log.error("Unsupported JWT token: {}", e.getMessage()); | ||
} catch (IllegalArgumentException e) { | ||
log.error("JWT token compact of handler are invalid: {}", e.getMessage()); | ||
} | ||
return false; | ||
} | ||
|
||
// 토큰에서 email(Subject) 추출 | ||
public String getEmail(String token) { | ||
return Jwts.parser() | ||
.verifyWith((SecretKey) key) | ||
.build() | ||
.parseSignedClaims(token).getPayload() | ||
.getSubject(); | ||
} | ||
} |