-
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 #16 from Team-Shaka/feat/2
✨ Feat : spring security 추가
- Loading branch information
Showing
10 changed files
with
518 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
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
10 changes: 10 additions & 0 deletions
10
src/main/java/treehouse/server/global/exception/JwtAuthenticationException.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,10 @@ | ||
package treehouse.server.global.exception; | ||
|
||
import org.springframework.security.core.AuthenticationException; | ||
|
||
public class JwtAuthenticationException extends AuthenticationException { | ||
|
||
public JwtAuthenticationException(GlobalErrorCode code) { | ||
super(code.name()); | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
src/main/java/treehouse/server/global/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,100 @@ | ||
package treehouse.server.global.security.config; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; | ||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; | ||
import org.springframework.security.config.http.SessionCreationPolicy; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
import org.springframework.web.cors.CorsConfigurationSource; | ||
import treehouse.server.global.security.filter.JwtAuthFilter; | ||
import treehouse.server.global.security.handler.JwtAccessDeniedHandler; | ||
import treehouse.server.global.security.handler.JwtAuthenticationEntryPoint; | ||
import treehouse.server.global.security.handler.JwtAuthenticationExceptionHandler; | ||
import treehouse.server.global.security.provider.TokenProvider; | ||
|
||
import java.util.Collections; | ||
|
||
import static org.springframework.security.config.Customizer.withDefaults; | ||
|
||
@Slf4j | ||
@RequiredArgsConstructor | ||
//@EnableWebSecurity(debug = true) | ||
@Configuration | ||
public class SecurityConfig { | ||
|
||
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint = new JwtAuthenticationEntryPoint(); | ||
|
||
private final JwtAccessDeniedHandler jwtAccessDeniedHandler = new JwtAccessDeniedHandler(); | ||
|
||
private final TokenProvider tokenProvider; | ||
|
||
private final JwtAuthenticationExceptionHandler jwtAuthenticationExceptionHandler = | ||
new JwtAuthenticationExceptionHandler(); | ||
|
||
private static final String[] JWT_WHITE_LIST ={ | ||
"/users/login-tmp","/users/reissue" | ||
}; | ||
|
||
/** | ||
* 특정 경로에 대한 보안 설정을 무시하도록 설정 | ||
* @return WebSecurityCustomizer | ||
*/ | ||
@Bean | ||
public WebSecurityCustomizer webSecurityCustomizer() { | ||
return (web) -> | ||
web.ignoring() | ||
.requestMatchers( | ||
"/health", | ||
"/schedule", | ||
"/v3/api-docs", | ||
"/v3/api-docs/**", | ||
"/favicon.io", | ||
"/swagger-ui/**", | ||
"/docs/**"); | ||
} | ||
|
||
@Bean | ||
public SecurityFilterChain JwtFilterChain(HttpSecurity http) throws Exception { | ||
return http.cors(corsConfigurer -> corsConfigurer.configurationSource(corsConfiguration())) | ||
.httpBasic(withDefaults()) | ||
.csrf(AbstractHttpConfigurer::disable) // 비활성화 | ||
.sessionManagement( | ||
manage -> | ||
manage.sessionCreationPolicy( | ||
SessionCreationPolicy.STATELESS)) // Session 사용 안함 | ||
.formLogin(AbstractHttpConfigurer::disable) | ||
.authorizeHttpRequests( | ||
authorize -> { | ||
// authorize.requestMatchers("/swagger-ui/**").permitAll(); | ||
authorize.requestMatchers("/users/**").permitAll(); | ||
authorize.anyRequest().authenticated(); | ||
}) | ||
.exceptionHandling( | ||
exceptionHandling -> | ||
exceptionHandling | ||
.authenticationEntryPoint(jwtAuthenticationEntryPoint) | ||
.accessDeniedHandler(jwtAccessDeniedHandler)) | ||
.addFilterBefore( | ||
new JwtAuthFilter(tokenProvider, JWT_WHITE_LIST), | ||
UsernamePasswordAuthenticationFilter.class) | ||
.addFilterBefore(jwtAuthenticationExceptionHandler, JwtAuthFilter.class) | ||
.build(); | ||
} | ||
|
||
public CorsConfigurationSource corsConfiguration() { | ||
return request -> { | ||
org.springframework.web.cors.CorsConfiguration config = | ||
new org.springframework.web.cors.CorsConfiguration(); | ||
config.setAllowedHeaders(Collections.singletonList("*")); // 모든 헤더 허용 | ||
config.setAllowedMethods(Collections.singletonList("*")); // 모든 메소드 허용 | ||
config.setAllowedOriginPatterns(Collections.singletonList("*")); // 모든 Origin 허용 | ||
config.setAllowCredentials(true); // 인증정보 허용 | ||
return config; | ||
}; | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
src/main/java/treehouse/server/global/security/filter/JwtAuthFilter.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,84 @@ | ||
package treehouse.server.global.security.filter; | ||
|
||
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 lombok.extern.slf4j.Slf4j; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.util.StringUtils; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
import treehouse.server.global.common.CommonResponse; | ||
import treehouse.server.global.exception.GlobalErrorCode; | ||
import treehouse.server.global.security.provider.TokenProvider; | ||
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
|
||
@Slf4j | ||
@RequiredArgsConstructor | ||
// 들어오는 요청 처리 | ||
public class JwtAuthFilter extends OncePerRequestFilter { | ||
private final TokenProvider tokenProvider; | ||
|
||
private final String[] whiteList; | ||
|
||
|
||
/* 요청이 들어올 때마다 실행. | ||
* 토큰 확인, 토큰 유효성 검사, 토큰에 포함된 정보를 기반으로 인증 수행 */ | ||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||
throws ServletException, IOException { | ||
// HTTP 요청에서 Authorization헤더를 찾아 토큰 반환 | ||
String accessToken = tokenProvider.resolveToken(request, "Access"); | ||
|
||
|
||
// 토큰이 있다면 진행 | ||
if(StringUtils.hasText(accessToken) && tokenProvider.validateToken(accessToken)) { | ||
|
||
Authentication authentication = tokenProvider.getAuthentication(accessToken); | ||
SecurityContextHolder.getContext().setAuthentication(authentication); // 인증 정보를 SecurityContext에 설정 | ||
|
||
} | ||
else{ | ||
SecurityContextHolder.getContext().setAuthentication(null); | ||
} | ||
// 다음 단계 실행 -> 다른 필터 및 컨트롤러 실행 | ||
filterChain.doFilter(request,response); | ||
} | ||
|
||
|
||
private String getRefreshTokenFromRequest(HttpServletRequest request) { | ||
String refreshToken = request.getHeader("Refresh-Token"); | ||
if (StringUtils.hasText(refreshToken)) { | ||
return refreshToken; | ||
} | ||
return null; | ||
} | ||
|
||
|
||
// JWT 인증과 관련된 예외 처리 | ||
public void jwtExceptionHandler(HttpServletResponse response, GlobalErrorCode errorCode) { | ||
response.setStatus(errorCode.getHttpStatus().value()); | ||
response.setContentType("application/json"); | ||
|
||
try { | ||
// AuthErrorCode로부터 code와 message 추출 | ||
String code = errorCode.getCode(); | ||
String message = errorCode.getMessage(); | ||
String json = new ObjectMapper().writeValueAsString(CommonResponse.onFailure(code, message, null)); // ApiResponse 객체를 JSON으로 변환 | ||
response.getWriter().write(json); | ||
} catch (Exception e) { | ||
log.error(e.getMessage()); | ||
} | ||
} | ||
|
||
@Override | ||
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { | ||
String path = request.getRequestURI(); | ||
return Arrays.stream(whiteList).anyMatch(path::startsWith); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
src/main/java/treehouse/server/global/security/handler/JwtAccessDeniedHandler.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,39 @@ | ||
package treehouse.server.global.security.handler; | ||
|
||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.security.access.AccessDeniedException; | ||
import org.springframework.security.web.access.AccessDeniedHandler; | ||
import treehouse.server.global.common.CommonResponse; | ||
import treehouse.server.global.exception.GlobalErrorCode; | ||
|
||
import java.io.IOException; | ||
import java.io.PrintWriter; | ||
|
||
public class JwtAccessDeniedHandler implements AccessDeniedHandler { | ||
|
||
private final Logger LOGGER = LoggerFactory.getLogger(JwtAccessDeniedHandler.class); | ||
|
||
@Override | ||
public void handle( | ||
HttpServletRequest request, | ||
HttpServletResponse response, | ||
AccessDeniedException accessDeniedException) | ||
throws IOException, ServletException { | ||
|
||
response.setContentType("application/json; charset=UTF-8"); | ||
response.setStatus(403); | ||
PrintWriter writer = response.getWriter(); | ||
|
||
// AuthErrorCode.AUTHENTICATION_DENIED enum에서 코드와 메시지를 얻음 | ||
String code = GlobalErrorCode.AUTHENTICATION_DENIED.getCode(); | ||
String message = GlobalErrorCode.AUTHENTICATION_DENIED.getMessage(); | ||
CommonResponse<String> apiErrorResult = CommonResponse.onFailure(code, message, null); | ||
writer.write(apiErrorResult.toString()); | ||
writer.flush(); | ||
writer.close(); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/main/java/treehouse/server/global/security/handler/JwtAuthenticationEntryPoint.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,41 @@ | ||
package treehouse.server.global.security.handler; | ||
|
||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.AuthenticationEntryPoint; | ||
import treehouse.server.global.common.CommonResponse; | ||
import treehouse.server.global.exception.GlobalErrorCode; | ||
|
||
import java.io.IOException; | ||
import java.io.PrintWriter; | ||
|
||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||
|
||
|
||
private final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class); | ||
|
||
@Override | ||
public void commence( | ||
HttpServletRequest request, | ||
HttpServletResponse response, | ||
AuthenticationException authException) | ||
throws IOException, ServletException { | ||
response.setContentType("application/json; charset=UTF-8"); | ||
response.setStatus(401); | ||
PrintWriter writer = response.getWriter(); | ||
|
||
// AuthErrorCode.AUTHENTICATION_REQUIRED enum에서 코드와 메시지를 얻음 | ||
String code = GlobalErrorCode.AUTHENTICATION_REQUIRED.getCode(); | ||
String message = GlobalErrorCode.AUTHENTICATION_REQUIRED.getMessage(); | ||
CommonResponse<String> apiErrorResult = CommonResponse.onFailure(code, message, null); | ||
|
||
writer.write(apiErrorResult.toString()); | ||
writer.flush(); | ||
writer.close(); | ||
} | ||
} | ||
|
38 changes: 38 additions & 0 deletions
38
...main/java/treehouse/server/global/security/handler/JwtAuthenticationExceptionHandler.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,38 @@ | ||
package treehouse.server.global.security.handler; | ||
|
||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
import treehouse.server.global.common.CommonResponse; | ||
import treehouse.server.global.exception.GlobalErrorCode; | ||
import treehouse.server.global.exception.JwtAuthenticationException; | ||
|
||
import java.io.IOException; | ||
import java.io.PrintWriter; | ||
|
||
public class JwtAuthenticationExceptionHandler extends OncePerRequestFilter { | ||
|
||
@Override | ||
protected void doFilterInternal( | ||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||
throws ServletException, IOException { | ||
try { | ||
filterChain.doFilter(request, response); | ||
} catch (JwtAuthenticationException authException) { | ||
response.setContentType("application/json; charset=UTF-8"); | ||
response.setStatus(HttpStatus.UNAUTHORIZED.value()); | ||
|
||
PrintWriter writer = response.getWriter(); | ||
String errorCodeName = authException.getMessage(); | ||
GlobalErrorCode errorCode = GlobalErrorCode.valueOf(errorCodeName); | ||
CommonResponse<String> apiErrorResult = CommonResponse.onFailure(errorCode.getCode(),errorCode.getMessage(), null); | ||
|
||
writer.write(apiErrorResult.toString()); | ||
writer.flush(); | ||
writer.close(); | ||
} | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
src/main/java/treehouse/server/global/security/handler/annotation/AuthMember.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,4 @@ | ||
package treehouse.server.global.security.handler.annotation; | ||
|
||
public @interface AuthMember { | ||
} |
Oops, something went wrong.