Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,45 +32,24 @@ public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// CSRF ๋น„ํ™œ์„ฑํ™” (REST API ์Šคํƒ€์ผ์—์„œ๋Š” ๋ณดํ†ต ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ)
.csrf(AbstractHttpConfigurer::disable)
// ํผ ๋กœ๊ทธ์ธ ๋น„ํ™œ์„ฑํ™” (์†Œ์…œ ๋กœ๊ทธ์ธ๋งŒ ์‚ฌ์šฉ)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.formLogin(AbstractHttpConfigurer::disable)
// HTTP ๊ธฐ๋ณธ ์ธ์ฆ ๋น„ํ™œ์„ฑํ™”
.httpBasic(AbstractHttpConfigurer::disable)

// CORS ์„ค์ •
.cors(corsCustomizer -> corsCustomizer.configurationSource(request -> {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:8080")); // React ํ”„๋ก ํŠธ์—”๋“œ URL
configuration.setAllowedMethods(Collections.singletonList("*")); // ๋ชจ๋“  HTTP ๋ฉ”์„œ๋“œ ํ—ˆ์šฉ
configuration.setAllowCredentials(true); // ์ฟ ํ‚ค ํ—ˆ์šฉ
configuration.setAllowedHeaders(Collections.singletonList("*")); // ๋ชจ๋“  ํ—ค๋” ํ—ˆ์šฉ
configuration.setMaxAge(3600L); // CORS ์บ์‹ฑ ์‹œ๊ฐ„ ์„ค์ •
return configuration;
}))

// ์†Œ์…œ ๋กœ๊ทธ์ธ ์„ค์ •
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService)) // ์‚ฌ์šฉ์ž ์ •๋ณด ์ฒ˜๋ฆฌ
.successHandler(customSuccessHandler)) // ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ์ฒ˜๋ฆฌ

// JWT ์ธ์ฆ ํ•„ํ„ฐ ์ถ”๊ฐ€
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
// ๋กœ๊ทธ์•„์›ƒ ํ•„ํ„ฐ ์ถ”๊ฐ€
.addFilterBefore(customLogoutFilter, LogoutFilter.class)

// ๊ถŒํ•œ๋ณ„ ์š”์ฒญ ์ฒ˜๋ฆฌ
.authorizeHttpRequests(auth -> auth

// ํ† ํฐ ์žฌ๋ฐœ๊ธ‰, ๋ฃจํŠธ ํŽ˜์ด์ง€, ๋กœ๊ทธ์ธ, OAuth2 ์š”์ฒญ์€ ์ธ์ฆ ์—†์ด ์ ‘๊ทผ ๊ฐ€๋Šฅ
.requestMatchers("/reissue", "/", "/login", "/oauth2/**").permitAll()

// ๋‚˜๋จธ์ง€ ์š”์ฒญ์€ ์ธ์ฆ ํ•„์š”
.requestMatchers("/login", "/oauth2/**", "/auth/**", "/reissue").permitAll()
// "/" ๊ฒฝ๋กœ ์ œ์™ธ
.anyRequest().authenticated())

// ์„ธ์…˜ ์ •์ฑ…: Stateless
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo ->
userInfo.userService(customOAuth2UserService))
.successHandler(customSuccessHandler))
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) // JWT ํ•„ํ„ฐ ์ถ”๊ฐ€
.addFilterBefore(customLogoutFilter, LogoutFilter.class);


return http.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.mycom.backenddaengplace.auth.interceptor.AuthorizationInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@RequiredArgsConstructor
Expand All @@ -19,9 +18,9 @@ public void addInterceptors(InterceptorRegistry registry) {
* // (...)
* .anyRequest().authenticated()) // ๊ทธ ์™ธ์˜ ์š”์ฒญ์€ ์ธ์ฆ ํ•„์š”
*/
registry.addInterceptor(authorizationInterceptor) // ์ธํ„ฐ์…‰ํ„ฐ ๋“ฑ๋ก (์—ฌ๋Ÿฌ ์ธํ„ฐ์…‰ํ„ฐ ๋“ฑ๋ก์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ, ํ•„์š” ์‹œ ์ˆœ์„œ ์กฐ์ •๋„ ๊ฐ€๋Šฅ)
.addPathPatterns("/**") // ์ธํ„ฐ์…‰ํ„ฐ ์ ์šฉ ๊ฒฝ๋กœ
.excludePathPatterns("/"); // ์ธํ„ฐ์…‰ํ„ฐ ์ œ์™ธ ๊ฒฝ๋กœ
registry.addInterceptor(authorizationInterceptor)
.addPathPatterns("/**") // ๋ชจ๋“  ๊ฒฝ๋กœ์— ์ ์šฉ, '/' ์ œ์™ธ ํ•„์š” ์—†์Œ
.excludePathPatterns("/error", "/logout", "/api/login"); // ์—๋Ÿฌ ํŽ˜์ด์ง€, ๋กœ๊ทธ์•„์›ƒ, ๋กœ๊ทธ์ธ API๋Š” ์ œ์™ธ
}

}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,49 +1,48 @@
package com.mycom.backenddaengplace.auth.dto;


import com.mycom.backenddaengplace.member.domain.Member;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.user.OAuth2User;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

@Getter
public class CustomOAuth2User implements OAuth2User {

private final UserDTO userDTO;
private final Member member; // Member ์ถ”๊ฐ€

public CustomOAuth2User(UserDTO userDTO) {

public CustomOAuth2User(UserDTO userDTO, Member member) { // ์ƒ์„ฑ์ž ์ˆ˜์ •
this.userDTO = userDTO;
this.member = member;
}

@Override
public Map<String, Object> getAttributes() {

return null;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collection = new ArrayList<>();
collection.add(new GrantedAuthority() {
@Override
public String getAuthority() {
// ๊ถŒํ•œ์ด ์—†์œผ๋ฉด ๊ธฐ๋ณธ ROLE_USER ๋ฐ˜ํ™˜
return "ROLE_USER";
}
});
collection.add(() -> "ROLE_USER"); // ๋žŒ๋‹ค์‹์œผ๋กœ ๋‹จ์ˆœํ™”
return collection;
}

@Override
public String getName() {
// userDTO.getName() ๋Œ€์‹  nickname์„ ๋ฐ˜ํ™˜
return userDTO.getNickname() != null ? userDTO.getNickname() : "Unknown User";
}

public String getUsername() {
// userDTO.getUsername() ๋Œ€์‹  provider + providerId ๋ฐ˜ํ™˜
return userDTO.getProvider() + "_" + userDTO.getProviderId();
}

// Member ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์†Œ๋“œ ์ถ”๊ฐ€
public Member getMember() {
return member;
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package com.mycom.backenddaengplace.auth.dto;

import com.mycom.backenddaengplace.member.domain.Member;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;

@Getter
@RequiredArgsConstructor
public class CustomUserDetails implements UserDetails {

private final Member Member;
private final Member member;

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Expand All @@ -35,7 +37,7 @@ public String getPassword() {
@Override
public String getUsername() {
// ์‚ฌ์šฉ์ž ์ด๋ฆ„ ๋Œ€์‹  providerId๋ฅผ ๊ณ ์œ  ์‹๋ณ„์ž๋กœ ๋ฐ˜ํ™˜
return Member.getProvider() + "_" + Member.getProviderId();
return member.getProvider() + "_" + member.getProviderId();
}

@Override
Expand Down
86 changes: 38 additions & 48 deletions src/main/java/com/mycom/backenddaengplace/auth/jwt/JWTFilter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.mycom.backenddaengplace.auth.jwt;

import com.mycom.backenddaengplace.auth.dto.CustomOAuth2User;
import com.mycom.backenddaengplace.auth.dto.CustomUserDetails;
import com.mycom.backenddaengplace.auth.dto.UserDTO;
import com.mycom.backenddaengplace.auth.repository.AuthMemberRepository;
import com.mycom.backenddaengplace.member.domain.Member;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
Expand All @@ -25,85 +28,72 @@
public class JWTFilter extends OncePerRequestFilter {

private final JWTUtil jwtUtil;
private final AuthMemberRepository authMemberRepository; // ์ถ”๊ฐ€

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// ํ—ค๋”์—์„œ accessํ‚ค์— ๋‹ด๊ธด ํ† ํฐ์„ ๊บผ๋ƒ„
// String accessToken = request.getHeader("access");
// String accessToken = request.getHeader("Authorization");
String accessToken = request.getHeader(HttpHeaders.AUTHORIZATION);
log.info("{}{} {}{}Authorization: {}", System.lineSeparator(), request.getMethod(), request.getRequestURI(),
System.lineSeparator(), accessToken);

// ํ† ํฐ์ด ์—†๋‹ค๋ฉด ๋‹ค์Œ ํ•„ํ„ฐ๋กœ ๋„˜๊น€
if (accessToken == null) {
if (accessToken == null || !accessToken.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}

// ํ† ํฐ ๋งŒ๋ฃŒ ์—ฌ๋ถ€ ํ™•์ธ, ๋งŒ๋ฃŒ์‹œ ๋‹ค์Œ ํ•„ํ„ฐ๋กœ ๋„˜๊ธฐ์ง€ ์•Š์Œ
accessToken = accessToken.substring(7); // "Bearer " ์ œ๊ฑฐ

try {
jwtUtil.isExpired(accessToken);
} catch (ExpiredJwtException e) {

//response body
PrintWriter writer = response.getWriter();
writer.print("access token expired");

//response status code
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}

// ํ† ํฐ์ด access์ธ์ง€ ํ™•์ธ (๋ฐœ๊ธ‰์‹œ ํŽ˜์ด๋กœ๋“œ์— ๋ช…์‹œ)
String category = jwtUtil.getCategory(accessToken);

if (!category.equals("access")) {

//response body
PrintWriter writer = response.getWriter();
writer.print("invalid access token");

//response status code
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}

// username, role ๊ฐ’์„ ํš๋“
String username = jwtUtil.getUsername(accessToken);
String role = jwtUtil.getRole(accessToken);

// Member ๊ฐ์ฒด ์ƒ์„ฑ (๋นŒ๋” ํŒจํ„ด ์‚ฌ์šฉ)
Member member = Member.builder()
.provider(username)
.providerId(username)
.nickname(username)
.build();


// CustomUserDetails ์ƒ์„ฑ
/*
* TODO: ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•
* 1. User ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
* 2. JPA ์—”ํ‹ฐํ‹ฐ์— UserDetails ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
* 3. ์ปค์Šคํ…€ UserDetails ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• (์ถ”์ฒœ)
*/
// User customUserDetails = new User(Member.getEmail(), Member.getPassword(), List.of(authEntity.getRole()));
CustomUserDetails customUserDetails = new CustomUserDetails(member);

// Authentication ๊ฐ์ฒด ์ƒ์„ฑ
Authentication authToken = new UsernamePasswordAuthenticationToken(
customUserDetails,
null,
customUserDetails.getAuthorities() // ๊ธฐ๋ณธ ๊ถŒํ•œ ROLE_USER ๋ฐ˜ํ™˜
);

// SecurityContextHolder์— ์ธ์ฆ ์ •๋ณด ์„ค์ •
// SecurityContext, PersistenceContext, XxxContext: Xxx
SecurityContextHolder.getContext().setAuthentication(authToken);

// ๋‹ค์Œ ํ•„ํ„ฐ๋กœ ์š”์ฒญ ์ „๋‹ฌ
filterChain.doFilter(request, response);
// username์—์„œ provider์™€ providerId ์ถ”์ถœ
String[] parts = username.split("_");
if (parts.length == 2) {
String provider = parts[0];
String providerId = parts[1];

// DB์—์„œ Member ์กฐํšŒ String[] parts = username.split("_");
Member member = authMemberRepository.findByProviderAndProviderId(provider, providerId);
if (member != null) {
// UserDTO ์ƒ์„ฑ
UserDTO userDTO = new UserDTO();
userDTO.setProvider(provider);
userDTO.setProviderId(providerId);
userDTO.setEmail(member.getEmail());
userDTO.setNickname(member.getNickname());
userDTO.setProfileImage(member.getProfileImageUrl());

// CustomOAuth2User ์ƒ์„ฑ
CustomOAuth2User customOAuth2User = new CustomOAuth2User(userDTO, member);

// Authentication ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ SecurityContext์— ์„ค์ •
Authentication authToken = new UsernamePasswordAuthenticationToken(
customOAuth2User,
null,
customOAuth2User.getAuthorities()
);

SecurityContextHolder.getContext().setAuthentication(authToken);
}
}

filterChain.doFilter(request, response);
}
}
Loading
Loading