-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 기본적인 Auth 기능 구현 #6
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
Merged
Changes from 3 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
6a6f00c
Feat #3 Auth 구현 파일 추가
CromNEXT 13b39a6
Feat #3 Id타입 String으로 변환 및 Auth 오류 수정
CromNEXT 82197d7
Feat #3 Status추가
CromNEXT a729543
Feat #3 gradle springdoc-openapi 버전 3.0.0으로 업그레이드
CromNEXT 2f14867
Feat #3 ExceptionHandler가 상세 에러를 반환하도록 수정
CromNEXT db85b74
Feat #3 GeneralException의 BaseStatus 캐스팅 문제 해결
CromNEXT 4d86cfc
Feat #3 JWT 필터 분기조건 수정
CromNEXT e8a565c
Feat #3 필드 boolean 명칭 수정
CromNEXT f074d8a
Feat #3 로그인 조건 수정
CromNEXT 27d7f4d
Feat #3 SignupRequest 비밀번호 마스킹
CromNEXT fc80592
Feat #3 Swagger 제목 & Token저장하는 곳 추가
CromNEXT 3fbfe4e
Feat #3 Swagger 반환값 Docs 수정
CromNEXT 98f944b
Feat #3 isDelete제거 deleted로 수정
CromNEXT d2b5085
[Refactor] #4 Penalty Entity 변경
Todom2 574f8ea
Feat #3 Id타입 String으로 변환 및 Auth 오류 수정
CromNEXT d696047
Feat #3 충돌 해결
CromNEXT 616de42
[Refactor] #4 Penalty Entity 변경
Todom2 c4c0c86
Feat #3 Id타입 String으로 변환 및 Auth 오류 수정
CromNEXT 006f3d8
Feat #3 Id타입 String으로 변환 및 Auth 오류 수정
CromNEXT 596d6c7
Feat #3 충돌 해결2
CromNEXT 49d31f7
Feat #3 JWT 에러코드 단순화, 에러로깅 추가 + gitignore 추가
CromNEXT 2217158
Feat #3 PanaltyTypes 필드의 @Enumerated 지정
CromNEXT 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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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
Binary file modified
BIN
+73 Bytes
(110%)
build/classes/java/main/ssurent/ssurentbe/SsurentbeApplication.class
Binary file not shown.
Binary file modified
BIN
+100 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/assists/entity/Assists$AssistsBuilder.class
Binary file not shown.
Binary file modified
BIN
+38 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/assists/entity/Assists.class
Binary file not shown.
Binary file modified
BIN
+99 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/item/entity/Category$CategoryBuilder.class
Binary file not shown.
Binary file modified
BIN
+38 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/item/entity/Category.class
Binary file not shown.
Binary file modified
BIN
+93 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/item/entity/Items$ItemsBuilder.class
Binary file not shown.
Binary file modified
BIN
+38 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/item/entity/Items.class
Binary file not shown.
Binary file modified
BIN
-8 Bytes
(100%)
...java/main/ssurent/ssurentbe/domain/rental/entity/RentalHistory$RentalHistoryBuilder.class
Binary file not shown.
Binary file modified
BIN
-6 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/rental/entity/RentalHistory.class
Binary file not shown.
Binary file modified
BIN
-8 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/users/entity/Panalty$PanaltyBuilder.class
Binary file not shown.
Binary file modified
BIN
-6 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/users/entity/Panalty.class
Binary file not shown.
Binary file modified
BIN
-8 Bytes
(100%)
...ava/main/ssurent/ssurentbe/domain/users/entity/UserPanaltyLog$UserPanaltyLogBuilder.class
Binary file not shown.
Binary file modified
BIN
-6 Bytes
(100%)
build/classes/java/main/ssurent/ssurentbe/domain/users/entity/UserPanaltyLog.class
Binary file not shown.
Binary file modified
BIN
+287 Bytes
(110%)
build/classes/java/main/ssurent/ssurentbe/domain/users/entity/Users$UsersBuilder.class
Binary file not shown.
Binary file modified
BIN
+214 Bytes
(110%)
build/classes/java/main/ssurent/ssurentbe/domain/users/entity/Users.class
Binary file not shown.
Binary file not shown.
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
29 changes: 29 additions & 0 deletions
29
src/main/java/ssurent/ssurentbe/common/base/BaseResponse.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,29 @@ | ||
| package ssurent.ssurentbe.common.base; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import ssurent.ssurentbe.common.status.ErrorStatus; | ||
| import ssurent.ssurentbe.common.status.SuccessStatus; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor | ||
| @JsonInclude(JsonInclude.Include.NON_NULL) | ||
| public class BaseResponse<T> { | ||
|
|
||
| private final String code; | ||
| private final String message; | ||
| private final T data; | ||
|
|
||
| public static <T> BaseResponse<T> success(SuccessStatus status, T data) { | ||
| return new BaseResponse<>(status.getCode(), status.getMessage(), data); | ||
| } | ||
|
|
||
| public static <T> BaseResponse<T> success(SuccessStatus status) { | ||
| return new BaseResponse<>(status.getCode(), status.getMessage(), null); | ||
| } | ||
|
|
||
| public static <T> BaseResponse<T> error(ErrorStatus status) { | ||
| return new BaseResponse<>(status.getCode(), status.getMessage(), null); | ||
| } | ||
| } |
48 changes: 48 additions & 0 deletions
48
src/main/java/ssurent/ssurentbe/common/config/SecurityConfig.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 ssurent.ssurentbe.common.config; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.security.authentication.AuthenticationManager; | ||
| import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; | ||
| import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
| import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
| import org.springframework.security.config.http.SessionCreationPolicy; | ||
| import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
| import org.springframework.security.crypto.password.PasswordEncoder; | ||
| import org.springframework.security.web.SecurityFilterChain; | ||
| import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
| import ssurent.ssurentbe.common.jwt.JwtAuthenticationFilter; | ||
|
|
||
| @Configuration | ||
| @EnableWebSecurity | ||
| @RequiredArgsConstructor | ||
| public class SecurityConfig { | ||
|
|
||
| private final JwtAuthenticationFilter jwtAuthenticationFilter; | ||
|
|
||
| @Bean | ||
| public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
| http | ||
| .csrf(csrf -> csrf.disable()) | ||
| .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) | ||
| .authorizeHttpRequests(auth -> auth | ||
| .requestMatchers("/api/auth/**").permitAll() | ||
| .requestMatchers("/swagger-ui/**", "/api-docs/**", "/swagger-ui.html").permitAll() | ||
| .anyRequest().authenticated() | ||
| ) | ||
| .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); | ||
|
|
||
| return http.build(); | ||
| } | ||
|
|
||
| @Bean | ||
| public PasswordEncoder passwordEncoder() { | ||
| return new BCryptPasswordEncoder(); | ||
| } | ||
|
|
||
| @Bean | ||
| public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { | ||
| return authenticationConfiguration.getAuthenticationManager(); | ||
| } | ||
| } |
15 changes: 15 additions & 0 deletions
15
src/main/java/ssurent/ssurentbe/common/exception/GeneralException.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,15 @@ | ||
| package ssurent.ssurentbe.common.exception; | ||
|
|
||
| import lombok.Getter; | ||
| import ssurent.ssurentbe.common.base.BaseStatus; | ||
|
|
||
| @Getter | ||
| public class GeneralException extends RuntimeException { | ||
|
|
||
| private final BaseStatus status; | ||
|
|
||
| public GeneralException(BaseStatus status) { | ||
| super(status.getMessage()); | ||
| this.status = status; | ||
| } | ||
| } |
19 changes: 19 additions & 0 deletions
19
src/main/java/ssurent/ssurentbe/common/exception/GlobalExceptionHandler.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,19 @@ | ||
| package ssurent.ssurentbe.common.exception; | ||
|
|
||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
| import ssurent.ssurentbe.common.base.BaseResponse; | ||
| import ssurent.ssurentbe.common.status.ErrorStatus; | ||
|
|
||
| @RestControllerAdvice | ||
| public class GlobalExceptionHandler { | ||
|
|
||
| @ExceptionHandler(GeneralException.class) | ||
| public ResponseEntity<BaseResponse<Void>> handleGeneralException(GeneralException e) { | ||
| ErrorStatus status = (ErrorStatus) e.getStatus(); | ||
| return ResponseEntity | ||
| .status(status.getHttpStatus()) | ||
| .body(BaseResponse.error(status)); | ||
CromNEXT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
65 changes: 65 additions & 0 deletions
65
src/main/java/ssurent/ssurentbe/common/jwt/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,65 @@ | ||
| package ssurent.ssurentbe.common.jwt; | ||
|
|
||
| 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 org.springframework.http.MediaType; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.util.StringUtils; | ||
| import org.springframework.web.filter.OncePerRequestFilter; | ||
| import ssurent.ssurentbe.common.base.BaseResponse; | ||
| import ssurent.ssurentbe.common.exception.GeneralException; | ||
| import ssurent.ssurentbe.common.status.ErrorStatus; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class JwtAuthenticationFilter extends OncePerRequestFilter { | ||
|
|
||
| private static final String AUTHORIZATION_HEADER = "Authorization"; | ||
| private static final String BEARER_PREFIX = "Bearer "; | ||
|
|
||
| private final JwtTokenProvider jwtTokenProvider; | ||
| private static final ObjectMapper objectMapper = new ObjectMapper(); | ||
|
|
||
| @Override | ||
| protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||
| throws ServletException, IOException { | ||
|
|
||
| String token = resolveToken(request); | ||
|
|
||
| if (StringUtils.hasText(token)) { | ||
| try { | ||
| jwtTokenProvider.validateToken(token); | ||
| Authentication authentication = jwtTokenProvider.getAuthentication(token); | ||
| SecurityContextHolder.getContext().setAuthentication(authentication); | ||
| } catch (GeneralException e) { | ||
| sendErrorResponse(response, (ErrorStatus) e.getStatus()); | ||
| return; | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| filterChain.doFilter(request, response); | ||
| } | ||
|
|
||
| private String resolveToken(HttpServletRequest request) { | ||
| String bearerToken = request.getHeader(AUTHORIZATION_HEADER); | ||
| if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) { | ||
| return bearerToken.substring(BEARER_PREFIX.length()); | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| private void sendErrorResponse(HttpServletResponse response, ErrorStatus status) throws IOException { | ||
| response.setStatus(status.getHttpStatus().value()); | ||
| response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
| response.setCharacterEncoding("UTF-8"); | ||
| objectMapper.writeValue(response.getWriter(), BaseResponse.error(status)); | ||
| } | ||
| } | ||
108 changes: 108 additions & 0 deletions
108
src/main/java/ssurent/ssurentbe/common/jwt/JwtTokenProvider.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,108 @@ | ||
| package ssurent.ssurentbe.common.jwt; | ||
|
|
||
| import io.jsonwebtoken.*; | ||
| import io.jsonwebtoken.io.Decoders; | ||
| import io.jsonwebtoken.security.Keys; | ||
| import io.jsonwebtoken.security.SignatureException; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.context.annotation.Lazy; | ||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.userdetails.UserDetails; | ||
| import org.springframework.security.core.userdetails.UserDetailsService; | ||
| import org.springframework.stereotype.Component; | ||
| import ssurent.ssurentbe.common.exception.GeneralException; | ||
| import ssurent.ssurentbe.common.status.ErrorStatus; | ||
|
|
||
| import javax.crypto.SecretKey; | ||
| import java.util.Date; | ||
|
|
||
| @Component | ||
| public class JwtTokenProvider { | ||
|
|
||
| private final SecretKey secretKey; | ||
| private final long accessTokenValidity; | ||
| private final long refreshTokenValidity; | ||
| private final UserDetailsService userDetailsService; | ||
|
|
||
| public JwtTokenProvider( | ||
| @Value("${jwt.secret}") String secret, | ||
| @Value("${jwt.access-token-validity}") long accessTokenValidity, | ||
| @Value("${jwt.refresh-token-validity}") long refreshTokenValidity, | ||
| @Lazy UserDetailsService userDetailsService) { | ||
| this.secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret)); | ||
| this.accessTokenValidity = accessTokenValidity; | ||
| this.refreshTokenValidity = refreshTokenValidity; | ||
| this.userDetailsService = userDetailsService; | ||
| } | ||
|
|
||
| public String createAccessToken(String studentNum) { | ||
| return createToken(studentNum, accessTokenValidity, "access"); | ||
| } | ||
|
|
||
| public String createRefreshToken(String studentNum) { | ||
| return createToken(studentNum, refreshTokenValidity, "refresh"); | ||
| } | ||
|
|
||
| private String createToken(String studentNum, long validity, String tokenType) { | ||
| Date now = new Date(); | ||
| Date expiration = new Date(now.getTime() + validity); | ||
|
|
||
| return Jwts.builder() | ||
| .subject(studentNum) | ||
| .claim("type", tokenType) | ||
| .issuedAt(now) | ||
| .expiration(expiration) | ||
| .signWith(secretKey) | ||
| .compact(); | ||
| } | ||
|
|
||
| public Authentication getAuthentication(String token) { | ||
| String studentNum = getStudentNum(token); | ||
| UserDetails userDetails = userDetailsService.loadUserByUsername(studentNum); | ||
| return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); | ||
| } | ||
|
|
||
| public String getStudentNum(String token) { | ||
| return Jwts.parser() | ||
| .verifyWith(secretKey) | ||
| .build() | ||
| .parseSignedClaims(token) | ||
| .getPayload() | ||
| .getSubject(); | ||
| } | ||
|
|
||
| public boolean validateToken(String token) { | ||
| try { | ||
| Jwts.parser() | ||
| .verifyWith(secretKey) | ||
| .build() | ||
| .parseSignedClaims(token); | ||
| return true; | ||
| } catch (SignatureException e) { | ||
| throw new GeneralException(ErrorStatus.JWT_INVALID_SIGNATURE); | ||
| } catch (MalformedJwtException e) { | ||
| throw new GeneralException(ErrorStatus.JWT_MALFORMED); | ||
| } catch (ExpiredJwtException e) { | ||
| throw new GeneralException(ErrorStatus.JWT_EXPIRED); | ||
| } catch (UnsupportedJwtException e) { | ||
| throw new GeneralException(ErrorStatus.JWT_UNSUPPORTED); | ||
| } catch (IllegalArgumentException e) { | ||
| throw new GeneralException(ErrorStatus.JWT_INVALID); | ||
| } | ||
| } | ||
|
|
||
| public boolean isRefreshToken(String token) { | ||
| try { | ||
| String type = Jwts.parser() | ||
| .verifyWith(secretKey) | ||
| .build() | ||
| .parseSignedClaims(token) | ||
| .getPayload() | ||
| .get("type", String.class); | ||
| return "refresh".equals(type); | ||
| } catch (JwtException | IllegalArgumentException e) { | ||
| return false; | ||
| } | ||
| } | ||
| } |
33 changes: 33 additions & 0 deletions
33
src/main/java/ssurent/ssurentbe/common/security/CustomUserDetailsService.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,33 @@ | ||
| package ssurent.ssurentbe.common.security; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
| import org.springframework.security.core.userdetails.User; | ||
| import org.springframework.security.core.userdetails.UserDetails; | ||
| import org.springframework.security.core.userdetails.UserDetailsService; | ||
| import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
| import org.springframework.stereotype.Service; | ||
| import ssurent.ssurentbe.common.status.ErrorStatus; | ||
| import ssurent.ssurentbe.domain.users.entity.Users; | ||
| import ssurent.ssurentbe.domain.users.repository.UserRepository; | ||
|
|
||
| import java.util.Collections; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class CustomUserDetailsService implements UserDetailsService { | ||
|
|
||
| private final UserRepository userRepository; | ||
|
|
||
| @Override | ||
| public UserDetails loadUserByUsername(String studentNum) throws UsernameNotFoundException { | ||
| Users user = userRepository.findByStudentNum(studentNum) | ||
| .orElseThrow(() -> new UsernameNotFoundException(ErrorStatus.USER_NOT_FOUND.getMessage())); | ||
|
|
||
| return new User( | ||
| user.getStudentNum(), | ||
| user.getPassword(), | ||
| Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + user.getRole().name())) | ||
| ); | ||
CromNEXT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
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.