-
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 1 commit
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
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
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(); | ||
| } | ||
| } |
46 changes: 46 additions & 0 deletions
46
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,46 @@ | ||
| package ssurent.ssurentbe.common.jwt; | ||
|
|
||
| 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.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 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; | ||
|
|
||
| @Override | ||
| protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||
| throws ServletException, IOException { | ||
|
|
||
| String token = resolveToken(request); | ||
|
|
||
| if (StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) { | ||
| Authentication authentication = jwtTokenProvider.getAuthentication(token); | ||
| SecurityContextHolder.getContext().setAuthentication(authentication); | ||
| } | ||
|
|
||
| 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; | ||
| } | ||
| } |
97 changes: 97 additions & 0 deletions
97
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,97 @@ | ||
| package ssurent.ssurentbe.common.jwt; | ||
|
|
||
| import io.jsonwebtoken.*; | ||
| import io.jsonwebtoken.io.Decoders; | ||
| import io.jsonwebtoken.security.Keys; | ||
| 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 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 (JwtException | IllegalArgumentException e) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| 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; | ||
| } | ||
| } | ||
| } |
32 changes: 32 additions & 0 deletions
32
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,32 @@ | ||
| 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.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("사용자를 찾을 수 없습니다: " + studentNum)); | ||
|
|
||
| return new User( | ||
| user.getStudentNum(), | ||
| user.getPassword(), | ||
| Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + user.getRole().name())) | ||
| ); | ||
| } | ||
| } |
42 changes: 42 additions & 0 deletions
42
src/main/java/ssurent/ssurentbe/domain/users/controller/AuthController.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,42 @@ | ||
| package ssurent.ssurentbe.domain.users.controller; | ||
|
|
||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.*; | ||
| import ssurent.ssurentbe.domain.users.dto.LoginRequest; | ||
| import ssurent.ssurentbe.domain.users.dto.SignupRequest; | ||
| import ssurent.ssurentbe.domain.users.dto.TokenResponse; | ||
| import ssurent.ssurentbe.domain.users.service.AuthService; | ||
|
|
||
| @Tag(name = "Auth", description = "인증 API") | ||
| @RestController | ||
| @RequestMapping("/api/auth") | ||
| @RequiredArgsConstructor | ||
| public class AuthController { | ||
|
|
||
| private final AuthService authService; | ||
|
|
||
| @Operation(summary = "회원가입", description = "학번, 이름, 전화번호, 비밀번호로 회원가입합니다.") | ||
| @PostMapping("/signup") | ||
| public ResponseEntity<TokenResponse> signup(@RequestBody SignupRequest request) { | ||
| TokenResponse response = authService.signup(request); | ||
| return ResponseEntity.ok(response); | ||
| } | ||
|
|
||
| @Operation(summary = "로그인", description = "학번과 비밀번호로 로그인합니다.") | ||
| @PostMapping("/login") | ||
| public ResponseEntity<TokenResponse> login(@RequestBody LoginRequest request) { | ||
| TokenResponse response = authService.login(request); | ||
| return ResponseEntity.ok(response); | ||
| } | ||
|
|
||
| @Operation(summary = "토큰 갱신", description = "리프레시 토큰으로 새로운 액세스 토큰을 발급받습니다.") | ||
| @PostMapping("/refresh") | ||
| public ResponseEntity<TokenResponse> refresh(@RequestHeader("Authorization") String authorization) { | ||
| String refreshToken = authorization.replace("Bearer ", ""); | ||
| TokenResponse response = authService.refresh(refreshToken); | ||
| return ResponseEntity.ok(response); | ||
| } | ||
| } |
7 changes: 7 additions & 0 deletions
7
src/main/java/ssurent/ssurentbe/domain/users/dto/LoginRequest.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,7 @@ | ||
| package ssurent.ssurentbe.domain.users.dto; | ||
|
|
||
| public record LoginRequest( | ||
| String studentNum, | ||
| String password | ||
| ) { | ||
| } |
9 changes: 9 additions & 0 deletions
9
src/main/java/ssurent/ssurentbe/domain/users/dto/SignupRequest.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,9 @@ | ||
| package ssurent.ssurentbe.domain.users.dto; | ||
|
|
||
| public record SignupRequest( | ||
| String studentNum, | ||
| String name, | ||
| String phoneNum, | ||
| String password | ||
| ) { | ||
CromNEXT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
11 changes: 11 additions & 0 deletions
11
src/main/java/ssurent/ssurentbe/domain/users/dto/TokenResponse.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,11 @@ | ||
| package ssurent.ssurentbe.domain.users.dto; | ||
|
|
||
| public record TokenResponse( | ||
| String accessToken, | ||
| String refreshToken, | ||
| String tokenType | ||
CromNEXT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) { | ||
| public static TokenResponse of(String accessToken, String refreshToken) { | ||
| return new TokenResponse(accessToken, refreshToken, "Bearer"); | ||
| } | ||
| } | ||
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
13 changes: 13 additions & 0 deletions
13
src/main/java/ssurent/ssurentbe/domain/users/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,13 @@ | ||
| package ssurent.ssurentbe.domain.users.repository; | ||
|
|
||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import org.springframework.stereotype.Repository; | ||
| import ssurent.ssurentbe.domain.users.entity.Users; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| @Repository | ||
| public interface UserRepository extends JpaRepository<Users, String> { | ||
| Optional<Users> findByStudentNum(String studentNum); | ||
| boolean existsByStudentNum(String studentNum); | ||
| } |
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.