Skip to content

Commit

Permalink
refactor(SecurityFilterChainConfig): 람다 사용으로 관련 있는 설정들에 대한 그룹화
Browse files Browse the repository at this point in the history
  • Loading branch information
bbang-jun committed Aug 14, 2024
1 parent 59659fe commit 54942e9
Show file tree
Hide file tree
Showing 21 changed files with 133 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.myadd.myadd.post.crud.dto.PostBackDto;
import com.myadd.myadd.response.BaseResponse;
import com.myadd.myadd.response.BaseResponseStatus;
import com.myadd.myadd.user.security.service.PrincipalDetails;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,16 @@
import com.myadd.myadd.post.domain.PostEntity;
import com.myadd.myadd.response.BaseResponse;
import com.myadd.myadd.response.BaseResponseStatus;
import com.myadd.myadd.user.security.service.PrincipalDetails;
import com.myadd.myadd.user.security.service.CustomUserDetails;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import javax.validation.Valid;
import java.io.IOException;

@RestController
Expand All @@ -37,7 +35,7 @@ public BaseResponse<PostBackDto> create(@RequestPart PostBackDto post, @ModelAtt
if(authentication == null)
return new BaseResponse<>(BaseResponseStatus.FAILED_NOT_AUTHENTICATION);

Long id = ((PrincipalDetails) authentication.getPrincipal()).getId();
Long id = ((CustomUserDetails) authentication.getPrincipal()).getId();

log.info(post.toString());
String S3FileName = fileUploadService.upload(image);
Expand Down Expand Up @@ -68,7 +66,7 @@ public BaseResponse<PostBackDto> update(@RequestParam("postId") Long postId,@Req
if(authentication == null)
return new BaseResponse<>(BaseResponseStatus.FAILED_NOT_AUTHENTICATION);

Long id = ((PrincipalDetails) authentication.getPrincipal()).getId();
Long id = ((CustomUserDetails) authentication.getPrincipal()).getId();

PostEntity postEntity = postCrudService.modifyPost(postId, post, image, id);
if(postEntity == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.myadd.myadd.post.search.controller;

import com.fasterxml.jackson.databind.ser.Serializers;
import com.myadd.myadd.post.crud.dto.PostBackDto;
import com.myadd.myadd.post.search.dto.PostSearchBackDto;
import com.myadd.myadd.post.search.dto.PostSearchFrontDto;
import com.myadd.myadd.post.search.service.PostSearchService;
import com.myadd.myadd.response.BaseResponse;
import com.myadd.myadd.response.BaseResponseStatus;
import com.myadd.myadd.user.security.service.PrincipalDetails;
import com.myadd.myadd.user.security.service.CustomUserDetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
Expand All @@ -28,7 +27,7 @@ public PostSearchController(PostSearchService postSearchService){
public Long getAuthentication(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication == null) return null;
Long userId = ((PrincipalDetails) authentication.getPrincipal()).getId(); // UserDetailsImpl은 사용자의 상세 정보를 구현한 클래스
Long userId = ((CustomUserDetails) authentication.getPrincipal()).getId(); // UserDetailsImpl은 사용자의 상세 정보를 구현한 클래스

return userId;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package com.myadd.myadd.user.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.myadd.myadd.fileUpload.service.FileUploadService;
import com.myadd.myadd.response.BaseResponse;
import com.myadd.myadd.response.BaseResponseStatus;
import com.myadd.myadd.user.domain.dto.ProfileChangeRequestDto;
import com.myadd.myadd.user.domain.dto.UserProfileDto;
import com.myadd.myadd.user.domain.entity.UserEntity;
import com.myadd.myadd.user.security.service.PrincipalDetails;
import com.myadd.myadd.user.security.service.CustomUserDetails;
import com.myadd.myadd.user.service.ChangeProfileService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -47,8 +43,8 @@ public BaseResponse<UserProfileDto> changeProfile(
if(authentication == null)
return new BaseResponse<>(BaseResponseStatus.FAILED_NOT_AUTHENTICATION);

String email = ((PrincipalDetails)authentication.getPrincipal()).getEmail(); // 이메일 또는 사용자명
Long id = ((PrincipalDetails) authentication.getPrincipal()).getId(); // UserDetailsImpl은 사용자의 상세 정보를 구현한 클래스
String email = ((CustomUserDetails)authentication.getPrincipal()).getEmail(); // 이메일 또는 사용자명
Long id = ((CustomUserDetails) authentication.getPrincipal()).getId(); // UserDetailsImpl은 사용자의 상세 정보를 구현한 클래스

userProfileDto = changeProfileService.findUser(id, email);

Expand Down Expand Up @@ -80,8 +76,8 @@ public BaseResponse<UserProfileDto> getMyProfile(){
if(authentication == null)
return new BaseResponse<>(BaseResponseStatus.FAILED_NOT_AUTHENTICATION);

String email = ((PrincipalDetails)authentication.getPrincipal()).getEmail(); // 이메일 또는 사용자명
Long id = ((PrincipalDetails) authentication.getPrincipal()).getId();
String email = ((CustomUserDetails)authentication.getPrincipal()).getEmail(); // 이메일 또는 사용자명
Long id = ((CustomUserDetails) authentication.getPrincipal()).getId();

userProfileDto = changeProfileService.findUser(id, email);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
import com.myadd.myadd.response.BaseResponseStatus;
import com.myadd.myadd.user.domain.dto.EmailRequestDto;
import com.myadd.myadd.user.domain.entity.UserEntity;
import com.myadd.myadd.user.domain.usertype.UserTypeEnum;
import com.myadd.myadd.user.security.usertype.UserTypeEnum;
import com.myadd.myadd.user.domain.dto.UserDto;
import com.myadd.myadd.user.security.service.PrincipalDetails;
import com.myadd.myadd.user.security.service.CustomUserDetails;
import com.myadd.myadd.user.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

// 로그인은 spring security에서 처리해주므로 별도로 controller를 구현할 필요 없음
Expand Down Expand Up @@ -69,8 +68,8 @@ public BaseResponse<UserDto> deleteUser() {
if(authentication == null)
return new BaseResponse<>(BaseResponseStatus.FAILED_NOT_AUTHENTICATION);

String email = ((PrincipalDetails)authentication.getPrincipal()).getEmail(); // 이메일 또는 사용자명
Long id = ((PrincipalDetails) authentication.getPrincipal()).getId(); // UserDetailsImpl은 사용자의 상세 정보를 구현한 클래스
String email = ((CustomUserDetails)authentication.getPrincipal()).getEmail(); // 이메일 또는 사용자명
Long id = ((CustomUserDetails) authentication.getPrincipal()).getId(); // UserDetailsImpl은 사용자의 상세 정보를 구현한 클래스

if(userService.findByEmail(email) == null || !userService.deleteUser(id, email))
return new BaseResponse<>(BaseResponseStatus.FAILED_ALREADY_DELETE_USER);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/myadd/myadd/user/domain/dto/UserDto.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.myadd.myadd.user.domain.dto;

import com.myadd.myadd.user.domain.usertype.UserTypeEnum;
import com.myadd.myadd.user.security.usertype.UserTypeEnum;
import com.myadd.myadd.user.domain.entity.UserEntity;
import lombok.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import com.myadd.myadd.post.domain.PostEntity;
import com.myadd.myadd.user.domain.dto.UserDto;
import com.myadd.myadd.user.domain.usertype.UserTypeEnum;
import com.myadd.myadd.user.security.usertype.UserTypeEnum;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.fasterxml.jackson.databind.JsonNode;

import com.myadd.myadd.user.domain.entity.UserEntity;
import com.myadd.myadd.user.domain.usertype.UserTypeEnum;
import com.myadd.myadd.user.security.usertype.UserTypeEnum;
import com.myadd.myadd.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.core.env.Environment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.myadd.myadd.user.domain.entity.UserEntity;
import com.myadd.myadd.user.security.service.PrincipalDetails;
import com.myadd.myadd.user.security.service.CustomUserDetails;
import com.myadd.myadd.user.non_security.kakao.service.KakaoLoginService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -62,8 +62,8 @@ public String kakaoCallback(@RequestParam String code, HttpServletRequest reques
@PostMapping("/users/my-info/delete/kakao-user")
public @ResponseBody String kakaoWithdrawal() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String email = ((PrincipalDetails)authentication.getPrincipal()).getEmail(); // 이메일 또는 사용자명
Long id = ((PrincipalDetails) authentication.getPrincipal()).getId(); // UserDetailsImpl은 사용자의 상세 정보를 구현한 클래스
String email = ((CustomUserDetails)authentication.getPrincipal()).getEmail(); // 이메일 또는 사용자명
Long id = ((CustomUserDetails) authentication.getPrincipal()).getId(); // UserDetailsImpl은 사용자의 상세 정보를 구현한 클래스
log.info("email = {}", email);
log.info("id = {}", id);
return kakaoLoginService.kakaoWithdrawal(id, email);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.myadd.myadd.user.domain.entity.UserEntity;
import com.myadd.myadd.user.domain.usertype.UserTypeEnum;
import com.myadd.myadd.user.security.usertype.UserTypeEnum;
import com.myadd.myadd.user.repository.UserRepository;
import com.myadd.myadd.user.non_security.kakao.OAuthToken;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
@Configuration
public class PasswordEncoder {
// BCrypt 알고리즘을 사용하는 PasswordEncoder 빈 생성
// 단순히 Bean이 싱글톤으로 관리되기 때문이라고 할 수 있겠다.
// BCryptPasswordEncoder의 용도 특성상 Bean으로 등록하지 않으면 객체가 무분별하게 생성될 수 있음.
// 이런 부담을 줄이고자 Bean으로 등록해서 사용.
@Bean
public BCryptPasswordEncoder encodePwd() {
return new BCryptPasswordEncoder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import com.myadd.myadd.response.BaseResponse;
import com.myadd.myadd.response.BaseResponseStatus;
import com.myadd.myadd.user.security.handler.CustomLogoutSuccessHandler;
import com.myadd.myadd.user.security.service.PrincipalOauth2UserService;
import com.myadd.myadd.user.security.service.AuthenticationProvider;
import com.myadd.myadd.user.security.handler.CustomAuthenticationFailureHandler;
import com.myadd.myadd.user.security.handler.CustomAuthenticationSuccessHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
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.core.Authentication;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
Expand All @@ -20,42 +22,50 @@
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// 어떤 요청에 대한 엔드 포인트에 도달하기 전에 요청을 가로채서 어떤 작업을 수행하는 컴포넌트를 “서블릿 필터(Servlet Filter)”라고 함. (보호나 인증 등에 대한 처리를 전처리하기 위해 사용)
// 이러한 필터들이 Chain, 즉 사슬처럼 엮인 것을 Filter Chain이라고 부름.
// Spring Security: SecurityFilterChain
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity // // 스프링 시큐리티 필터가 스프링 필터체인에 등록이 됨
public class SecurityFilterChainConfig {

@Autowired
private PrincipalOauth2UserService principalOauth2UserService;
private AuthenticationProvider authenticationProvider;

// 관련 있는 설정들은 람다를 사용해서 그룹화
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.httpBasic(); // HTTP Basic 인증 활성화
httpSecurity.csrf().disable(); // CSRF 보호 비활성화 (주의: 프로덕션 환경에서는 권장되지 않음)
httpSecurity.authorizeHttpRequests()
.antMatchers("/posts/**").authenticated() // /posts/** 경로는 인증된 사용자만 접근 가능
.antMatchers("/users/my-info/**").authenticated() // /users/my-info/** 경로는 인증된 사용자만 접근 가능
.antMatchers("/users/change-password/**").authenticated() // /users/change-password/** 경로는 인증된 사용자만 접근 가능
.anyRequest().permitAll() // 그 외 모든 요청은 누구나 접근 가능
httpSecurity
.httpBasic() // HTTP Basic 인증 활성화
.and()
.formLogin() // 폼 로그인 설정
// 시큐리티가 /users/login/email 경로로 로직을 만들어서 시큐리티 로그인을 처리함(로그인 처리 URL 설정)
// 결과적으로 로그인을 위해 UserController에 따로 "/users/login/email"을 구현하지 않아도 괜찮다.
// 매우 편리하지만, 이 과정에서 UserDetails가 필요하기에 따로 이를 구현한 클래스를 만들어줘야한다.
// 이 로그인 과정에서 필요한 것이 있기 때문에 auth 패키지를 파서 PrincipalDetails 을 만들어줘야한다.
.loginProcessingUrl("/users/login/email")
.successHandler(new CustomAuthenticationSuccessHandler()) // 로그인 성공 핸들러 설정
.failureHandler(new CustomAuthenticationFailureHandler()) // 로그인 실패 핸들러 설정
.usernameParameter("email") // 사용자 이름 파라미터를 'email'로 설정
.and()
.logout() // 로그아웃 설정
.logoutUrl("/users/my-info/logout")
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.and()
.oauth2Login() // OAuth2 로그인 설정
.userInfoEndpoint()
// OAuth2 사용자 서비스 설정
// 로그인한 사용자에게 받은 정보가 principalOauth2UserService의 매개변수인 userRequest로 return
.userService(principalOauth2UserService);
.csrf().disable() // CSRF 보호 비활성화 (주의: 프로덕션 환경에서는 권장되지 않음)
.authorizeHttpRequests(auth -> auth // 인증 요청 설정
// /posts/**, /users/my-info/**, /users/change-password/** 경로는 인증된 사용자만 접근 가능
.antMatchers("/posts/**", "/users/my-info/**", "/users/change-password/**").authenticated()
// 그 외 모든 요청은 누구나 접근 가능
.anyRequest().permitAll()
)
.formLogin(form -> form // 폼 로그인 설정
// 시큐리티가 /users/login/email 경로로 로직을 만들어서 시큐리티 로그인을 처리함(이메일 방식 로그인 처리 URL 설정)
// 로그인을 위해 UserController에 따로 "/users/login/email"을 구현하지 않아도 됨.
// 이 과정에서 UserDetails가 필요하기에 따로 이를 구현한 클래스를 만들어줘야한다.
// 이 로그인 과정에서 필요한 것이 있기 때문에 auth 패키지를 파서 PrincipalDetails 을 만들어줘야한다.
.loginProcessingUrl("/users/login/email")
.successHandler(new CustomAuthenticationSuccessHandler()) // 로그인 성공 핸들러 설정
.failureHandler(new CustomAuthenticationFailureHandler()) // 로그인 실패 핸들러 설정
.usernameParameter("email") // 사용자 이름 파라미터를 'email'로 설정
)
.logout(logout -> logout // 로그아웃 설정
.logoutUrl("/users/my-info/logout")
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
)
.oauth2Login(oauth2 -> oauth2 // OAuth2 로그인 설정
.userInfoEndpoint()
// OAuth2로 로그인한 사용자에 대해서는 authenticationProvider에 구현한대로 처리함.
// 로그인한 사용자에게 받은 정보가 AuthenticationProvider의 매개변수인 userRequest로 return
.userService(authenticationProvider)
) ;

return httpSecurity.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// Spring Security: Authentication
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
Expand All @@ -33,4 +34,22 @@ public void emailLoginSuccess(HttpServletResponse response) throws IOException {
response.setCharacterEncoding("UTF-8");
response.getWriter().write(jsonResponse);
}

public String oauth2LoginSuccess(HttpServletResponse response, String provider) throws IOException {
BaseResponse<BaseResponseStatus> successResponse = null;

if(provider.equals("google"))
successResponse = new BaseResponse<>(BaseResponseStatus.SUCCESS_GOOGLE_LOGIN);
else if(provider.equals("kakao"))
successResponse = new BaseResponse<>(BaseResponseStatus.SUCCESS_KAKAO_LOGIN);

String jsonResponse = new ObjectMapper().writeValueAsString(successResponse);

response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(jsonResponse);

return jsonResponse;
}
}
Loading

0 comments on commit 54942e9

Please sign in to comment.