Skip to content

Commit

Permalink
Merge pull request #53 from kakao-tech-campus-2nd-step3/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
amm0124 authored Oct 7, 2024
2 parents ec9b123 + 14637ed commit d779b70
Show file tree
Hide file tree
Showing 13 changed files with 310 additions and 18 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ dependencies {

// Redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// AWS
implementation(platform("software.amazon.awssdk:bom:2.27.21"))
implementation("software.amazon.awssdk:s3")

}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package poomasi.domain.auth.config;

import jdk.jfr.Description;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import poomasi.domain.auth.util.JwtUtil;
import poomasi.global.redis.service.RedisService;
import poomasi.global.util.JwtUtil;

@Configuration
public class SecurityBeanGenerator {

@Autowired
private RedisService redisService;

@Bean
@Description("AuthenticationProvider를 위한 Spring bean")
public PasswordEncoder passwordEncoder() {
Expand All @@ -27,7 +32,7 @@ MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
@Bean
@Description("jwt 토큰 발급을 위한 spring bean")
JwtUtil jwtProvider() {
return new JwtUtil();
return new JwtUtil(redisService);
}
}

11 changes: 6 additions & 5 deletions src/main/java/poomasi/domain/auth/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
import poomasi.domain.auth.security.filter.CustomLogoutFilter;
import poomasi.domain.auth.security.filter.CustomUsernamePasswordAuthenticationFilter;
import poomasi.domain.auth.security.filter.JwtAuthenticationFilter;
import poomasi.domain.auth.util.JwtUtil;
import poomasi.global.util.JwtUtil;


@AllArgsConstructor
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true) // 인가 처리에 대한 annotation
@EnableMethodSecurity(securedEnabled = false, prePostEnabled = false) // 인가 처리에 대한 annotation
public class SecurityConfig {

private final AuthenticationConfiguration authenticationConfiguration;
Expand All @@ -40,8 +41,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
// TODO : 나중에 허용될 endpoint가 많아지면 whiteList로 관리 예정
// 임시로 GET : [api/farms, api/products, api/login, api/signup, /]은 열어둠
http.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(HttpMethod.GET, "/api/farms/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/products/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/farm/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/product/**").permitAll()
.requestMatchers("/api/login", "/", "/api/signup").permitAll()
.anyRequest().
authenticated()
Expand Down Expand Up @@ -74,7 +75,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
http.httpBasic(AbstractHttpConfigurer::disable);

//log out filter 추가
http.addFilterBefore(new CustomLogoutFilter(), CustomLogoutFilter.class);
//http.addFilterBefore(new CustomLogoutFilter(), CustomLogoutFilter.class);
return http.build();

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@

import java.io.IOException;

public class CustomLogoutFilter extends OncePerRequestFilter {
//extends OncePerRequestFilter
public class CustomLogoutFilter {

@Override
/*@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// access token 블랙리스트 저장해야 함
// refresh token 삭제해야 함
}
*/
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import poomasi.domain.auth.security.userdetail.UserDetailsImpl;
import poomasi.domain.auth.util.JwtUtil;
import poomasi.global.util.JwtUtil;

@RequiredArgsConstructor
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
Expand All @@ -37,8 +37,8 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR
String username = customUserDetails.getUsername();
String role = customUserDetails.getAuthority();

String accessToken = jwtUtil.generateAccessToken(username, role);
String refreshToken = jwtUtil.generateRefreshToken(username, role);
String accessToken = "";//jwtUtil.generateAccessToken(username, role);
String refreshToken = "";//jwtUtil.generateRefreshToken(username, role);

response.setHeader("access", accessToken);
response.addCookie(createCookie("refresh", refreshToken));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;
import poomasi.domain.auth.util.JwtUtil;
import poomasi.domain.member.entity.Member;
import poomasi.domain.member.entity.Role;
import poomasi.global.util.JwtUtil;

import java.io.IOException;
import java.io.PrintWriter;
Expand Down Expand Up @@ -54,7 +54,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
}

// access token 추출하기
String tokenType = jwtUtil.getTokenTypeFromToken(accessToken);
String tokenType = "";//jwtUtil.getTokenTypeFromToken(accessToken);

if(!tokenType.equals("access")){
log.info("[인증 실패] - 위조된 토큰입니다.");
Expand All @@ -64,13 +64,13 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
return;
}

String username = jwtUtil.getEmailFromToken(accessToken);
String role = jwtUtil.getRoleFromToken(accessToken);
//String username = jwtUtil.getEmailFromToken(accessToken);
//String role = jwtUtil.getRoleFromToken(accessToken);

//TODO : Object, Object, Collection 형태 ..처리 해야 함
//TODO : userDetailsImpl(), null(password)
//TODO : security context에 저장해야 함.
Member member = new Member(username, Role.valueOf(role));
//Member member = new Member(username, Role.valueOf(role));



Expand Down
20 changes: 20 additions & 0 deletions src/main/java/poomasi/global/config/aws/AwsProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package poomasi.global.config.aws;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties("aws")
public class AwsProperties {
private String access;
private String secret;
private S3Properties s3;

@Data
public static class S3Properties {
private String bucket;
private String region;
}
}
52 changes: 52 additions & 0 deletions src/main/java/poomasi/global/config/s3/S3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package poomasi.global.config.s3;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import poomasi.global.config.aws.AwsProperties;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;

@Configuration
@RequiredArgsConstructor
public class S3Config {

private final AwsProperties awsProperties;

@Bean("awsCredentials")
public AwsCredentialsProvider awsCredentials() {
return DefaultCredentialsProvider.create();
}

@Bean
public S3Presigner s3Presigner(@Qualifier("awsCredentials") AwsCredentialsProvider awsCredentials) {
return S3Presigner
.builder()
.credentialsProvider(awsCredentials)
.region(Region.of(awsProperties.getS3().getRegion()))
.build();
}

@Bean("s3AsyncClient")
public S3AsyncClient s3AsyncClient(@Qualifier("awsCredentials") AwsCredentialsProvider awsCredentials) {
return S3AsyncClient
.builder()
.credentialsProvider(awsCredentials)
.region(Region.of(awsProperties.getS3().getRegion()))
.build();
}

@Bean("s3Client")
public S3Client s3Client(@Qualifier("awsCredentials") AwsCredentialsProvider awsCredentials) {
return S3Client
.builder()
.credentialsProvider(awsCredentials)
.region(Region.of(awsProperties.getS3().getRegion()))
.build();
}
}
80 changes: 80 additions & 0 deletions src/main/java/poomasi/global/config/s3/S3PresignedUrlService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package poomasi.global.config.s3;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import poomasi.global.util.EncryptionUtil;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class S3PresignedUrlService {
private final S3Presigner s3Presigner;
private final EncryptionUtil encryptionUtil;
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final Long SIGNATURE_DURATION = 10L;


public String createPresignedGetUrl(String bucketName, String keyName) {
GetObjectRequest objectRequest = GetObjectRequest.builder()
.bucket(bucketName)
.key(keyName)
.build();

GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(SIGNATURE_DURATION))
.getObjectRequest(objectRequest)
.build();

PresignedGetObjectRequest presignedRequest = s3Presigner.presignGetObject(presignRequest);
log.info("Presigned URL: [{}]", presignedRequest.url().toString());
log.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

return presignedRequest.url().toExternalForm();

}

public String createPresignedPutUrl(String bucketName, String keyPrefix, Map<String, String> metadata) {
LocalDateTime now = LocalDateTime.now();
String date = now.format(DATE_FORMATTER);
String encodedTime = encryptionUtil.encodeTime(now).substring(0, 10);

String keyName = String.format("%s/%s/%s.jpg", keyPrefix, date, encodedTime);


PutObjectRequest objectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(keyName)
.metadata(metadata)
.build();

PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(SIGNATURE_DURATION))
.putObjectRequest(objectRequest)
.build();


PresignedPutObjectRequest presignedRequest = s3Presigner.presignPutObject(presignRequest);
String myURL = presignedRequest.url().toString();
log.info("Presigned URL to upload a file to: [{}]", myURL);
log.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

return presignedRequest.url().toExternalForm();
}


}

// reference: https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/userguide/example_s3_Scenario_PresignedUrl_section.html
25 changes: 25 additions & 0 deletions src/main/java/poomasi/global/config/s3/TestController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package poomasi.global.config.s3;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class TestController {
private final S3PresignedUrlService s3PresignedUrlService;

@GetMapping("/presigned-url-put")
public ResponseEntity<?> presignedUrlPut() {
String presignedPutUrl = s3PresignedUrlService.createPresignedPutUrl("poomasi", "test", null);
return ResponseEntity.ok(presignedPutUrl);
}

@GetMapping("/presigned-url-get")
public ResponseEntity<?> presignedUrlGet(@RequestParam String keyname) {
String presignedGetUrl = s3PresignedUrlService.createPresignedGetUrl("poomasi", keyname);
return ResponseEntity.ok(presignedGetUrl);
}
}
2 changes: 1 addition & 1 deletion src/main/java/poomasi/global/error/ApplicationError.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
@Getter
@AllArgsConstructor
public enum ApplicationError {
EXAMPLE_ERROR("애플리케이션 에러 예시입니다.");
ENCRYPT_ERROR("암호화 에러입니다.");

private final String message;

Expand Down
25 changes: 25 additions & 0 deletions src/main/java/poomasi/global/util/EncryptionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package poomasi.global.util;

import org.springframework.stereotype.Component;
import poomasi.global.error.ApplicationError;
import poomasi.global.error.ApplicationException;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.util.Base64;

@Component
public class EncryptionUtil {

public String encodeTime(LocalDateTime time) {
try {
String timeString = time.toString();
byte[] hash = MessageDigest.getInstance("SHA-256")
.digest(timeString.getBytes(StandardCharsets.UTF_8));
return Base64.getUrlEncoder().withoutPadding().encodeToString(hash);
} catch (Exception e) {
throw new ApplicationException(ApplicationError.ENCRYPT_ERROR);
}
}
}
Loading

0 comments on commit d779b70

Please sign in to comment.