-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Spring Security 초기 설정 (+ Test case 에러 관련) (#22)
* chore: external-api 모듈 spring boot starter security 의존성 주입 * chore: security config 설정 * chore: method security config 설정 * fix: 기존 api 인가 권한 is-anonymous로 제한 * fix: security config 인증, 인가 예외 필터 제거 (로그인 작업 시 추가) * fix: user sync helper oauth 반환 수정 * test: user sync helper 메서드 반환 타입 수정 * test: username 반환 검증 추가 * fix: pennyway infra application @spring boot application 어노테이션 제거 * test: 성공 응답 객체 code 값 2000으로 수정 * chore: spring security test 의존성 주입 * test: auth controller 성공 응답 set cookie 헤더 존재 여부 판단으로 수정 * chore: sub project test 블럭 추가 * feat: security user details & service 정의 * chore: local 환경 내 logging level 정보 추가 * fix: user sync helper transaction 제거
- Loading branch information
1 parent
e09ab66
commit 39f97e9
Showing
11 changed files
with
552 additions
and
340 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
.../src/main/java/kr/co/pennyway/api/common/security/authentication/SecurityUserDetails.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package kr.co.pennyway.api.common.security.authentication; | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnore; | ||
import kr.co.pennyway.domain.domains.user.domain.User; | ||
import kr.co.pennyway.domain.domains.user.type.Role; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collection; | ||
|
||
@Getter | ||
public class SecurityUserDetails implements UserDetails { | ||
private final Long userId; | ||
private final String username; | ||
private final Collection<? extends GrantedAuthority> authorities; | ||
private final boolean accountNonLocked; | ||
|
||
@JsonIgnore | ||
private boolean enabled; | ||
@JsonIgnore | ||
private String password; | ||
@JsonIgnore | ||
private boolean credentialsNonExpired; | ||
@JsonIgnore | ||
private boolean accountNonExpired; | ||
|
||
@Builder | ||
private SecurityUserDetails(Long userId, String username, Collection<? extends GrantedAuthority> authorities, boolean accountNonLocked) { | ||
this.userId = userId; | ||
this.username = username; | ||
this.authorities = authorities; | ||
this.accountNonLocked = accountNonLocked; | ||
} | ||
|
||
public static UserDetails from(User user) { | ||
return SecurityUserDetails.builder() | ||
.userId(user.getId()) | ||
.username(user.getUsername()) | ||
.authorities(Arrays.stream(Role.values()) | ||
.filter(roleType -> roleType == user.getRole()) | ||
.map(roleType -> (GrantedAuthority) roleType::getType) | ||
.toList()) | ||
.accountNonLocked(user.getLocked()) | ||
.build(); | ||
} | ||
|
||
@Override | ||
public Collection<? extends GrantedAuthority> getAuthorities() { | ||
return authorities; | ||
} | ||
|
||
@Override | ||
public String getPassword() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public String getUsername() { | ||
return username; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonExpired() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonLocked() { | ||
return accountNonLocked; | ||
} | ||
|
||
@Override | ||
public boolean isCredentialsNonExpired() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public boolean isEnabled() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...rc/main/java/kr/co/pennyway/api/common/security/authentication/UserDetailServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package kr.co.pennyway.api.common.security.authentication; | ||
|
||
import kr.co.pennyway.domain.domains.user.service.UserService; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.cache.annotation.Cacheable; | ||
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; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class UserDetailServiceImpl implements UserDetailsService { | ||
private final UserService userService; | ||
|
||
@Override | ||
@Cacheable(value = "securityUser", key = "#userId", unless = "#result == null", cacheManager = "securityUserCacheManager") | ||
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException { | ||
try { | ||
return SecurityUserDetails.from(userService.readUser(Long.parseLong(userId))); | ||
} catch (Exception e) { | ||
return null; | ||
} | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
...p-external-api/src/main/java/kr/co/pennyway/api/config/security/MethodSecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package kr.co.pennyway.api.config.security; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; | ||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; | ||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; | ||
|
||
@Configuration | ||
@EnableMethodSecurity(securedEnabled = true) | ||
public class MethodSecurityConfig { | ||
@Bean | ||
protected MethodSecurityExpressionHandler createExpressionHandler() { | ||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); | ||
return expressionHandler; | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
...way-app-external-api/src/main/java/kr/co/pennyway/api/config/security/SecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package kr.co.pennyway.api.config.security; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.boot.autoconfigure.security.servlet.PathRequest; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.http.HttpMethod; | ||
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.configurers.AbstractHttpConfigurer; | ||
import org.springframework.security.config.http.SessionCreationPolicy; | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
import org.springframework.web.cors.CorsConfiguration; | ||
import org.springframework.web.cors.CorsConfigurationSource; | ||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | ||
|
||
import java.util.Arrays; | ||
|
||
@Configuration | ||
@EnableWebSecurity | ||
@RequiredArgsConstructor | ||
public class SecurityConfig { | ||
private static final String[] publicReadOnlyPublicEndpoints = { | ||
"/favicon.ico", | ||
// Swagger | ||
"/api-docs/**", "/v3/api-docs/**", "/swagger-ui/**", "/swagger", | ||
}; | ||
|
||
@Bean | ||
public BCryptPasswordEncoder bCryptPasswordEncoder() { | ||
return new BCryptPasswordEncoder(); | ||
} | ||
|
||
@Bean | ||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
http.csrf(AbstractHttpConfigurer::disable) | ||
.httpBasic(AbstractHttpConfigurer::disable) | ||
.cors((cors) -> cors.configurationSource(corsConfigurationSource())) | ||
.formLogin(AbstractHttpConfigurer::disable) | ||
.logout(AbstractHttpConfigurer::disable) | ||
.sessionManagement((sessionManagement) -> | ||
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) | ||
.authorizeHttpRequests( | ||
auth -> auth.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() | ||
.requestMatchers(HttpMethod.OPTIONS, "*").permitAll() | ||
.requestMatchers(HttpMethod.GET, publicReadOnlyPublicEndpoints).permitAll() | ||
.anyRequest().permitAll() | ||
); | ||
return http.build(); | ||
} | ||
|
||
// TODO: dev, test, prod 환경이 정해지면 수정 필요. | ||
@Bean | ||
CorsConfigurationSource corsConfigurationSource() { | ||
CorsConfiguration configuration = new CorsConfiguration(); | ||
configuration.addAllowedOrigin("http://localhost:3000"); | ||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE")); | ||
configuration.setAllowCredentials(true); | ||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | ||
source.registerCorsConfiguration("/**", configuration); | ||
return source; | ||
} | ||
} |
Oops, something went wrong.