-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from GDSC-KNU/feat/auth
인증/인가 구현 #5
- Loading branch information
Showing
26 changed files
with
874 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 |
---|---|---|
|
@@ -35,3 +35,6 @@ out/ | |
|
||
### VS Code ### | ||
.vscode/ | ||
|
||
### env ### | ||
.env |
This file contains 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 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 gdsc.comunity.annotation; | ||
|
||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Documented | ||
@Target(ElementType.PARAMETER) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface UserId { | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/gdsc/comunity/controller/AuthController.java
This file contains 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 gdsc.comunity.controller; | ||
|
||
import gdsc.comunity.dto.JwtTokensDto; | ||
import gdsc.comunity.dto.RefreshTokenDto; | ||
import gdsc.comunity.service.AuthService; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@Slf4j | ||
@RestController | ||
@RequestMapping("/api/auth") | ||
@RequiredArgsConstructor | ||
public class AuthController { | ||
private final AuthService authService; | ||
|
||
@PostMapping("/refresh") | ||
public ResponseEntity<JwtTokensDto> refreshNewTokens( | ||
@RequestBody RefreshTokenDto refreshTokenDto | ||
) { | ||
//1. refresh token 유효성 검증 | ||
authService.validateRefreshToken(refreshTokenDto.refreshToken()); | ||
|
||
JwtTokensDto newTokens = authService.refreshNewTokens(refreshTokenDto.refreshToken()); | ||
return ResponseEntity.ok(newTokens); | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
src/main/java/gdsc/comunity/controller/OAuthController.java
This file contains 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,37 @@ | ||
package gdsc.comunity.controller; | ||
|
||
import gdsc.comunity.dto.GoogleUserInfo; | ||
import gdsc.comunity.dto.JwtTokensDto; | ||
import java.util.Map; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import gdsc.comunity.service.OAuthGoogleService; | ||
|
||
@Slf4j | ||
@RestController | ||
@RequestMapping("/oauth") | ||
@RequiredArgsConstructor | ||
public class OAuthController { | ||
private final OAuthGoogleService oAuthGoogleService; | ||
|
||
@GetMapping("/login/google/callback") | ||
public ResponseEntity<?> loginByGoogleCallback( | ||
@RequestParam("code") String code | ||
) { | ||
//1. 해당 코드로 구글에게 토큰을 요청 | ||
String tokenByGoogle = oAuthGoogleService.getAccessToken(code); | ||
|
||
//2. 토큰을 받아서 유저 정보를 가져옴 | ||
Map<String, String> userInfo = oAuthGoogleService.getUserInfoByAccessToken(tokenByGoogle); | ||
|
||
//3. 유저 정보를 토대로 회원가입 or 로그인 | ||
JwtTokensDto jwtDto = oAuthGoogleService.loginOrRegister(userInfo); | ||
return new ResponseEntity<>(jwtDto, HttpStatus.OK); | ||
} | ||
} |
This file contains 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 gdsc.comunity.dto; | ||
|
||
import lombok.Builder; | ||
|
||
@Builder | ||
public record JwtTokensDto( | ||
String accessToken, | ||
String refreshToken | ||
) { | ||
} |
This file contains 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 gdsc.comunity.dto; | ||
|
||
import lombok.Builder; | ||
|
||
@Builder | ||
public record RefreshTokenDto( | ||
String refreshToken | ||
) { | ||
} |
This file contains 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 gdsc.comunity.exception; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
@Getter | ||
@RequiredArgsConstructor | ||
public class CustomException extends RuntimeException{ | ||
private final ErrorCode errorCode; | ||
} |
This file contains 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,23 @@ | ||
package gdsc.comunity.exception; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import org.springframework.http.HttpStatus; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public enum ErrorCode { | ||
//401 | ||
LOGIN_REQUIRED(40100, HttpStatus.UNAUTHORIZED, "로그인이 필요합니다."), | ||
INVALID_REFRESH_TOKEN_ERROR(40101, HttpStatus.UNAUTHORIZED, "유효하지 않은 리프레시 토큰입니다."), | ||
INVALID_ACCESS_TOKEN_ERROR(40102, HttpStatus.UNAUTHORIZED, "유효하지 않은 엑세스 토큰입니다."), | ||
EMPTY_REFRESH_TOKEN_ERROR(40103, HttpStatus.UNAUTHORIZED, "해당 유저의 리프레시 토큰이 존재하지 않습니다."), | ||
INVALID_TOKEN_PREFIX_ERROR(40104, HttpStatus.UNAUTHORIZED, "토큰의 prefix가 올바르지 않습니다"), | ||
|
||
//403 | ||
OAUTH_SERVER_ERROR(40300, HttpStatus.BAD_GATEWAY, "OAuth 서버 에러입니다."); | ||
|
||
private final int code; | ||
private final HttpStatus status; | ||
private final String message; | ||
} |
36 changes: 36 additions & 0 deletions
36
src/main/java/gdsc/comunity/interceptor/UserIdArgumentResolver.java
This file contains 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,36 @@ | ||
package gdsc.comunity.interceptor; | ||
|
||
import gdsc.comunity.annotation.UserId; | ||
import gdsc.comunity.exception.CustomException; | ||
import gdsc.comunity.exception.ErrorCode; | ||
import org.springframework.core.MethodParameter; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.bind.support.WebDataBinderFactory; | ||
import org.springframework.web.context.request.NativeWebRequest; | ||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
import org.springframework.web.method.support.ModelAndViewContainer; | ||
|
||
@Component | ||
public class UserIdArgumentResolver implements HandlerMethodArgumentResolver { | ||
|
||
//parameter객체의 타입을 확인하는 메소드 | ||
@Override | ||
public boolean supportsParameter(MethodParameter parameter) { | ||
return parameter.getParameterType().equals(Long.class) | ||
&& parameter.hasParameterAnnotation(UserId.class); | ||
} | ||
|
||
@Override | ||
public Object resolveArgument( | ||
MethodParameter parameter, | ||
ModelAndViewContainer mavContainer, | ||
NativeWebRequest webRequest, | ||
WebDataBinderFactory binderFactory | ||
) { | ||
Object userIdObj = webRequest.getAttribute("userId", NativeWebRequest.SCOPE_REQUEST); | ||
if (userIdObj == null) { | ||
throw new CustomException(ErrorCode.LOGIN_REQUIRED); | ||
} | ||
return Long.parseLong((String) userIdObj); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/main/java/gdsc/comunity/interceptor/UserIdInterceptor.java
This file contains 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,27 @@ | ||
package gdsc.comunity.interceptor; | ||
|
||
import gdsc.comunity.security.info.UserPrincipal; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.servlet.HandlerInterceptor; | ||
|
||
@Slf4j | ||
@Component | ||
public class UserIdInterceptor implements HandlerInterceptor { | ||
@Override | ||
public boolean preHandle( | ||
HttpServletRequest request, | ||
HttpServletResponse response, | ||
Object handler | ||
) throws Exception { | ||
UserPrincipal userPrincipal = (UserPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); | ||
|
||
//request객체에 UserId 값 추가 | ||
request.setAttribute("userId", userPrincipal.getId()); | ||
|
||
return HandlerInterceptor.super.preHandle(request, response, handler); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
src/main/java/gdsc/comunity/interceptor/config/WebMvcConfig.java
This file contains 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,36 @@ | ||
package gdsc.comunity.interceptor.config; | ||
|
||
import gdsc.comunity.interceptor.UserIdArgumentResolver; | ||
import gdsc.comunity.interceptor.UserIdInterceptor; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
import org.springframework.web.servlet.config.annotation.EnableWebMvc; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
@Configuration | ||
@EnableWebMvc | ||
@RequiredArgsConstructor | ||
public class WebMvcConfig implements WebMvcConfigurer { | ||
private final UserIdArgumentResolver userIdArgumentResolver; | ||
private final UserIdInterceptor userIdInterceptor; | ||
|
||
@Override | ||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { | ||
WebMvcConfigurer.super.addArgumentResolvers(resolvers); | ||
resolvers.add(this.userIdArgumentResolver); | ||
} | ||
|
||
@Override | ||
public void addInterceptors(final InterceptorRegistry registry) { | ||
registry.addInterceptor(userIdInterceptor) | ||
.addPathPatterns("/**") | ||
.excludePathPatterns("/api/login", | ||
"/api/auth/register", | ||
"/oauth/**", | ||
"/swagger-ui/**", | ||
"/v3/api-docs/**"); | ||
} | ||
} |
2 changes: 2 additions & 0 deletions
2
src/main/java/gdsc/comunity/repository/user/UserRepository.java
This file contains 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,7 +1,9 @@ | ||
package gdsc.comunity.repository.user; | ||
|
||
import gdsc.comunity.entity.user.User; | ||
import java.util.Optional; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface UserRepository extends JpaRepository<User, Long> { | ||
Optional<User> findByEmail(String email); | ||
} |
92 changes: 92 additions & 0 deletions
92
src/main/java/gdsc/comunity/security/config/SecurityConfig.java
This file contains 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,92 @@ | ||
package gdsc.comunity.security.config; | ||
|
||
import gdsc.comunity.exception.CustomException; | ||
import gdsc.comunity.exception.ErrorCode; | ||
import gdsc.comunity.security.filter.JwtAuthenticationFilter; | ||
import gdsc.comunity.security.filter.JwtExceptionFilter; | ||
import gdsc.comunity.security.info.UserPrincipal; | ||
import gdsc.comunity.security.jwt.JwtProvider; | ||
import gdsc.comunity.security.jwt.RefreshToken; | ||
import gdsc.comunity.security.jwt.RefreshTokenRepository; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.util.Optional; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.http.SessionCreationPolicy; | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; | ||
import org.springframework.security.web.authentication.logout.LogoutFilter; | ||
|
||
@Slf4j | ||
@Configuration | ||
@RequiredArgsConstructor | ||
public class SecurityConfig { | ||
private final JwtProvider jwtProvider; | ||
private final RefreshTokenRepository refreshTokenRepository; | ||
|
||
@Bean | ||
public BCryptPasswordEncoder passwordEncoder() { | ||
return new BCryptPasswordEncoder(); | ||
} | ||
|
||
@Bean | ||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
http | ||
.csrf(AbstractHttpConfigurer::disable) | ||
.httpBasic(AbstractHttpConfigurer::disable) | ||
.sessionManagement(sm -> | ||
sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS) | ||
) | ||
|
||
.authorizeHttpRequests(registry -> | ||
registry | ||
.requestMatchers("/api/login", "/api/auth/register").permitAll() | ||
.requestMatchers("/oauth/**").permitAll() | ||
.requestMatchers("/api/auth/refresh").permitAll() | ||
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() | ||
.anyRequest().authenticated() | ||
) | ||
.formLogin(AbstractHttpConfigurer::disable) | ||
|
||
.logout(logout -> logout | ||
.logoutUrl("/api/auth/logout") | ||
.addLogoutHandler((request, response, authentication) -> { | ||
//1. authentication에서 userId 추출 | ||
Long userId = ((UserPrincipal) authentication.getPrincipal()).getId(); | ||
|
||
//2. 해당 userId로 redis에서 refresh token 조회 후 삭제 | ||
Optional<RefreshToken> refreshTokenOP = refreshTokenRepository.findByUserId(userId); | ||
if (refreshTokenOP.isEmpty()) { | ||
throw new CustomException(ErrorCode.EMPTY_REFRESH_TOKEN_ERROR); | ||
} | ||
refreshTokenRepository.delete(refreshTokenOP.get()); | ||
}) | ||
.logoutSuccessHandler((request, response, authentication) -> { | ||
response.setStatus(HttpStatus.OK.value()); | ||
response.addHeader("message", "logout success"); | ||
} | ||
) | ||
) | ||
|
||
.exceptionHandling(exceptionHandling -> exceptionHandling | ||
.authenticationEntryPoint((request, response, authException) -> response.setStatus(HttpServletResponse.SC_UNAUTHORIZED)) | ||
.accessDeniedHandler((request, response, accessDeniedException) -> response.setStatus(HttpServletResponse.SC_FORBIDDEN)) | ||
) | ||
|
||
.addFilterBefore( | ||
new JwtAuthenticationFilter(jwtProvider), | ||
LogoutFilter.class | ||
) | ||
.addFilterBefore( | ||
new JwtExceptionFilter(), | ||
JwtAuthenticationFilter.class | ||
); | ||
|
||
return http.build(); | ||
} | ||
} |
Oops, something went wrong.