forked from kookmin-sw/cap-template
-
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.
Merge pull request #21 from kookmin-sw/BE
- Loading branch information
Showing
38 changed files
with
1,019 additions
and
8 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
33 changes: 33 additions & 0 deletions
33
backend/src/main/java/com/project/capstone/auth/controller/AuthController.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,33 @@ | ||
package com.project.capstone.auth.controller; | ||
|
||
import com.project.capstone.auth.controller.dto.LoginRequest; | ||
import com.project.capstone.auth.controller.dto.SignupRequest; | ||
import com.project.capstone.auth.controller.dto.TokenResponse; | ||
import com.project.capstone.auth.service.AuthService; | ||
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.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RequestMapping("/auth") | ||
@RequiredArgsConstructor | ||
@RestController | ||
public class AuthController { | ||
|
||
private final AuthService authService; | ||
|
||
@PostMapping("/signup") | ||
public ResponseEntity<TokenResponse> signup(@RequestBody SignupRequest request) { | ||
authService.signup(request); | ||
TokenResponse tokenResponse = authService.login(request.email()); | ||
return ResponseEntity.ok().body(tokenResponse); | ||
} | ||
|
||
@PostMapping("/login") | ||
public ResponseEntity<TokenResponse> login(@RequestBody LoginRequest request) { | ||
TokenResponse tokenResponse = authService.login(request.email()); | ||
return ResponseEntity.ok().body(tokenResponse); | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
backend/src/main/java/com/project/capstone/auth/controller/dto/LoginRequest.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,6 @@ | ||
package com.project.capstone.auth.controller.dto; | ||
|
||
public record LoginRequest( | ||
String email | ||
) { | ||
} |
9 changes: 9 additions & 0 deletions
9
backend/src/main/java/com/project/capstone/auth/controller/dto/SignupRequest.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,9 @@ | ||
package com.project.capstone.auth.controller.dto; | ||
|
||
public record SignupRequest( | ||
String email, | ||
String name, | ||
int age, | ||
String gender | ||
) { | ||
} |
6 changes: 6 additions & 0 deletions
6
backend/src/main/java/com/project/capstone/auth/controller/dto/TokenResponse.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,6 @@ | ||
package com.project.capstone.auth.controller.dto; | ||
|
||
public record TokenResponse( | ||
String token | ||
) { | ||
} |
52 changes: 52 additions & 0 deletions
52
backend/src/main/java/com/project/capstone/auth/domain/PrincipalDetails.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 com.project.capstone.auth.domain; | ||
|
||
import com.project.capstone.member.domain.Member; | ||
import lombok.AllArgsConstructor; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
|
||
import java.util.Collection; | ||
import java.util.UUID; | ||
|
||
@AllArgsConstructor | ||
public class PrincipalDetails implements UserDetails { | ||
|
||
private Member member; | ||
|
||
@Override | ||
public Collection<? extends GrantedAuthority> getAuthorities() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String getPassword() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String getUsername() { | ||
return member.getEmail(); | ||
} | ||
|
||
public String getUserId() {return member.getId().toString(); } | ||
|
||
@Override | ||
public boolean isAccountNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonLocked() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isCredentialsNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isEnabled() { | ||
return true; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
backend/src/main/java/com/project/capstone/auth/exception/AuthException.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 com.project.capstone.auth.exception; | ||
|
||
import com.project.capstone.common.exception.BaseException; | ||
import com.project.capstone.common.exception.ExceptionType; | ||
|
||
public class AuthException extends BaseException { | ||
public AuthException(ExceptionType exceptionType) { | ||
super(exceptionType); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
backend/src/main/java/com/project/capstone/auth/exception/AuthExceptionType.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,39 @@ | ||
package com.project.capstone.auth.exception; | ||
|
||
import com.project.capstone.common.exception.ExceptionType; | ||
import lombok.AllArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
|
||
import static org.springframework.http.HttpStatus.*; | ||
|
||
@AllArgsConstructor | ||
public enum AuthExceptionType implements ExceptionType { | ||
ALREADY_EMAIL_EXIST(BAD_REQUEST, 1000, "이메일이 이미 존재합니다."), | ||
EMAIL_NOT_FOUND(NOT_FOUND, 1001, "이메일을 찾을 수 없습니다."), | ||
SIGNATURE_NOT_FOUND(UNAUTHORIZED, 1002, "서명을 확인하지 못했습니다"), | ||
SIGNATURE_INVALID(UNAUTHORIZED, 1003, "서명이 올바르지 않습니다."), | ||
MALFORMED_TOKEN(UNAUTHORIZED, 1004, "토큰의 길이 및 형식이 올바르지 않습니다"), | ||
EXPIRED_TOKEN(UNAUTHORIZED, 1005, "이미 만료된 토큰입니다"), | ||
UNSUPPORTED_TOKEN(UNAUTHORIZED, 1006, "지원되지 않는 토큰입니다"), | ||
INVALID_TOKEN(UNAUTHORIZED, 1007, "토큰이 유효하지 않습니다"), | ||
; | ||
|
||
private final HttpStatus status; | ||
private final int exceptionCode; | ||
private final String message; | ||
|
||
@Override | ||
public HttpStatus httpStatus() { | ||
return status; | ||
} | ||
|
||
@Override | ||
public int exceptionCode() { | ||
return exceptionCode; | ||
} | ||
|
||
@Override | ||
public String message() { | ||
return message; | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
backend/src/main/java/com/project/capstone/auth/jwt/JwtAuthenticationEntryPoint.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,29 @@ | ||
package com.project.capstone.auth.jwt; | ||
|
||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.AuthenticationEntryPoint; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.servlet.HandlerExceptionResolver; | ||
|
||
import java.io.IOException; | ||
|
||
@Slf4j | ||
@Component | ||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||
|
||
private final HandlerExceptionResolver resolver; | ||
|
||
public JwtAuthenticationEntryPoint(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver) { | ||
this.resolver = resolver; | ||
} | ||
|
||
@Override | ||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { | ||
resolver.resolveException(request, response, null, (Exception) request.getAttribute("exception")); | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
backend/src/main/java/com/project/capstone/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,62 @@ | ||
package com.project.capstone.auth.jwt; | ||
|
||
|
||
import com.project.capstone.auth.exception.AuthException; | ||
import com.project.capstone.member.exception.MemberException; | ||
import io.jsonwebtoken.ExpiredJwtException; | ||
import io.jsonwebtoken.MalformedJwtException; | ||
import io.jsonwebtoken.UnsupportedJwtException; | ||
import io.jsonwebtoken.security.SignatureException; | ||
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.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
import java.io.IOException; | ||
|
||
import static com.project.capstone.auth.exception.AuthExceptionType.*; | ||
import static com.project.capstone.member.exception.MemberExceptionType.*; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
@Slf4j | ||
public class JwtAuthenticationFilter extends OncePerRequestFilter { | ||
|
||
private final JwtProvider jwtProvider; | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { | ||
String token = jwtProvider.resolveToken(request); | ||
try { | ||
String id = jwtProvider.validateTokenAndGetId(token); | ||
Authentication authentication = jwtProvider.createAuthentication(id); | ||
log.info(authentication.getName()); | ||
SecurityContextHolder.getContext().setAuthentication(authentication); | ||
} catch (SecurityException e) { | ||
request.setAttribute("exception", new AuthException(SIGNATURE_NOT_FOUND)); | ||
} catch (SignatureException e) { | ||
request.setAttribute("exception", new AuthException(SIGNATURE_INVALID)); | ||
} catch (MalformedJwtException e) { | ||
request.setAttribute("exception", new AuthException(MALFORMED_TOKEN)); | ||
} catch (ExpiredJwtException e) { | ||
request.setAttribute("exception", new AuthException(EXPIRED_TOKEN)); | ||
} catch (UnsupportedJwtException e) { | ||
request.setAttribute("exception", new AuthException(UNSUPPORTED_TOKEN)); | ||
} catch (IllegalArgumentException e) { | ||
request.setAttribute("exception", new AuthException(INVALID_TOKEN)); | ||
} catch (UsernameNotFoundException e) { | ||
request.setAttribute("exception", new MemberException(MEMBER_NOT_FOUND)); | ||
} catch (Exception e) { | ||
request.setAttribute("exception", new Exception()); | ||
} | ||
|
||
filterChain.doFilter(request, response); | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
backend/src/main/java/com/project/capstone/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,91 @@ | ||
package com.project.capstone.auth.jwt; | ||
|
||
import com.project.capstone.auth.service.PrincipalDetailService; | ||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.SignatureAlgorithm; | ||
import io.jsonwebtoken.security.Keys; | ||
import jakarta.annotation.PostConstruct; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
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 org.springframework.util.StringUtils; | ||
|
||
import java.security.Key; | ||
import java.time.LocalDateTime; | ||
import java.time.ZoneId; | ||
import java.util.Date; | ||
import java.util.UUID; | ||
|
||
@Slf4j | ||
@Component | ||
@RequiredArgsConstructor | ||
public class JwtProvider { | ||
|
||
@Value("${jwt.secret}") | ||
private String secret; | ||
private Key key; | ||
private static final int EXPIRED_DURATION = 24; | ||
private static final String AUTHORIZATION_HEADER = "Authorization"; | ||
private static final String GRANT_TYPE = "Bearer "; | ||
private final PrincipalDetailService principalDetailService; | ||
|
||
@PostConstruct | ||
private void init() { | ||
key = Keys.hmacShaKeyFor(secret.getBytes()); | ||
} | ||
|
||
public String generate(String id) { | ||
Claims claims = Jwts.claims(); | ||
claims.put("id", id); | ||
return generateToken(claims); | ||
} | ||
|
||
private String generateToken(Claims claims) { | ||
return Jwts.builder() | ||
.setClaims(claims) | ||
.setIssuedAt(issueAt()) | ||
.setExpiration(expireAt()) | ||
.signWith(key, SignatureAlgorithm.HS512) | ||
.compact(); | ||
} | ||
|
||
private Date expireAt() { | ||
LocalDateTime now = LocalDateTime.now(); | ||
log.info(Date.from(now.plusHours(EXPIRED_DURATION).atZone(ZoneId.systemDefault()).toInstant()).toString()); | ||
return Date.from(now.plusHours(EXPIRED_DURATION).atZone(ZoneId.systemDefault()).toInstant()); | ||
} | ||
|
||
private Date issueAt() { | ||
LocalDateTime now = LocalDateTime.now(); | ||
log.info(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()).toString()); | ||
return Date.from(now.atZone(ZoneId.systemDefault()).toInstant()); | ||
} | ||
|
||
public String resolveToken(HttpServletRequest request) { | ||
String bearToken = request.getHeader(AUTHORIZATION_HEADER); | ||
if (StringUtils.hasText(bearToken) && bearToken.startsWith(GRANT_TYPE)) { | ||
return bearToken.substring(7); | ||
} | ||
return null; | ||
} | ||
|
||
public String validateTokenAndGetId(String token) { | ||
return Jwts.parserBuilder() | ||
.setSigningKey(key) | ||
.build() | ||
.parseClaimsJws(token) | ||
.getBody() | ||
.get("id", String.class); | ||
} | ||
|
||
public Authentication createAuthentication(String id) { | ||
UserDetails userDetails = principalDetailService.loadUserByUsername(id); | ||
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
backend/src/main/java/com/project/capstone/auth/service/AuthService.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.project.capstone.auth.service; | ||
|
||
import com.project.capstone.auth.controller.dto.SignupRequest; | ||
import com.project.capstone.auth.controller.dto.TokenResponse; | ||
import com.project.capstone.auth.exception.AuthException; | ||
import com.project.capstone.auth.jwt.JwtProvider; | ||
import com.project.capstone.member.domain.Member; | ||
import com.project.capstone.member.domain.MemberRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import static com.project.capstone.auth.exception.AuthExceptionType.*; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class AuthService { | ||
|
||
private final MemberRepository memberRepository; | ||
private final JwtProvider jwtProvider; | ||
public void signup(SignupRequest request) { | ||
if (memberRepository.findMemberByEmail(request.email()).isPresent()) { | ||
throw new AuthException(ALREADY_EMAIL_EXIST); | ||
} | ||
memberRepository.save(new Member(request)); | ||
} | ||
@Transactional | ||
public TokenResponse login(String email) { | ||
Member member = memberRepository.findMemberByEmail(email) | ||
.orElseThrow(() -> new AuthException(EMAIL_NOT_FOUND)); | ||
return new TokenResponse(generateToken(member)); | ||
} | ||
|
||
private String generateToken(Member member) { | ||
return jwtProvider.generate(member.getId().toString()); | ||
} | ||
} |
Oops, something went wrong.