Skip to content
Merged
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 @@ -57,8 +57,6 @@ public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exceptio
.authorizeHttpRequests(
auth ->
defaultAuthorizeHttpRequest(auth)
.requestMatchers(SWAGGER_ENDPOINTS).permitAll()
.requestMatchers(LOGIN_ENDPOINT).permitAll()
.anyRequest().authenticated()
).build();
}
Expand All @@ -83,8 +81,10 @@ private AbstractRequestMatcherRegistry<AuthorizeHttpRequestsConfigurer<HttpSecur
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.requestMatchers(HttpMethod.OPTIONS, "*").permitAll()
.requestMatchers(HttpMethod.GET, READ_ONLY_PUBLIC_ENDPOINTS).permitAll()
.requestMatchers(SWAGGER_ENDPOINTS).permitAll()
.requestMatchers(LOGIN_ENDPOINT).permitAll()
.requestMatchers(HEALTH_CHECK_ENDPOINT).permitAll()
.requestMatchers(REISSUANCE_ENDPOINTS).permitAll()
.requestMatchers(REISSUANCE_ENDPOINT).permitAll()
.requestMatchers(SWAGGER_ENDPOINTS).permitAll();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ private WebSecurityUrl() {
"/swagger/api-docs/**", "/swagger/v3/api-docs/**",
"/swagger-ui/**", "/swagger"
};
public static final String REISSUANCE_ENDPOINTS = "/api/auths/reissuance";
public static final String[] PUBLIC_ENDPOINTS = {LOGIN_ENDPOINT, REISSUANCE_ENDPOINTS};
public static final String REISSUANCE_ENDPOINT = "/api/auths/reissuance";
public static final String TEMPORARY_TOKEN_ALLOWED_ENDPOINT = "/api/members/initial-password";
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import clap.server.adapter.outbound.jwt.access.AccessTokenClaimKeys;
import clap.server.application.port.outbound.auth.ForbiddenTokenPort;
import clap.server.application.port.outbound.auth.JwtProvider;
import clap.server.exception.AuthException;
import clap.server.exception.JwtException;
import clap.server.exception.code.AuthErrorCode;
import io.jsonwebtoken.Claims;
Expand All @@ -24,11 +23,13 @@
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Arrays;
import java.util.stream.Stream;

import static clap.server.adapter.inbound.security.WebSecurityUrl.*;

Expand All @@ -42,13 +43,22 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final AccessDeniedHandler accessDeniedHandler;
private final ForbiddenTokenPort forbiddenTokenPort;

public static final String[] PUBLIC_ENDPOINTS = Stream.of(
HEALTH_CHECK_ENDPOINT,
READ_ONLY_PUBLIC_ENDPOINTS,
SWAGGER_ENDPOINTS
).flatMap(Arrays::stream).toArray(String[]::new);

public static final String[] ANONYMOUS_ENDPOINTS = {LOGIN_ENDPOINT, REISSUANCE_ENDPOINT};

@Override
protected void doFilterInternal(
@NotNull HttpServletRequest request,
@NotNull HttpServletResponse response,
@NotNull FilterChain filterChain
) throws ServletException, IOException {
try {

if (isAnonymousRequest(request)) {
filterChain.doFilter(request, response);
return;
Expand All @@ -66,10 +76,19 @@ protected void doFilterInternal(
}

private boolean isAnonymousRequest(HttpServletRequest request) {
String accessToken = request.getHeader(HttpHeaders.AUTHORIZATION);
return accessToken == null;
boolean isAnonymousURI = Arrays.stream(ANONYMOUS_ENDPOINTS)
.anyMatch(endpoint -> new AntPathMatcher().match(endpoint, request.getRequestURI()));
boolean isAnonymous = request.getHeader(HttpHeaders.AUTHORIZATION) == null;
return isAnonymousURI && isAnonymous;
}

@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return Arrays.stream(PUBLIC_ENDPOINTS)
.anyMatch(endpoint -> new AntPathMatcher().match(endpoint, request.getRequestURI()));
}


private String resolveAccessToken(
HttpServletRequest request
) throws ServletException {
Expand Down Expand Up @@ -106,6 +125,8 @@ private String resolveAccessToken(
}




private boolean isTemporaryTokenAllowed(String requestUrl) {
return requestUrl.equals(TEMPORARY_TOKEN_ALLOWED_ENDPOINT);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package clap.server.adapter.inbound.web.dto.member.request;

import jakarta.validation.constraints.NotBlank;

public record VerifyPasswordRequest(
@NotBlank
String password
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@
import clap.server.adapter.inbound.security.service.SecurityUserDetails;
import clap.server.adapter.inbound.web.dto.member.request.UpdateInitialPasswordRequest;
import clap.server.adapter.inbound.web.dto.member.request.UpdatePasswordRequest;
import clap.server.adapter.inbound.web.dto.member.request.VerifyPasswordRequest;
import clap.server.application.port.inbound.member.ResetInitialPasswordUsecase;
import clap.server.application.port.inbound.member.ResetPasswordUsecase;
import clap.server.application.port.inbound.member.VerifyPasswordUseCase;
import clap.server.common.annotation.architecture.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.*;

@Tag(name = "01. Member [비밀번호 관련]")
@WebAdapter
Expand All @@ -29,22 +27,22 @@ public class ResetPasswordController {
@Operation(summary = "초기 로그인 후 비밀번호 재설정 API")
@PatchMapping("/members/initial-password")
public void resetPasswordAndActivateMember(@AuthenticationPrincipal SecurityUserDetails userInfo,
@RequestBody UpdateInitialPasswordRequest request) {
@RequestBody @Valid UpdateInitialPasswordRequest request) {
resetInitialPasswordUsecase.resetPasswordAndActivateMember(userInfo.getUserId(),request.password());
}

@Operation(summary = "비밀번호 재설정 API")
@PatchMapping("/members/password")
public void resetPassword(@AuthenticationPrincipal SecurityUserDetails userInfo,
@RequestBody UpdatePasswordRequest request) {
@RequestBody @Valid UpdatePasswordRequest request) {
resetPasswordUsecase.resetPassword(userInfo.getUserId(), request.password());
}


@Operation(summary = "비밀번호 검증 API")
@GetMapping("/members/password")
@PostMapping("/members/password")
public void verifyPassword(@AuthenticationPrincipal SecurityUserDetails userInfo,
@RequestBody @NotBlank String password) {
verifyPasswordUseCase.verifyPassword(userInfo.getUserId(), password);
@RequestBody @Valid VerifyPasswordRequest request) {
verifyPasswordUseCase.verifyPassword(userInfo.getUserId(), request.password());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public enum AuthErrorCode implements BaseErrorCode {
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "AUTH_001", "인증 과정에서 오류가 발생하였습니다."),
FORBIDDEN(HttpStatus.FORBIDDEN, "AUTH_002", "접근이 거부되었습니다"),
EMPTY_ACCESS_KEY(HttpStatus.FORBIDDEN, "AUTH_003", "AccessToken 이 비어있습니다."),
LOGOUT_ERROR(HttpStatus.FORBIDDEN, "AUTH_004", "로그 아웃된 사용자입니다."),
// LOGOUT_ERROR(HttpStatus.FORBIDDEN, "AUTH_004", "로그 아웃된 사용자입니다."),
EXPIRED_TOKEN(HttpStatus.FORBIDDEN, "AUTH_005", "사용기간이 만료된 토큰입니다."),
TAKEN_AWAY_TOKEN(HttpStatus.FORBIDDEN, "AUTH_006", "탈취당한 토큰입니다. 다시 로그인 해주세요."),
WITHOUT_OWNER_REFRESH_TOKEN(HttpStatus.FORBIDDEN, "AUTH_007", "소유자가 아닌 RefreshToken 입니다."),
Expand Down