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
Binary file added hajin/.DS_Store
Binary file not shown.
23 changes: 22 additions & 1 deletion hajin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@ dependencies {
implementation 'org.hibernate.orm:hibernate-core:6.0.2.Final'
implementation 'mysql:mysql-connector-java:8.0.33'

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'

// Anotation Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'

// Bean Validation
implementation 'org.hibernate.validator:hibernate-validator:8.0.0.Final'
//implementation 'org.hibernate.validator:hibernate-validator:8.0.0.Final'

// QueryDSL
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
Expand All @@ -54,6 +60,21 @@ dependencies {
implementation 'org.springdoc:springdoc-openapi-ui:1.6.9'
implementation 'org.springdoc:springdoc-openapi-data-rest:1.6.9'

// SoringBoot Security
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'

// thymeleaf
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

// thymeleaf ์—์„œ๋„ security ๋™์ž‘
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.1.RELEASE'

// jwt
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation "com.querydsl:querydsl-jpa"
}
Expand Down
Binary file added hajin/src/.DS_Store
Binary file not shown.
Binary file added hajin/src/main/.DS_Store
Binary file not shown.
Binary file added hajin/src/main/java/.DS_Store
Binary file not shown.
Binary file added hajin/src/main/java/umc/.DS_Store
Binary file not shown.
Binary file added hajin/src/main/java/umc/study/.DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions hajin/src/main/java/umc/study/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import umc.study.config.properties.JwtProperties;
import umc.study.service.StoreService.StoreQueryService;

@SpringBootApplication
@EntityScan(basePackages = "umc.study.domain")
@EnableJpaAuditing // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ์„ฑ๊ณผ ์ˆ˜์ • ์ •๋ณด ์ž๋™ ๊ธฐ๋ก, ๊ด€๋ฆฌ
@EnableConfigurationProperties(JwtProperties.class)
public class Application {

public static void main(String[] args) {
Expand Down
1 change: 0 additions & 1 deletion hajin/src/main/java/umc/study/apiPayload/ApiResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public class ApiResponse<T> {
private T reslt;

// ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ ์‘๋‹ต ์ƒ์„ฑ

public static <T> ApiResponse<T> onSuccess(T result){
return new ApiResponse<>(true, SuccessStatus._OK.getCode() , SuccessStatus._OK.getMessage(), result);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ public enum ErrorStatus implements BaseErrorCode {
_BAD_REQUEST(HttpStatus.BAD_REQUEST,"COMMON400","์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค."),
_UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"COMMON401","์ธ์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."),
_FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "๊ธˆ์ง€๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค."),

FOOD_CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค."),
STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค."),
// ๋ฉค๋ฒ„ ๊ด€๋ ค ์—๋Ÿฌ
MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "์‚ฌ์šฉ์ž๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."),
NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "๋‹‰๋„ค์ž„์€ ํ•„์ˆ˜ ์ž…๋‹ˆ๋‹ค."),
INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "MEMBER4003", "ํŒจ์Šค์›Œ๋“œ๊ฐ€ ๋ถˆ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค."),
// ํŽ˜์ด์ง• ์—๋Ÿฌ
PAGE_INVALID(HttpStatus.BAD_REQUEST, "PAGE001", "page๋Š” 1 ์ด์ƒ์˜ ๊ฐ’์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."),

// ์˜ˆ์‹œ
ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "๊ฒŒ์‹œ๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.");
// ์ธ์ฆ ์—๋Ÿฌ
INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "AUTH001", "์œ ํšจํ•˜์ง€ ์•Š์€ ํ† ํฐ์ž…๋‹ˆ๋‹ค.");

private final HttpStatus httpStatus;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
@Slf4j
@RestControllerAdvice(annotations = {RestController.class})
public class ExceptionAdvice extends ResponseEntityExceptionHandler {


@ExceptionHandler
public ResponseEntity<Object> validation(ConstraintViolationException e, WebRequest request) {
String errorMessage = e.getConstraintViolations().stream()
Expand Down Expand Up @@ -69,7 +67,7 @@ private ResponseEntity<Object> handleExceptionInternal(Exception e, ErrorReasonD
HttpHeaders headers, HttpServletRequest request) {

ApiResponse<Object> body = ApiResponse.onFailure(reason.getCode(),reason.getMessage(),null);
// e.printStackTrace();
// e.printStackTrace();

WebRequest webRequest = new ServletWebRequest(request);
return super.handleExceptionInternal(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package umc.study.apiPayload.exception.handler;

import umc.study.apiPayload.code.status.ErrorStatus;
import umc.study.apiPayload.exception.GeneralException;

public class FoodCategoryHandler extends GeneralException {
public FoodCategoryHandler(ErrorStatus errorStatus) {
super(errorStatus);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public class TempHandler extends GeneralException {
public TempHandler(BaseErrorCode errorCode) {
super(errorCode);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package umc.study.apiPayload.exception.handler;

import umc.study.apiPayload.code.BaseErrorCode;
import umc.study.apiPayload.exception.GeneralException;

public class UserHandler extends GeneralException {
public UserHandler(BaseErrorCode errorCode) {
super(errorCode);
}
}
39 changes: 39 additions & 0 deletions hajin/src/main/java/umc/study/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package umc.study.config;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SwaggerConfig {

@Bean
public OpenAPI UMCstudyAPI() {
Info info = new Info()
.title("UMC Server WorkBook API")
.description("UMC Server WorkBook API ๋ช…์„ธ์„œ")
.version("1.0.0");

String jwtSchemeName = "JWT TOKEN";
// API ์š”์ฒญํ—ค๋”์— ์ธ์ฆ์ •๋ณด ํฌํ•จ
SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtSchemeName);
// SecuritySchemes ๋“ฑ๋ก
Components components = new Components()
.addSecuritySchemes(jwtSchemeName, new SecurityScheme()
.name(jwtSchemeName)
.type(SecurityScheme.Type.HTTP) // HTTP ๋ฐฉ์‹
.scheme("bearer")
.bearerFormat("JWT"));

return new OpenAPI()
.addServersItem(new Server().url("/"))
.info(info)
.addSecurityItem(securityRequirement)
.components(components);
}
}
16 changes: 16 additions & 0 deletions hajin/src/main/java/umc/study/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package umc.study.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import umc.study.validation.resolver.PageParamResolver;

import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new PageParamResolver());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package umc.study.config.properties;

public final class Constants {
public static final String AUTH_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
}
31 changes: 31 additions & 0 deletions hajin/src/main/java/umc/study/config/properties/JwtProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package umc.study.config.properties;

import jakarta.annotation.PostConstruct;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@Getter
@Setter
@ConfigurationProperties("jwt.token")
public class JwtProperties {
private String secretKey="";
private Expiration expiration;

@Getter
@Setter
public static class Expiration{
private Long access;
// TODO: refreshToken
}

// ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ ๊ฐ’ ์ œ๋Œ€๋กœ ๋“ค์–ด์™”๋Š”์ง€ ํ™•์ธ
@PostConstruct
public void testInit() {
System.out.println("jwt.secretKey = " + secretKey);
System.out.println("jwt.expiration.access = " + (expiration != null ? expiration.getAccess() : "expiration is null"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package umc.study.config.security;

import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import umc.study.domain.Users;
import umc.study.repository.UserRepository.UserRepository;

@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {

private final UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users user = userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("ํ•ด๋‹น ์ด๋ฉ”์ผ์„ ๊ฐ€์ง„ ์œ ์ €๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: " + username));

return org.springframework.security.core.userdetails.User
.withUsername(user.getEmail())
.password(user.getPassword())
.roles(user.getRole().name())
.build();
}

}
53 changes: 53 additions & 0 deletions hajin/src/main/java/umc/study/config/security/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package umc.study.config.security;

import lombok.RequiredArgsConstructor;
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.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import umc.study.config.security.jwt.JwtAuthenticationFilter;
import umc.study.config.security.jwt.JwtTokenProvider;

// ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์ž‘์„ฑํ•œ ๋ณด์•ˆ ์„ค์ •์ด Spring Security์˜ ๊ธฐ๋ณธ ์„ค์ •๋ณด๋‹ค ์šฐ์„  ์ ์šฉ
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

private final JwtTokenProvider jwtTokenProvider;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/", "/home", "/signup", "/css/**", "/users/**", "/members/signup", "/users/join", "/users/login", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)

.formLogin((form) -> form
.loginPage("/login")
.defaultSuccessUrl("/home", true)
.permitAll()
)
.logout((logout) -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.csrf()
.disable()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);

return http.build();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package umc.study.config.security.jwt;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
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 umc.study.config.properties.Constants;

import java.io.IOException;

@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final JwtTokenProvider jwtTokenProvider;

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

String token = resolveToken(request);

if(StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}

private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(Constants.AUTH_HEADER);
if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(Constants.TOKEN_PREFIX)) {
return bearerToken.substring(Constants.TOKEN_PREFIX.length());
}
return null;
}
}
Loading