-
Notifications
You must be signed in to change notification settings - Fork 10
[6주차]강태이/[feat]카카오 회원가입 및 로그인 기능 구현 #123
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
kangtayie
wants to merge
5
commits into
Leets-Official:강태이/main
Choose a base branch
from
kangtayie:강태이/6주차
base: 강태이/main
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.
The head ref may contain hidden characters: "\uAC15\uD0DC\uC774/6\uC8FC\uCC28"
Open
Changes from 4 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
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
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
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
41 changes: 41 additions & 0 deletions
41
blog_manage/src/main/java/com/leets/backend/blog/config/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,41 @@ | ||
| package com.leets.backend.blog.config; | ||
|
|
||
| import jakarta.servlet.FilterChain; | ||
| import jakarta.servlet.ServletException; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | ||
| import org.springframework.web.filter.OncePerRequestFilter; | ||
| import com.leets.backend.blog.service.CustomUserDetailsService; | ||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| import java.io.IOException; | ||
|
|
||
| public class JwtAuthenticationFilter extends OncePerRequestFilter { | ||
|
|
||
| private final JwtTokenProvider jwtTokenProvider; | ||
| private final CustomUserDetailsService userDetailsService; | ||
|
|
||
| public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider, CustomUserDetailsService userDetailsService) { | ||
| this.jwtTokenProvider = jwtTokenProvider; | ||
| this.userDetailsService = userDetailsService; | ||
| } | ||
|
|
||
| @Override | ||
| protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) | ||
| throws ServletException, IOException { | ||
| String header = req.getHeader("Authorization"); | ||
| String token = null; | ||
| if (header != null && header.startsWith("Bearer ")) { | ||
| token = header.substring(7); | ||
| } | ||
| if (token != null && jwtTokenProvider.validateToken(token)) { | ||
| String email = jwtTokenProvider.getSubject(token); | ||
| var userDetails = userDetailsService.loadUserByUsername(email); | ||
| var auth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); | ||
| auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(req)); | ||
| SecurityContextHolder.getContext().setAuthentication(auth); | ||
| } | ||
| chain.doFilter(req, res); | ||
| } | ||
| } |
16 changes: 16 additions & 0 deletions
16
blog_manage/src/main/java/com/leets/backend/blog/config/JwtEntryPoint.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,16 @@ | ||
| package com.leets.backend.blog.config; | ||
|
|
||
| import org.springframework.security.core.AuthenticationException; | ||
| import org.springframework.security.web.AuthenticationEntryPoint; | ||
| import jakarta.servlet.http.*; | ||
| import java.io.IOException; | ||
|
|
||
| public class JwtEntryPoint implements AuthenticationEntryPoint { | ||
| @Override | ||
| public void commence(HttpServletRequest request, HttpServletResponse response, | ||
| AuthenticationException authException) throws IOException { | ||
| response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | ||
| response.setContentType("application/json"); | ||
| response.getWriter().write("{\"message\":\"Unauthorized\"}"); | ||
| } | ||
| } |
86 changes: 86 additions & 0 deletions
86
blog_manage/src/main/java/com/leets/backend/blog/config/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,86 @@ | ||
| package com.leets.backend.blog.config; | ||
|
|
||
| import io.jsonwebtoken.JwtException; | ||
| import io.jsonwebtoken.Jwts; | ||
| import io.jsonwebtoken.SignatureAlgorithm; | ||
| import io.jsonwebtoken.security.Keys; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.security.Key; | ||
| import java.util.Date; | ||
|
|
||
| @Component | ||
| public class JwtTokenProvider { | ||
|
|
||
| private final Key key; | ||
| private final long accessTokenValidityMs; | ||
| private final long refreshTokenValidityMs; | ||
|
|
||
| public JwtTokenProvider( | ||
| @Value("${jwt.secret}") String secret, | ||
| @Value("${jwt.access-token-validity}") long accessValidity, | ||
| @Value("${jwt.refresh-token-validity}") long refreshValidity) { | ||
| this.key = Keys.hmacShaKeyFor(secret.getBytes()); | ||
| this.accessTokenValidityMs = accessValidity; | ||
| this.refreshTokenValidityMs = refreshValidity; | ||
| } | ||
|
|
||
| public String createAccessToken(String subject, String role) { | ||
| Date now = new Date(); | ||
| Date expiry = new Date(now.getTime() + accessTokenValidityMs); | ||
|
|
||
| return Jwts.builder() | ||
| .setSubject(subject) | ||
| .claim("role", role) | ||
| .setIssuedAt(now) | ||
| .setExpiration(expiry) | ||
| .signWith(key, SignatureAlgorithm.HS256) | ||
| .compact(); | ||
| } | ||
|
|
||
| public String createRefreshToken(String subject) { | ||
| Date now = new Date(); | ||
| Date expiry = new Date(now.getTime() + refreshTokenValidityMs); | ||
|
|
||
| return Jwts.builder() | ||
| .setSubject(subject) | ||
| .setIssuedAt(now) | ||
| .setExpiration(expiry) | ||
| .signWith(key, SignatureAlgorithm.HS256) | ||
| .compact(); | ||
| } | ||
|
|
||
| public String generateToken(String subject) { | ||
| // 기본적으로 USER 권한으로 AccessToken 생성 | ||
| return createAccessToken(subject, "ROLE_USER"); | ||
| } | ||
|
|
||
| // 토큰 유효성 검사 | ||
| public boolean validateToken(String token) { | ||
| try { | ||
| Jwts.parser().setSigningKey(key).build().parseClaimsJws(token); | ||
| return true; | ||
| } catch (JwtException | IllegalArgumentException ex) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| // 토큰에서 이메일 추출 | ||
| public String getSubject(String token) { | ||
| return Jwts.parser().setSigningKey(key) | ||
| .build() | ||
| .parseClaimsJws(token) | ||
| .getBody() | ||
| .getSubject(); | ||
| } | ||
|
|
||
| // 토큰 만료시간 추출 | ||
| public Date getExpiration(String token) { | ||
| return Jwts.parser().setSigningKey(key) | ||
| .build() | ||
| .parseClaimsJws(token) | ||
| .getBody() | ||
| .getExpiration(); | ||
| } | ||
| } | ||
75 changes: 50 additions & 25 deletions
75
blog_manage/src/main/java/com/leets/backend/blog/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 |
|---|---|---|
| @@ -1,49 +1,74 @@ | ||
| package com.leets.backend.blog.config; | ||
|
|
||
| import com.leets.backend.blog.service.CustomUserDetailsService; | ||
| 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.method.configuration.EnableMethodSecurity; | ||
| 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; | ||
|
|
||
| @Configuration | ||
| @EnableWebSecurity | ||
| @EnableMethodSecurity | ||
| public class SecurityConfig { | ||
|
|
||
| private final JwtTokenProvider jwtTokenProvider; | ||
| private final CustomUserDetailsService userDetailsService; | ||
|
|
||
| // 생성자를 통한 의존성 주입 | ||
| public SecurityConfig(JwtTokenProvider jwtTokenProvider, CustomUserDetailsService userDetailsService) { | ||
| this.jwtTokenProvider = jwtTokenProvider; | ||
| this.userDetailsService = userDetailsService; | ||
| } | ||
|
|
||
| // 비밀번호 인코더 Bean 등록 | ||
| @Bean | ||
| public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { | ||
| http | ||
| // 세션 비활성화 | ||
| .sessionManagement(session -> session | ||
| .sessionCreationPolicy(SessionCreationPolicy.STATELESS)) | ||
| public PasswordEncoder passwordEncoder() { | ||
| return new BCryptPasswordEncoder(); | ||
| } | ||
|
|
||
| // AuthenticationManager Bean 등록 | ||
| @Bean | ||
| public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { | ||
| return authenticationConfiguration.getAuthenticationManager(); | ||
| } | ||
|
|
||
| .csrf(csrf -> csrf.disable()) // CSRF 보호 비활성화 (API 서버에서는 보통 비활성화) | ||
| .cors(cors -> cors.disable()) | ||
| // JWT 인증 필터 Bean 등록 | ||
| @Bean | ||
| public JwtAuthenticationFilter jwtAuthenticationFilter() { | ||
| return new JwtAuthenticationFilter(jwtTokenProvider, userDetailsService); | ||
| } | ||
|
|
||
| // 폼 로그인, http 인증 비활성화 | ||
| .formLogin(form -> form.disable()) | ||
| .httpBasic(basic -> basic.disable()) | ||
| // 보안 필터 체인 설정 | ||
| @Bean | ||
| public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
| http | ||
| // CSRF 비활성화 | ||
| .csrf(csrf -> csrf.disable()) | ||
|
|
||
| // 요청에 대한 인가(Authorization) 설정 | ||
| .authorizeHttpRequests(authorize -> authorize | ||
| // Swagger UI 및 API 문서 경로에 대한 접근 허용 | ||
| // 요청별 인가 규칙 설정 | ||
| .authorizeHttpRequests(auth -> auth | ||
| .requestMatchers( | ||
| "/api/auth/**", // 인증 관련 경로 허용 | ||
| "/v3/api-docs/**", | ||
| "/swagger-ui/**", | ||
| "/swagger-ui.html", | ||
| "/swagger-resources/**", | ||
| "/webjars/**", | ||
| "/api/**" | ||
| "/swagger-ui.html" | ||
| ).permitAll() | ||
| .requestMatchers( | ||
| "/posts/**", | ||
| "/" | ||
| ).permitAll() | ||
| // 그 외 모든 요청은 인가 필요 | ||
| .anyRequest().authenticated() | ||
| ); | ||
| .anyRequest().authenticated() // 나머지는 인증 필요 | ||
| ) | ||
|
|
||
| // 세션 사용 안 함 (STATELESS) | ||
| .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); | ||
|
|
||
| http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); | ||
|
|
||
| return http.build(); | ||
| } | ||
| } | ||
| } |
46 changes: 46 additions & 0 deletions
46
blog_manage/src/main/java/com/leets/backend/blog/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,46 @@ | ||
| package com.leets.backend.blog.controller; | ||
|
|
||
| import com.leets.backend.blog.dto.auth.*; | ||
| import com.leets.backend.blog.common.ApiResponse; | ||
| import com.leets.backend.blog.service.AuthService; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.*; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/api/auth") | ||
| public class AuthController { | ||
|
|
||
| private final AuthService authService; | ||
| public AuthController(AuthService authService) { | ||
| this.authService = authService; | ||
| } | ||
|
|
||
| // 회원가입 | ||
| @PostMapping("/signup") | ||
| public ResponseEntity<ApiResponse<Void>> signup(@RequestBody SignUpRequest req) { | ||
| authService.signup(req); | ||
| return ResponseEntity.ok(ApiResponse.onSuccess("User registered")); | ||
| } | ||
|
|
||
| // 로그인 | ||
| @PostMapping("/login") | ||
| public ResponseEntity<ApiResponse<TokenResponse>> login(@RequestBody LoginRequest req) { | ||
| TokenResponse tokens = authService.login(req); | ||
| return ResponseEntity.ok(ApiResponse.onSuccess(HttpStatus.OK, "Login success", tokens)); | ||
| } | ||
|
|
||
| // 토큰 재발급 | ||
| @PostMapping("/refresh") | ||
| public ResponseEntity<ApiResponse<TokenResponse>> refresh(@RequestBody RefreshTokenRequest rreq) { | ||
| TokenResponse tokens = authService.refreshToken(rreq.getRefreshToken()); | ||
| return ResponseEntity.ok(ApiResponse.onSuccess(HttpStatus.OK, "Token refreshed", tokens)); | ||
| } | ||
|
|
||
| // 로그아웃 | ||
| @PostMapping("/logout") | ||
| public ResponseEntity<ApiResponse<Void>> logout(@RequestParam String email) { | ||
| authService.logout(email); | ||
| return ResponseEntity.ok(ApiResponse.onSuccess("Logged out")); | ||
| } | ||
| } | ||
|
Comment on lines
+41
to
+47
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. 현재 로그아웃을 할 때 URL 쿼리 파라미터로 이메일을 받고 있는데, 이 코드는 현재 로그인한 사용자가 아닌 파라미터로 넘어온 다른 사용자를 로그아웃 시킬 수 있는 보안상의 위험이 있다고 합니다!! 로그아웃은 현재 인증된 사용자를 기준으로 처리해주시면 좋을 것 같아요 👍🏻👍🏻 |
||
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
지금 validationToken 메서드에서 예외 발생 시 false만 반환하고 있는데, 어떤 이유로 토큰 검증에 실패했는지 로그에 남기면 디버깅에 도움이 된다고 합니다!
이런식으로 하는 게 좋다고 하네요 저도 몰랐는데 알아갑니당 👀👀