-
Notifications
You must be signed in to change notification settings - Fork 1
과제 1-2 pgsshiho 과제제출 #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
pgsshiho
wants to merge
5
commits into
master
Choose a base branch
from
shiho
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
d1a4587
task1-2
pgsshiho 00fc3ad
task1-2
pgsshiho a892f91
feedback,but I don't know about I clear the first feedback
pgsshiho 7593d43
feedback,but I don't know about I clear the first feedback
pgsshiho 074cb14
feedback,but I don't know about I clear the first feedback
pgsshiho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
61 changes: 61 additions & 0 deletions
61
...ain/java/com/gsm/_8th/class4/backend/task12/domain/auth/Controller/newsignController.java
This file contains hidden or 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,61 @@ | ||
| package com.gsm._8th.class4.backend.task12.domain.auth.Controller; | ||
|
|
||
| import com.gsm._8th.class4.backend.task12.domain.auth.Repository.UserRepository; | ||
| import com.gsm._8th.class4.backend.task12.domain.auth.Service.newsignService; | ||
| import com.gsm._8th.class4.backend.task12.domain.auth.dto.UserLoginRequest; | ||
| import com.gsm._8th.class4.backend.task12.domain.auth.dto.UserSignupRequest; | ||
| import com.gsm._8th.class4.backend.task12.global.security.TokenResponse; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.*; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/api/user") | ||
| @RequiredArgsConstructor | ||
| public class newsignController { | ||
|
|
||
| private final newsignService authService; | ||
| private final UserRepository userRepository; | ||
| @PostMapping("/signup") | ||
| public ResponseEntity<String> signup(@RequestBody UserSignupRequest request) { | ||
| authService.signup(request); | ||
| return ResponseEntity.ok("회원가입 성공"); | ||
| } | ||
|
snowykte0426 marked this conversation as resolved.
Outdated
|
||
|
|
||
| @PostMapping("/login") | ||
|
snowykte0426 marked this conversation as resolved.
Outdated
|
||
| public ResponseEntity<?> login(@RequestBody UserLoginRequest request) { | ||
| TokenResponse tokenResponse = authService.login(request.getUsername(), request.getPassword()); | ||
|
|
||
| if (tokenResponse != null) { | ||
| return ResponseEntity.ok(tokenResponse); | ||
| } else { | ||
| return ResponseEntity.status(401).body("로그인 실패: 아이디 또는 비밀번호가 잘못되었습니다."); | ||
| } | ||
| } | ||
|
|
||
| @PostMapping("/refresh") | ||
| public ResponseEntity<?> refreshToken(@RequestHeader("Refresh-Token") String refreshToken) { | ||
| TokenResponse newTokens = authService.refreshToken(refreshToken); | ||
|
|
||
| if (newTokens != null) { | ||
| return ResponseEntity.ok(newTokens); | ||
| } else { | ||
| return ResponseEntity.status(403).body("유효하지 않은 리프레시 토큰입니다."); | ||
| } | ||
| } | ||
| @DeleteMapping("/delete") | ||
| public ResponseEntity<?> deleteUser(@RequestBody UserLoginRequest request) { | ||
|
snowykte0426 marked this conversation as resolved.
Outdated
|
||
| TokenResponse tokenResponse = authService.login(request.getUsername(), request.getPassword()); | ||
|
|
||
| if (tokenResponse != null) { | ||
| return userRepository.findByUsername(request.getUsername()) | ||
| .map(user -> { | ||
| userRepository.delete(user); | ||
| return ResponseEntity.ok("계정 삭제 완료"); | ||
| }) | ||
| .orElse(ResponseEntity.status(404).body("사용자를 찾을 수 없습니다.")); | ||
| } else { | ||
| return ResponseEntity.status(401).body("로그인 실패: 아이디 또는 비밀번호가 잘못되었습니다."); | ||
|
snowykte0426 marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
| } | ||
28 changes: 28 additions & 0 deletions
28
src/main/java/com/gsm/_8th/class4/backend/task12/domain/auth/Entity/NewSign.java
This file contains hidden or 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,28 @@ | ||
| package com.gsm._8th.class4.backend.task12.domain.auth.Entity; | ||
|
|
||
| import jakarta.persistence.*; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| @Entity | ||
| @Getter | ||
| @AllArgsConstructor | ||
| @NoArgsConstructor | ||
| @Builder | ||
| @Table(name = "new_sign") | ||
| public class NewSign { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| private Long id; | ||
| private String username; | ||
| private String password; | ||
| private String role; | ||
| private String email; | ||
| } | ||
|
Comment on lines
+1
to
+28
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Entity 이름이 만약 회원가입이 승인제로 이뤄지는 서비스여서 새 계정 정보를 승인 전까지 임시로 저장하는 테이블이라면 나름 적합할 수도 있겠지만... |
||
18 changes: 18 additions & 0 deletions
18
src/main/java/com/gsm/_8th/class4/backend/task12/domain/auth/HTML/login.html
This file contains hidden or 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 @@ | ||
| <!DOCTYPE html> | ||
| <html xmlns:th="http://www.thymeleaf.org"> | ||
| <head> | ||
| <title>로그인</title> | ||
| </head> | ||
| <body> | ||
| <h2>로그인</h2> | ||
| <form action="/login" method="post"> | ||
| <label>사용자 이름:</label> | ||
| <input type="text" name="username" required> | ||
| <br> | ||
| <label>비밀번호:</label> | ||
| <input type="password" name="password" required> | ||
| <br> | ||
| <button type="submit">로그인</button> | ||
| </form> | ||
| </body> | ||
| </html> |
19 changes: 19 additions & 0 deletions
19
src/main/java/com/gsm/_8th/class4/backend/task12/domain/auth/HTML/signup.html
This file contains hidden or 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,19 @@ | ||
| <!DOCTYPE html> | ||
| <html xmlns:th="http://www.thymeleaf.org"> | ||
| <head> | ||
| <title>회원가입</title> | ||
| </head> | ||
| <body> | ||
| <h2>회원가입</h2> | ||
| <form action="/user/signup" method="post"> | ||
| <label>사용자 이름:</label> | ||
| <input type="text" name="username" required> | ||
| <br> | ||
| <label>비밀번호:</label> | ||
| <input type="password" name="password" required> | ||
| <br> | ||
| <button type="submit">회원가입</button> | ||
| </form> | ||
| <a href="/user/login">로그인</a> | ||
| </body> | ||
| </html> |
10 changes: 10 additions & 0 deletions
10
src/main/java/com/gsm/_8th/class4/backend/task12/domain/auth/Repository/UserRepository.java
This file contains hidden or 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.gsm._8th.class4.backend.task12.domain.auth.Repository; | ||
|
|
||
| import com.gsm._8th.class4.backend.task12.domain.auth.Entity.NewSign; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| public interface UserRepository extends JpaRepository<NewSign, Long> { | ||
| Optional<NewSign> findByUsername(String username); | ||
| } |
64 changes: 64 additions & 0 deletions
64
src/main/java/com/gsm/_8th/class4/backend/task12/domain/auth/Service/newsignService.java
This file contains hidden or 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,64 @@ | ||
| package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
|
||
| import com.gsm._8th.class4.backend.task12.domain.auth.dto.UserSignupRequest; | ||
| import com.gsm._8th.class4.backend.task12.global.security.TokenResponse; | ||
| import com.gsm._8th.class4.backend.task12.domain.auth.Entity.NewSign; | ||
| import com.gsm._8th.class4.backend.task12.domain.auth.Repository.UserRepository; | ||
| import com.gsm._8th.class4.backend.task12.global.security.JwtTokenService; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.security.crypto.password.PasswordEncoder; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class newsignService { | ||
|
|
||
| private final UserRepository userRepository; | ||
| private final JwtTokenService jwtTokenService; | ||
| private final PasswordEncoder passwordEncoder; | ||
|
|
||
| @Transactional | ||
| public void signup(UserSignupRequest request) { | ||
| if (userRepository.findByUsername(request.getUsername()).isPresent()) { | ||
| throw new RuntimeException("이미 존재하는 사용자입니다."); | ||
| } | ||
| NewSign newUser = NewSign.builder() | ||
| .username(request.getUsername()) | ||
| .password(passwordEncoder.encode(request.getPassword())) | ||
| .email(request.getEmail()) | ||
| .role("ROLE_USER") | ||
| .build(); | ||
| userRepository.save(newUser); | ||
| } | ||
|
|
||
| // 로그인 | ||
| public TokenResponse login(String username, String rawPassword) { | ||
| Optional<NewSign> userOptional = userRepository.findByUsername(username); | ||
| if (userOptional.isPresent()) { | ||
| NewSign user = userOptional.get(); | ||
| if (passwordEncoder.matches(rawPassword, user.getPassword())) { | ||
| String accessToken = jwtTokenService.createAccessToken(username); | ||
| String refreshToken = jwtTokenService.createRefreshToken(username); | ||
| return new TokenResponse(accessToken, refreshToken); | ||
| } | ||
| } | ||
| return null; // 로그인 실패 | ||
|
snowykte0426 marked this conversation as resolved.
Outdated
|
||
| } | ||
| // 리프레시 토큰을 통한 JWT 재발급 | ||
| public TokenResponse refreshToken(String refreshToken) { | ||
| String username = jwtTokenService.getUsernameFromToken(refreshToken); | ||
|
|
||
| if (username != null && jwtTokenService.validateRefreshToken(username, refreshToken)) { | ||
| jwtTokenService.revokeRefreshToken(username); // 기존 리프레시 토큰 폐기 Refresh Token Rotation 적용 | ||
|
|
||
| String newAccessToken = jwtTokenService.createAccessToken(username); | ||
| String newRefreshToken = jwtTokenService.createRefreshToken(username); | ||
|
|
||
| return new TokenResponse(newAccessToken, newRefreshToken); | ||
| } | ||
| return null; | ||
|
snowykte0426 marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
12 changes: 12 additions & 0 deletions
12
src/main/java/com/gsm/_8th/class4/backend/task12/domain/auth/dto/UserLoginRequest.java
This file contains hidden or 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,12 @@ | ||
| package com.gsm._8th.class4.backend.task12.domain.auth.dto; | ||
|
|
||
| import lombok.Getter; | ||
| import lombok.Setter; | ||
|
|
||
| @Getter | ||
| @Setter | ||
|
|
||
| public class UserLoginRequest { | ||
| private String username; | ||
| private String password; | ||
| } |
24 changes: 24 additions & 0 deletions
24
src/main/java/com/gsm/_8th/class4/backend/task12/domain/auth/dto/UserSignupRequest.java
This file contains hidden or 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,24 @@ | ||
| package com.gsm._8th.class4.backend.task12.domain.auth.dto; | ||
|
|
||
| import jakarta.validation.constraints.Email; | ||
| import jakarta.validation.constraints.NotBlank; | ||
| import jakarta.validation.constraints.Size; | ||
| import lombok.Getter; | ||
| import lombok.Setter; | ||
|
|
||
| @Getter | ||
| @Setter | ||
| public class UserSignupRequest { | ||
|
|
||
| @NotBlank(message = "사용자 이름은 필수 입력값입니다.") | ||
| @Size(min = 3, max = 20, message = "사용자 이름은 3~20자로 입력해야 합니다.") | ||
| private String username; | ||
|
|
||
| @NotBlank(message = "비밀번호는 필수 입력값입니다.") | ||
| @Size(min = 6, message = "비밀번호는 최소 6자 이상이어야 합니다.") | ||
| private String password; | ||
|
|
||
| @NotBlank(message = "이메일은 필수 입력값입니다.") | ||
| @Email(message = "올바른 이메일 형식을 입력해주세요.") | ||
| private String email; | ||
| } |
48 changes: 48 additions & 0 deletions
48
...main/java/com/gsm/_8th/class4/backend/task12/global/security/JwtAuthenticationFilter.java
This file contains hidden or 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,48 @@ | ||
| package com.gsm._8th.class4.backend.task12.global.security; | ||
|
|
||
| import jakarta.servlet.FilterChain; | ||
| import jakarta.servlet.ServletException; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.security.core.userdetails.User; | ||
| import org.springframework.security.core.userdetails.UserDetails; | ||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| import org.springframework.web.filter.OncePerRequestFilter; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| @RequiredArgsConstructor | ||
| public class JwtAuthenticationFilter extends OncePerRequestFilter { | ||
|
|
||
| private final JwtTokenService jwtTokenService; | ||
|
|
||
| @Override | ||
| protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||
| throws ServletException, IOException { | ||
|
|
||
| String token = request.getHeader("Authorization"); | ||
|
|
||
| if (token != null && token.startsWith("Bearer ")) { | ||
| token = token.substring(7); | ||
| if (jwtTokenService.validateToken(token)) { | ||
| String username = jwtTokenService.getUsernameFromToken(token); | ||
|
|
||
| // UserDetails 객체 생성 | ||
| UserDetails userDetails = User.withUsername(username) | ||
| .password("") // JWT는 비밀번호를 검증하지 않음 | ||
| .authorities("ROLE_USER") // 기본 역할 부여 | ||
| .build(); | ||
|
|
||
| // 인증 객체 생성 | ||
| UsernamePasswordAuthenticationToken authentication = | ||
| new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); | ||
|
|
||
| // Spring Security 컨텍스트에 저장 | ||
| SecurityContextHolder.getContext().setAuthentication(authentication); | ||
| } | ||
| } | ||
| filterChain.doFilter(request, response); | ||
| } | ||
| } |
71 changes: 71 additions & 0 deletions
71
src/main/java/com/gsm/_8th/class4/backend/task12/global/security/JwtTokenService.java
This file contains hidden or 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,71 @@ | ||
| package com.gsm._8th.class4.backend.task12.global.security; | ||
|
|
||
| import io.jsonwebtoken.*; | ||
| import io.jsonwebtoken.security.Keys; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import java.nio.charset.StandardCharsets; | ||
| import java.security.Key; | ||
| import java.util.Date; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
|
|
||
| @Slf4j | ||
| @Service | ||
| public class JwtTokenService { | ||
|
|
||
| @Value("${jwt.secret}") | ||
| private String secretKey; | ||
|
|
||
| private final long accessTokenValidity = 60 * 60 * 1000L; | ||
| private final long refreshTokenValidity = 120 * 60 * 1000L; | ||
| private final ConcurrentHashMap<String, String> refreshTokenStore = new ConcurrentHashMap<>(); | ||
| private Key getSigningKey() { | ||
| return Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); | ||
| } | ||
| public String createAccessToken(String username) { | ||
| return createToken(username, accessTokenValidity); | ||
| } | ||
| public String createRefreshToken(String username) { | ||
| String refreshToken = createToken(username, refreshTokenValidity); | ||
| refreshTokenStore.put(username, refreshToken); | ||
| return refreshToken; | ||
| } | ||
| private String createToken(String subject, long validity) { | ||
| Date now = new Date(); | ||
| return Jwts.builder() | ||
| .setSubject(subject) | ||
| .setIssuedAt(now) | ||
| .setExpiration(new Date(now.getTime() + validity)) | ||
| .signWith(getSigningKey(), SignatureAlgorithm.HS256) // 변경됨 | ||
| .compact(); | ||
| } | ||
| public boolean validateToken(String token) { | ||
| try { | ||
| Jws<Claims> claims = Jwts.parser() | ||
| .setSigningKey(getSigningKey()) | ||
| .build() | ||
| .parseClaimsJws(token); | ||
|
|
||
| return !claims.getBody().getExpiration().before(new Date()); // 만료 확인 | ||
| } catch (JwtException | IllegalArgumentException e) { | ||
| log.error("유효하지 않은 토큰: {}", e.getMessage()); | ||
| return false; | ||
| } | ||
| } | ||
| public String getUsernameFromToken(String token) { | ||
| return Jwts.parser() // 변경됨 | ||
| .setSigningKey(getSigningKey()) | ||
| .build() | ||
| .parseClaimsJws(token) | ||
| .getBody() | ||
| .getSubject(); | ||
| } | ||
| public boolean validateRefreshToken(String username, String refreshToken) { | ||
| return refreshToken.equals(refreshTokenStore.get(username)); | ||
| } | ||
| public void revokeRefreshToken(String username) { | ||
| refreshTokenStore.remove(username); | ||
| } | ||
| } |
5 changes: 5 additions & 0 deletions
5
src/main/java/com/gsm/_8th/class4/backend/task12/global/security/SecretKey.java
This file contains hidden or 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,5 @@ | ||
| package com.gsm._8th.class4.backend.task12.global.security; | ||
|
|
||
| public class SecretKey { | ||
| public static String JWT_SECRET_KEY = "236979CB6F1AD6B6A6184A31E6BE37DB3818CC36871E26235DD67DCFE4041492"; | ||
|
snowykte0426 marked this conversation as resolved.
Outdated
|
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.