Skip to content

Commit

Permalink
Merge pull request #122 from Team-SilverTown/feature
Browse files Browse the repository at this point in the history
🦾 개발 서버 배포
  • Loading branch information
IjjS authored Mar 14, 2024
2 parents 3288748 + 8060d5b commit de6f028
Show file tree
Hide file tree
Showing 62 changed files with 1,740 additions and 262 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package team.silvertown.masil.common.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
Expand Down Expand Up @@ -91,4 +92,17 @@ public ResponseEntity<ErrorResponse> handleHttpMessageNotReadableException(
return handleUnknownException((Exception) rootCause);
}

@ExceptionHandler(BeanInstantiationException.class)
public ResponseEntity<ErrorResponse> handleBeanInstantiationException(
BeanInstantiationException e
) {
Throwable rootCause = e.getRootCause();

if (rootCause instanceof BadRequestException) {
return handleBadRequestException((BadRequestException) rootCause);
}

return handleUnknownException(e);
}

}
27 changes: 27 additions & 0 deletions src/main/java/team/silvertown/masil/common/scroll/OrderType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package team.silvertown.masil.common.scroll;

import io.micrometer.common.util.StringUtils;
import java.util.Arrays;
import java.util.Objects;
import team.silvertown.masil.common.exception.BadRequestException;
import team.silvertown.masil.common.scroll.dto.ScrollErrorCode;

public enum OrderType {
LATEST,
MOST_POPULAR;

public static OrderType get(String order) {
if (StringUtils.isBlank(order)) {
return LATEST;
}

return Arrays.stream(OrderType.values())
.filter(orderType -> order.equals(orderType.name()))
.findFirst()
.orElseThrow(() -> new BadRequestException(ScrollErrorCode.INVALID_ORDER_TYPE));
}

public static boolean isMostPopular(OrderType orderType) {
return Objects.isNull(orderType) || orderType == MOST_POPULAR;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package team.silvertown.masil.common.scroll.dto;

import java.util.Objects;
import lombok.Builder;
import lombok.Getter;

@Getter
public final class NormalListRequest {

private final String depth1;
private final String depth2;
private final String depth3;
private final ScrollRequest scrollRequest;

@Builder
private NormalListRequest(
String depth1,
String depth2,
String depth3,
String order,
String cursor,
int size
) {
this.depth1 = depth1;
this.depth2 = depth2;
this.depth3 = depth3;
this.scrollRequest = new ScrollRequest(order, cursor, size);
}

public int getSize() {
return this.scrollRequest.getSize();
}

public boolean isBasedOnAddress() {
return Objects.nonNull(depth1) && Objects.nonNull(depth2) && Objects.nonNull(depth3);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package team.silvertown.masil.common.scroll.dto;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import team.silvertown.masil.common.exception.ErrorCode;

@RequiredArgsConstructor
@Getter
public enum ScrollErrorCode implements ErrorCode {
INVALID_ORDER_TYPE(20311000, "올바르지 않은 정렬 기준입니다"),
INVALID_CURSOR_FORMAT(20311001, "정렬 기준에 맞지 않은 커서 형식입니다");

private final int code;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package team.silvertown.masil.common.scroll.dto;

import lombok.Getter;
import team.silvertown.masil.common.scroll.OrderType;
import team.silvertown.masil.common.validator.ScrollValidator;

@Getter
public final class ScrollRequest {

private final OrderType order;
private final String cursor;
private final int size;

public ScrollRequest(
String order,
String cursor,
int size
) {
OrderType orderType = OrderType.get(order);

ScrollValidator.validateCursorFormat(cursor, orderType);

this.order = orderType;
this.cursor = cursor;
this.size = size;
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package team.silvertown.masil.common.response;
package team.silvertown.masil.common.scroll.dto;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package team.silvertown.masil.common.validator;

import io.micrometer.common.util.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import team.silvertown.masil.common.exception.BadRequestException;
import team.silvertown.masil.common.scroll.OrderType;
import team.silvertown.masil.common.scroll.dto.ScrollErrorCode;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ScrollValidator extends Validator {

private static final int ID_CURSOR_LENGTH = 16;
private static final String INITIAL_CURSUR = "0";

public static void validateCursorFormat(String cursor, OrderType order) {
if (StringUtils.isBlank(cursor) || cursor.equals(INITIAL_CURSUR)) {
return;
}

if (OrderType.isMostPopular(order)) {
throwIf(cursor.length() != ID_CURSOR_LENGTH,
() -> new BadRequestException(ScrollErrorCode.INVALID_CURSOR_FORMAT));
return;
}

notOver(cursor.length(), ID_CURSOR_LENGTH, ScrollErrorCode.INVALID_CURSOR_FORMAT);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package team.silvertown.masil.config.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.JwtParser;
Expand All @@ -9,17 +10,18 @@
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.MacAlgorithm;
import io.micrometer.common.util.StringUtils;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.StringJoiner;
import javax.crypto.SecretKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import team.silvertown.masil.user.domain.UserAuthority;
import team.silvertown.masil.user.repository.UserAuthorityRepository;
import team.silvertown.masil.user.validator.UserValidator;
import team.silvertown.masil.user.domain.Authority;

@Slf4j
@Component
Expand All @@ -29,20 +31,19 @@ public class JwtTokenProvider {
private static final String EXPIRED_TOKEN = "Expired JWT token.";
private static final String UNSUPPORTED_TOKEN = "Unsupported JWT token.";
private static final String UNEXPECTED_TOKEN = "JWT token compact of handler are invalid.";
private static final String NO_AUTHORITY = "No authority included in JWT";
private static final String USER_ID_CLAIM = "user_id";
private static final String AUTHORITIES_CLAIM = "authorities";
private static final String AUTHORITIES_DELIM = " ";
private static final int MILLS = 1000;

private final long tokenValidityInMilliseconds;
private final String issuer;
private final MacAlgorithm algorithm;
private final SecretKey secretKey;
private final JwtParser jwtParser;
private final UserAuthorityRepository userAuthorityRepository;

public JwtTokenProvider(
JwtProperties jwtProperties,
UserAuthorityRepository userAuthorityRepository
) {
public JwtTokenProvider(JwtProperties jwtProperties) {
byte[] keyBytes = Decoders.BASE64.decode(jwtProperties.base64Secret());

this.tokenValidityInMilliseconds = jwtProperties.tokenValidityInSeconds() * MILLS;
Expand All @@ -53,32 +54,37 @@ public JwtTokenProvider(
.verifyWith(secretKey)
.requireIssuer(issuer)
.build();
this.userAuthorityRepository = userAuthorityRepository;
}

public String createToken(long userId) {
public String createToken(long userId, List<Authority> authorities) {
Date now = new Date();
Date validity = new Date(now.getTime() + tokenValidityInMilliseconds);
StringJoiner joiner = new StringJoiner(" ");

authorities.forEach(authority -> joiner.add(authority.getAuthority()));

return Jwts.builder()
.issuer(issuer)
.issuedAt(now)
.expiration(validity)
.claim(USER_ID_CLAIM, userId)
.claim(AUTHORITIES_CLAIM, joiner.toString())
.signWith(secretKey, algorithm)
.compact();
}

public Authentication getAuthentication(String token) {
long userId = jwtParser.parseSignedClaims(token)
.getPayload()
.get(USER_ID_CLAIM, Long.class);
Claims claims = jwtParser.parseSignedClaims(token)
.getPayload();
Long userId = claims.get(USER_ID_CLAIM, Long.class);
String authorityNames = claims.get(AUTHORITIES_CLAIM, String.class);

List<UserAuthority> userAuthorities = userAuthorityRepository.findAllByUserId(userId);
UserValidator.validateAuthority(userAuthorities);
if (StringUtils.isBlank(authorityNames)) {
throw new InsufficientAuthenticationException(NO_AUTHORITY);
}

List<GrantedAuthority> authorities = userAuthorities.stream()
.map(UserAuthority::getName)
List<Authority> authorities = Arrays.stream(authorityNames.split(AUTHORITIES_DELIM))
.map(Authority::get)
.toList();

return new UsernamePasswordAuthenticationToken(userId, token, authorities);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package team.silvertown.masil.config.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
Expand All @@ -23,7 +22,7 @@ public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException
) throws IOException, ServletException {
) throws IOException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,18 @@ public class HttpRequestsConfigurer
"/swagger-resources/**"
};
private static final String AUTH_RESOURCE = "/api/v1/users/login";
private static final String[] POST_GET_RESOURCES = {
"/api/v1/posts"
private static final String[] GET_PERMIT_ALL_RESOURCES = {
// users
"api/v1/users/**",

// posts
"/api/v1/posts",
"/api/v1/posts/**",

// mates
"/api/v1/mates/**"
};
private static final String USER_ME_RESOURCE = "/api/v1/users/me";
private static final String ADMIN_PANEL = "/{0}/**";

private final SnapAdminProperties snapAdminProperties;
Expand All @@ -40,10 +49,12 @@ public void customize(
.permitAll()
.requestMatchers(AUTH_RESOURCE)
.permitAll()
.requestMatchers(HttpMethod.GET, POST_GET_RESOURCES)
.requestMatchers(HttpMethod.GET, GET_PERMIT_ALL_RESOURCES)
.permitAll()
.requestMatchers(MessageFormat.format(ADMIN_PANEL, snapAdminProperties.getBaseUrl()))
.permitAll()
.requestMatchers(HttpMethod.GET, USER_ME_RESOURCE)
.authenticated()
.anyRequest()
.authenticated();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
@Getter
@AllArgsConstructor
public enum ImageErrorCode implements ErrorCode {
NOT_SUPPORTED_CONTENT(500_16000, "지원하지 않는 파일 형식 입니다");
NOT_SUPPORTED_CONTENT(500_16000, "지원하지 않는 파일 형식 입니다"),
FILE_IS_EMPTY(500_16001, "비어있는 파일입니다");

private final int code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package team.silvertown.masil.image.validator;

import org.springframework.web.multipart.MultipartFile;
import team.silvertown.masil.common.exception.BadRequestException;
import team.silvertown.masil.common.validator.Validator;
import team.silvertown.masil.image.exception.ImageErrorCode;

public class ImageFileServiceValidator extends Validator {

public static void validateImgFile(MultipartFile file) {
throwIf(file.isEmpty(), () -> new BadRequestException(ImageErrorCode.FILE_IS_EMPTY));
String contentType = file.getContentType();
throwIf(!ImageFileType.isImage(contentType),
() -> new BadRequestException(ImageErrorCode.NOT_SUPPORTED_CONTENT));
}

}
Loading

0 comments on commit de6f028

Please sign in to comment.