Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthenticationFilte
.authorizeHttpRequests(auth -> auth
// 인증 없이 로그인,회원가입은 가능.
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/auth/**", "/email/**").permitAll()
.requestMatchers("/auth/login", "/auth/password/update-link/send",
"/auth/password", "/email/**").permitAll()
.requestMatchers("/signup").permitAll()

// 로그아웃은 로그인 이후 가능
.requestMatchers("/auth/logout").hasAnyAuthority("일반", "관리자")

// 회원 관련된 건 일반 권한 필요
.requestMatchers("/members/**").hasAuthority("일반")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.keunsori.keunsoriserver.domain.member.domain.Member;
import com.keunsori.keunsoriserver.domain.member.repository.MemberRepository;
import com.keunsori.keunsoriserver.global.exception.AuthException;
import com.keunsori.keunsoriserver.global.properties.JwtProperties;
import com.keunsori.keunsoriserver.global.util.TokenUtil;
import com.keunsori.keunsoriserver.global.util.CookieUtil;
import jakarta.servlet.FilterChain;
Expand Down Expand Up @@ -32,8 +33,13 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
// 프리플라이트는 통과
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true;
}
String path = request.getServletPath();
return path.startsWith("/auth/login");
return path.equals("/auth/login") || path.startsWith("/signup") || path.startsWith("/email/")
|| path.startsWith("/swagger-ui/") || path.startsWith("/v3/api-docs/");
}

@Override
Expand All @@ -42,38 +48,98 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
String accessToken = CookieUtil.getCookieValue(request, "Access-Token");
String refreshToken = CookieUtil.getCookieValue(request, "Refresh-Token");

if (accessToken == null) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
accessToken = authHeader.substring(7); // "Bearer " 뒷부분만 추출
}
// Swagger 전용
String bearerAccessToken = null;
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
bearerAccessToken = authHeader.substring(7);
}

boolean authenticated = false;

// Access Token 검사
if (accessToken != null){
try {
tokenUtil.validateToken(accessToken);

String studentId = tokenUtil.getStudentIdFromToken(accessToken);
String status = tokenUtil.getStatusFromToken(accessToken);

SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(
studentId,
null,
List.of(new SimpleGrantedAuthority(status))
)
);
authenticated = true;

// Refresh-Token 만료 시 재발급
boolean neednewRefreshToken = (refreshToken == null);

if (!neednewRefreshToken) {
try {
tokenUtil.validateToken(refreshToken);
} catch (AuthException e) {
neednewRefreshToken = true;
}
}

if (neednewRefreshToken) {
Member member = memberRepository.findByStudentIdIgnoreCase(studentId)
.orElseThrow(() -> new AuthException(STUDENT_ID_NOT_EXISTS));
String newRefreshToken = tokenUtil.generateRefreshToken(member.getStudentId(), member.getName(), member.getStatus());
refreshTokenRepository.saveRefreshToken(member.getStudentId(), newRefreshToken, JwtProperties.REFRESH_TOKEN_VALIDITY_TIME);
CookieUtil.addRefreshTokenCookie(response,newRefreshToken);
}
} catch (AuthException ignored) {
// Access-Token 존재 X -> Refresh-Token 검사로 넘어감
}
}

if (accessToken != null) {
tokenUtil.validateToken(accessToken);

String studentId = tokenUtil.getStudentIdFromToken(accessToken);
String status = tokenUtil.getStatusFromToken(accessToken);
// Refresh Token 검사
if (!authenticated && refreshToken != null) {
tokenUtil.validateToken(refreshToken);

UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(studentId, null, List.of(() -> status));
SecurityContextHolder.getContext().setAuthentication(authentication);
} else if (refreshToken != null) {
String studentId = tokenUtil.getStudentIdFromToken(refreshToken);
String storedRefreshToken = refreshTokenRepository.getRefreshToken(studentId);

if (storedRefreshToken == null || !storedRefreshToken.equals(refreshToken)) {
CookieUtil.deleteCookie(response, "Access-Token");
CookieUtil.deleteCookie(response, "Refresh-Token");
throw new AuthException(INVALID_REFRESH_TOKEN);
}

Member member = memberRepository.findByStudentIdIgnoreCase(studentId)
.orElseThrow(() -> new AuthException(STUDENT_ID_NOT_EXISTS));

String newAccessToken = tokenUtil.generateAccessToken(member.getStudentId(), member.getName(), member.getStatus());

CookieUtil.addAccessTokenCookie(response, newAccessToken);

UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(studentId, null, List.of(new SimpleGrantedAuthority(member.getStatus().name())));
SecurityContextHolder.getContext().setAuthentication(authentication);
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
studentId,
null,
List.of(new SimpleGrantedAuthority(member.getStatus().name()))
)
);
authenticated = true;
}

// Swagger 전용 다시 시도
if (!authenticated && bearerAccessToken != null) {
tokenUtil.validateToken(bearerAccessToken);

String studentId = tokenUtil.getStudentIdFromToken(bearerAccessToken);
String status = tokenUtil.getStatusFromToken(bearerAccessToken);

SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(
studentId,
null,
List.of(new SimpleGrantedAuthority(status))
)
);
authenticated = true;
Comment on lines +128 to +142
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 스웨거 검증 로직이 따로 있는 이유가 있을까요?
엑세스 토큰을 검증하는 로직이 동일한 것 같아서, 엑세스 토큰, 리프레시 토큰이 모두 없다면 -> 헤더 체크해서 엑세스 토큰 세팅하고 -> 엑세스 토큰이 있다면 검증 수행 과 같은 플로우로 가도 괜찮지 않을까 생각했어요

}

chain.doFilter(request, response);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package com.keunsori.keunsoriserver.global.util;

import com.keunsori.keunsoriserver.domain.member.domain.vo.MemberStatus;
import com.keunsori.keunsoriserver.global.constant.TokenConstant;
import com.keunsori.keunsoriserver.global.exception.AuthException;
import com.keunsori.keunsoriserver.global.properties.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Date;
Expand Down
Loading