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
1 change: 1 addition & 0 deletions backend-board/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {
runtimeOnly 'com.mysql:mysql-connector-j'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@
public interface AuthUserRepository extends JpaRepository<AuthUser, Long> {
boolean existsByUsername(String username);

AuthUser findObjectByUsername(String username);

Optional<AuthUser> findByUsername(String username);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import com.backendboard.global.security.filter.CustomLogoutFilter;
import com.backendboard.global.security.filter.JwtFilter;
import com.backendboard.global.security.filter.LoginFilter;
import com.backendboard.global.security.handler.CustomOauth2SuccessHandler;
import com.backendboard.global.security.service.CustomOAuth2UserService;
import com.backendboard.global.security.service.CustomUserDetailsService;
import com.backendboard.global.security.service.RefreshTokenService;
import com.backendboard.global.util.JwtUtil;
Expand All @@ -27,29 +29,40 @@
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
public static final String[] ALLOWED_URLS = {
"/login",
"/join",
"/reissue",
"/swagger-ui/**",
"/swagger-resources/**",
"/v3/api-docs/**",
"/images/**",
};

private final AuthenticationConfiguration authenticationConfiguration;
private final CustomUserDetailsService customUserDetailsService;
private final JwtUtil jwtUtil;
private final RefreshTokenService refreshTokenService;
private final CustomOAuth2UserService customOAuth2UserService;
private final CustomOauth2SuccessHandler customOauth2SuccessHandler;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((auth) -> auth
.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(
userInfoEndpointConfig -> userInfoEndpointConfig.userService(customOAuth2UserService))
.successHandler(customOauth2SuccessHandler))
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/**").permitAll()
.requestMatchers("/login", "/join", "/reissue", "/swagger-ui/**", "/swagger-resources/**",
"/v3/api-docs/**")
.permitAll()
.anyRequest()
.authenticated())
.requestMatchers(ALLOWED_URLS).permitAll().anyRequest().authenticated())
.addFilterBefore(new JwtFilter(customUserDetailsService, jwtUtil), LoginFilter.class)
.addFilterAt(
new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil, refreshTokenService),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshTokenService), LogoutFilter.class)
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.backendboard.global.security.dto;

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

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.user.OAuth2User;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class CustomOAuth2User implements OAuth2User {
private final OAuth2UserDto oAuth2UserDto;

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

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<GrantedAuthority> authorities = new ArrayList<>();

authorities.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return oAuth2UserDto.role().getValue();
}
});
return authorities;
}

@Override
public String getName() {
return oAuth2UserDto.name();
}

public String getUsername() {
return oAuth2UserDto.username();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.backendboard.global.security.dto;

import java.util.Map;

public class KakaoOAuth2Response implements OAuth2Response {
private final Map<String, Object> attribute;

public KakaoOAuth2Response(Map<String, Object> attribute) {
this.attribute = attribute;
}

@Override
public String getProvider() {
return "kakao";
}

@Override
public String getProviderId() {
return attribute.get("id").toString();
}

@Override
public String getName() {
return ((Map<String, Object>)attribute.get("properties")).get("nickname").toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.backendboard.global.security.dto;

public interface OAuth2Response {
String getProvider();

String getProviderId();

String getName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.backendboard.global.security.dto;

import com.backendboard.domain.auth.entity.type.UserRole;

public record OAuth2UserDto(String username, String name, UserRole role) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.backendboard.global.security.handler;

import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import com.backendboard.global.security.dto.CustomOAuth2User;
import com.backendboard.global.util.JwtUtil;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class CustomOauth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final JwtUtil jwtUtil;

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {

CustomOAuth2User customOAuth2User = (CustomOAuth2User)authentication.getPrincipal();
String username = customOAuth2User.getUsername();

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
GrantedAuthority auth = iterator.next();
String role = auth.getAuthority();

String accessToken = jwtUtil.createAccessToken(username, role);
String refreshToken = jwtUtil.createRefreshToken(username, role);

response.addCookie(jwtUtil.createAccessCookie(accessToken));
response.addCookie(jwtUtil.createRefreshCookie(refreshToken));

response.sendRedirect("http://localhost:80/");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.backendboard.global.security.service;

import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.backendboard.domain.auth.entity.AuthUser;
import com.backendboard.domain.auth.entity.type.UserRole;
import com.backendboard.domain.auth.repository.AuthUserRepository;
import com.backendboard.domain.user.entity.User;
import com.backendboard.domain.user.repository.UserRepository;
import com.backendboard.global.security.dto.CustomOAuth2User;
import com.backendboard.global.security.dto.KakaoOAuth2Response;
import com.backendboard.global.security.dto.OAuth2Response;
import com.backendboard.global.security.dto.OAuth2UserDto;

import lombok.RequiredArgsConstructor;

@Transactional(readOnly = true)
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
private final AuthUserRepository authUserRepository;

@Transactional
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {

OAuth2User oAuth2User = super.loadUser(userRequest);

String registrationId = userRequest.getClientRegistration().getRegistrationId();
OAuth2Response oAuth2Response = null;

if (registrationId.equals("kakao")) {
oAuth2Response = new KakaoOAuth2Response(oAuth2User.getAttributes());
} else {
return null;
}

String username = oAuth2Response.getProvider() + "#" + oAuth2Response.getProviderId();
String name = oAuth2Response.getName();
AuthUser authUser = authUserRepository.findObjectByUsername(username);
OAuth2UserDto oAuth2UserDto = null;

if (authUser == null) {
AuthUser newAuthUser = AuthUser.create(username, "", UserRole.USER);
User newUser = newAuthUser.createUser(name, name + "#" + oAuth2Response.getProvider());
authUserRepository.save(newAuthUser);
userRepository.save(newUser);
oAuth2UserDto = new OAuth2UserDto(username, name, UserRole.USER);
} else {
oAuth2UserDto = new OAuth2UserDto(username, name, authUser.getRole());
}

return new CustomOAuth2User(oAuth2UserDto);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,19 @@ public String extractedRefreshToken(Cookie[] cookies) {
.orElse(null);
}

public Cookie createAccessCookie(String token) {
Cookie cookie = new Cookie("access", token);
cookie.setMaxAge((int)(expiredAccessTokenTime / 1000));
cookie.setHttpOnly(false);
cookie.setPath("/");
return cookie;
}

public Cookie createRefreshCookie(String token) {
Cookie cookie = new Cookie("refresh", token);
cookie.setMaxAge((int)(expiredRefreshTokenTime / 1000));
cookie.setHttpOnly(true);
cookie.setPath("/");
return cookie;
}

Expand Down