Skip to content

Commit

Permalink
#37 refactor. Security 모듈을 API 모듈로 이동
Browse files Browse the repository at this point in the history
  • Loading branch information
electronyoon committed Feb 15, 2025
1 parent ee83c55 commit ca6cfd9
Show file tree
Hide file tree
Showing 63 changed files with 459 additions and 1,318 deletions.
7 changes: 0 additions & 7 deletions .env.local

This file was deleted.

11 changes: 0 additions & 11 deletions .env.prod

This file was deleted.

5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
.PHONY: local down clean localdb

local:
docker-compose --env-file .env.local -f docker-compose.yml -f docker-compose.local.yml build --no-cache
docker-compose --env-file .env.local -f docker-compose.yml -f docker-compose.local.yml build
docker-compose --env-file .env.local -f docker-compose.yml -f docker-compose.local.yml up

# prod:
# docker-compose --env-file .env.prod -f docker-compose.yml -f docker-compose.prod.yml up

down:
docker-compose -f docker-compose.yml -f docker-compose.local.yml down -v

Expand Down
18 changes: 4 additions & 14 deletions api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,10 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}

processResources {
from("${rootProject.projectDir}") {
include "*.env.local"
}
}

bootRun {
args = ["--spring.config.additional-location=optional:${rootProject.projectDir}/config/"]
// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
implementation 'org.springdoc:springdoc-openapi-starter-common:2.7.0'
}

bootJar {
Expand All @@ -25,8 +19,4 @@ bootJar {

tasks.named('jar') {
enabled = false
}

test {
systemProperty "spring.config.additional-location", "optional:${rootProject.projectDir}/config/"
}
}
7 changes: 0 additions & 7 deletions api/src/main/java/com/zipsoon/api/ApiApplication.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
package com.zipsoon.api;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.zipsoon")
@MapperScan(basePackages = {
"com.zipsoon.api.estate.mapper",
"com.zipsoon.api.user.mapper",
"com.zipsoon.api.user.repository",
"com.zipsoon.common.mapper"
})
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.zipsoon.api.security.dto;
package com.zipsoon.api.auth.dto;

import java.time.LocalDateTime;

Expand Down
8 changes: 8 additions & 0 deletions api/src/main/java/com/zipsoon/api/auth/dto/LoginRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.zipsoon.api.auth.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

public record LoginRequest(
@NotBlank @Email String email
) {}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.zipsoon.api.user.dto;
package com.zipsoon.api.auth.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

public record UserSignupRequest(
public record SignupRequest(
@NotBlank @Email String email,
@NotBlank String password,
@NotBlank String name
) {}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.zipsoon.api.security.model;
package com.zipsoon.api.auth.model;

public enum AuthProvider {
LOCAL,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
package com.zipsoon.api.security.model;
package com.zipsoon.api.auth.model;

import com.zipsoon.api.user.domain.User;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;

@Getter
public class UserPrincipal implements OAuth2User, UserDetails {
public class UserPrincipal implements OAuth2User {
private final Long id;
private final String email;
private final String password;
private final Collection<? extends GrantedAuthority> authorities;
@Setter
private Map<String, Object> attributes;

private UserPrincipal(Long id, String email, String password, Collection<? extends GrantedAuthority> authorities) {
private UserPrincipal(Long id, String email, Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.email = email;
this.password = password;
this.authorities = authorities;
}

public static UserPrincipal create(User user) {
return new UserPrincipal(
user.getId(),
user.getEmail(),
user.getPassword(),
Collections.singletonList(new SimpleGrantedAuthority(user.getRole().name()))
);
}
Expand All @@ -44,42 +40,13 @@ public static UserPrincipal create(User user, Map<String, Object> attributes) {
}

@Override
public String getPassword() {
return password;
}

@Override
public String getUsername() {
return email;
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
public String getName() {
return id.toString();
}

@Override
public Map<String, Object> getAttributes() {
return attributes;
}

@Override
public String getName() {
return String.valueOf(id);
}
}
68 changes: 68 additions & 0 deletions api/src/main/java/com/zipsoon/api/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.zipsoon.api.config;

import com.zipsoon.api.security.filter.JwtAuthenticationFilter;
import com.zipsoon.api.security.handler.JwtAuthenticationEntryPoint;
import com.zipsoon.api.security.jwt.JwtProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@Import(MyBatisConfig.class)
public class SecurityConfig {

private final JwtProvider jwtTokenProvider;

@Bean
public WebSecurityCustomizer configure() {
return web -> web.ignoring()
.requestMatchers(
"/swagger-ui.html",
"/swagger-ui/**",
"/swagger-resources/**",
"/v3/api-docs/**",
"/webjars/**",
"/error"
);
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)

.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(
jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class
)
.exceptionHandling(ex ->
ex.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
)
.build();
}

@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter(jwtTokenProvider);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@

@Mapper
public interface EstateMapper {
default int getWgs84Srid() {
return 4326; // WGS84 좌표계 SRID 값
}

List<EstateSnapshot> findAllInViewport(
@Param("viewport") ViewportRequest viewport,
@Param("limit") int limit
@Param("limit") int limit,
@Param("srid") int srid
);

Optional<EstateSnapshot> findById(@Param("id") Long id);
Expand Down
60 changes: 34 additions & 26 deletions api/src/main/java/com/zipsoon/api/estate/service/EstateService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,48 @@
import com.zipsoon.api.estate.dto.EstateResponse;
import com.zipsoon.api.estate.dto.ViewportRequest;
import com.zipsoon.api.estate.mapper.EstateMapper;
import com.zipsoon.common.exception.domain.ResourceNotFoundException;
import com.zipsoon.api.exception.custom.ServiceException;
import com.zipsoon.api.exception.model.ErrorCode;
import com.zipsoon.common.domain.EstateSnapshot;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

import static com.zipsoon.common.exception.ErrorCode.ESTATE_NOT_FOUND;
import static com.zipsoon.api.exception.model.ErrorCode.ESTATE_NOT_FOUND;

@Service
@RequiredArgsConstructor
public class EstateService {
private final EstateMapper estateMapper;
private static final int MAX_RESULTS_PER_ZOOM = 1000;

@Transactional(readOnly = true)
public List<EstateResponse> findEstatesInViewport(ViewportRequest request) {
int limit = calculateLimit(request.zoom());
return estateMapper.findAllInViewport(request, limit).stream()
.map(EstateResponse::from)
.collect(Collectors.toList());
}

@Transactional(readOnly = true)
public EstateDetailResponse findEstateDetail(Long id) {
return estateMapper.findById(id)
.map(EstateDetailResponse::from)
.orElseThrow(() -> new ResourceNotFoundException(ESTATE_NOT_FOUND));
}

private int calculateLimit(int zoom) {
if (zoom <= 8) return 100;
if (zoom <= 14) return 500;
return MAX_RESULTS_PER_ZOOM;
}
private final EstateMapper estateMapper;
private static final int MAX_RESULTS_PER_ZOOM = 1000;
private static final int SRID = 4326; // WGS84 좌표계 SRID 값

@Transactional(readOnly = true)
public List<EstateResponse> findEstatesInViewport(ViewportRequest request) {
int limit = calculateLimit(request.zoom());
List<EstateSnapshot> estates = estateMapper.findAllInViewport(request, limit, SRID);

if (estates.isEmpty()) {
throw new ServiceException(ErrorCode.ESTATE_NOT_FOUND, "해당 지역에 매물이 없습니다.");
}

return estates.stream()
.map(EstateResponse::from)
.toList();
}

@Transactional(readOnly = true)
public EstateDetailResponse findEstateDetail(Long id) {
return estateMapper.findById(id)
.map(EstateDetailResponse::from)
.orElseThrow(() -> new ServiceException(ESTATE_NOT_FOUND));
}

private int calculateLimit(int zoom) {
if (zoom <= 8) return 100;
if (zoom <= 14) return 500;
return MAX_RESULTS_PER_ZOOM;
}
}
Loading

0 comments on commit ca6cfd9

Please sign in to comment.