-
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
base: 강태이/main
Are you sure you want to change the base?
The head ref may contain hidden characters: "\uAC15\uD0DC\uC774/6\uC8FC\uCC28"
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| 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); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.leets.backend.blog.config; | ||
|
|
||
| import org.springframework.security.core.AuthenticationException; | ||
| import org.springframework.security.web.AuthenticationEntryPoint; | ||
| import org.springframework.stereotype.Component; | ||
| import jakarta.servlet.http.*; | ||
| import java.io.IOException; | ||
|
|
||
| @Component | ||
| public class JwtEntryPoint implements AuthenticationEntryPoint { | ||
|
|
||
| @Override | ||
| public void commence(HttpServletRequest request, HttpServletResponse response, | ||
| AuthenticationException authException) throws IOException { | ||
| response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401 | ||
| response.setContentType("application/json; charset=UTF-8"); | ||
|
|
||
| // 인증 실패 메세지 | ||
| String json = ("{\"message\":\"Unauthorized - 인증이 필요합니다.\"}"); | ||
|
|
||
| response.getWriter().write(json); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| 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("${spring.jwt.secret}") String secret, | ||
| @Value("${spring.jwt.access-token-validity}") long accessValidity, | ||
| @Value("${spring.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) { | ||
| 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(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package com.leets.backend.blog.config; | ||
|
|
||
| import io.swagger.v3.oas.models.Components; | ||
| import io.swagger.v3.oas.models.OpenAPI; | ||
| import io.swagger.v3.oas.models.info.Info; | ||
| import io.swagger.v3.oas.models.security.SecurityRequirement; | ||
| import io.swagger.v3.oas.models.security.SecurityScheme; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
|
|
||
| @Configuration | ||
| public class OpenApiConfig { | ||
|
|
||
| @Bean | ||
| public OpenAPI openAPI() { | ||
| // Security Scheme 이름 정의 | ||
| String securitySchemeName = "Bearer Authentication"; | ||
|
|
||
| // 모든 API에 이 Security Requirement를 적용 | ||
| SecurityRequirement securityRequirement = new SecurityRequirement() | ||
| .addList(securitySchemeName); | ||
|
|
||
| // JWT Bearer Token을 위한 Security Scheme 정의 | ||
| SecurityScheme securityScheme = new SecurityScheme() | ||
| .name(securitySchemeName) | ||
| .type(SecurityScheme.Type.HTTP) // HTTP 인증 방식 | ||
| .scheme("bearer") // Bearer 토큰 사용 | ||
| .bearerFormat("JWT") // JWT 형식 | ||
| .description("JWT 토큰을 입력하세요. 'Bearer ' 접두사는 자동으로 추가됩니다."); | ||
|
|
||
| return new OpenAPI() | ||
| .info(new Info() | ||
| .title("Blog Management API") | ||
| .description("블로그 관리 시스템 API 문서") | ||
| .version("1.0.0")) | ||
| .addSecurityItem(securityRequirement) // 모든 API에 보안 적용 | ||
| .components(new Components() | ||
| .addSecuritySchemes(securitySchemeName, securityScheme)); | ||
| } | ||
| } |
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만 반환하고 있는데, 어떤 이유로 토큰 검증에 실패했는지 로그에 남기면 디버깅에 도움이 된다고 합니다!
이런식으로 하는 게 좋다고 하네요 저도 몰랐는데 알아갑니당 👀👀