-
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.
Merge pull request #2 from Central-MakeUs/1-social-login-구현하기
social login 구현하기
- Loading branch information
Showing
13 changed files
with
376 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
# Purithm_Server | ||
# PURITHM-Server | ||
CMC 15기 PURITHM 서버 레포지토리입니다. |
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
66 changes: 66 additions & 0 deletions
66
purithm/src/main/java/com/example/purithm/auth/config/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,66 @@ | ||
package com.example.purithm.auth.config; | ||
|
||
import com.example.purithm.auth.exception.JWTAuthenticationEntryPoint; | ||
import com.example.purithm.auth.filter.JWTFilter; | ||
import com.example.purithm.auth.handler.OAuth2AuthenticationFailureHandler; | ||
import com.example.purithm.auth.handler.OAuth2AuthenticationSuccessHandler; | ||
import com.example.purithm.auth.jwt.JWTUtil; | ||
import com.example.purithm.auth.service.OAuth2UserService; | ||
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.config.http.SessionCreationPolicy; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
@Configuration | ||
@EnableWebSecurity | ||
public class SecurityConfig { | ||
|
||
private final OAuth2UserService oAuth2UserService; | ||
private final OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler; | ||
private final OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler; | ||
private final JWTAuthenticationEntryPoint jwtAuthenticationEntryPoint; | ||
private final JWTUtil jwtUtil; | ||
|
||
public SecurityConfig( | ||
OAuth2UserService oAuth2UserService, | ||
OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler, | ||
OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler, | ||
JWTAuthenticationEntryPoint jwtAuthenticationEntryPoint, | ||
JWTUtil jwtUtil) { | ||
this.oAuth2UserService = oAuth2UserService; | ||
this.oAuth2AuthenticationSuccessHandler = oAuth2AuthenticationSuccessHandler; | ||
this.oAuth2AuthenticationFailureHandler = oAuth2AuthenticationFailureHandler; | ||
this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint; | ||
this.jwtUtil = jwtUtil; | ||
} | ||
@Bean | ||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
http | ||
.csrf(csrf -> csrf.disable()) | ||
.formLogin(formLogin -> formLogin.disable()) | ||
.httpBasic(httpBasic -> httpBasic.disable()) | ||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) | ||
.addFilterBefore(new JWTFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class) | ||
.authorizeHttpRequests(authorize -> authorize | ||
.requestMatchers("/api/**").authenticated() | ||
.anyRequest().permitAll() | ||
) | ||
.oauth2Login(oauth2Login -> oauth2Login | ||
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint | ||
.baseUri("/oauth2/authorization") | ||
) | ||
.redirectionEndpoint(redirectionEndpoint -> redirectionEndpoint | ||
.baseUri("/login/oauth2/code/*") | ||
) | ||
.userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint.userService(oAuth2UserService)) | ||
.successHandler(oAuth2AuthenticationSuccessHandler) | ||
.failureHandler(oAuth2AuthenticationFailureHandler) | ||
) | ||
.exceptionHandling(handler -> handler.authenticationEntryPoint(jwtAuthenticationEntryPoint)); | ||
|
||
return http.build(); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
purithm/src/main/java/com/example/purithm/auth/dto/response/LoginSuccessResponseDto.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,11 @@ | ||
package com.example.purithm.auth.dto.response; | ||
|
||
import lombok.Builder; | ||
|
||
@Builder | ||
public record LoginSuccessResponseDto( | ||
int code, | ||
String message, | ||
String token | ||
) { | ||
} |
35 changes: 35 additions & 0 deletions
35
purithm/src/main/java/com/example/purithm/auth/entity/CustomOAuth2User.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,35 @@ | ||
package com.example.purithm.auth.entity; | ||
|
||
import java.util.Collection; | ||
import java.util.Map; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.oauth2.core.user.OAuth2User; | ||
|
||
public class CustomOAuth2User implements OAuth2User { | ||
|
||
private final String username; | ||
|
||
public CustomOAuth2User(String username) { | ||
this.username = username; | ||
} | ||
|
||
@Override | ||
public <A> A getAttribute(String name) { | ||
return OAuth2User.super.getAttribute(name); | ||
} | ||
|
||
@Override | ||
public Map<String, Object> getAttributes() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Collection<? extends GrantedAuthority> getAuthorities() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return username; | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
purithm/src/main/java/com/example/purithm/auth/exception/JWTAuthenticationEntryPoint.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,22 @@ | ||
package com.example.purithm.auth.exception; | ||
|
||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.AuthenticationEntryPoint; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||
|
||
@Override | ||
public void commence(HttpServletRequest request, HttpServletResponse response, | ||
AuthenticationException authException) throws IOException, ServletException { | ||
response.sendError( | ||
HttpServletResponse.SC_UNAUTHORIZED, | ||
authException.getLocalizedMessage() | ||
); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
purithm/src/main/java/com/example/purithm/auth/filter/JWTFilter.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,51 @@ | ||
package com.example.purithm.auth.filter; | ||
|
||
import com.example.purithm.auth.entity.CustomOAuth2User; | ||
import com.example.purithm.auth.jwt.JWTUtil; | ||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
public class JWTFilter extends OncePerRequestFilter { | ||
|
||
private final JWTUtil jwtUtil; | ||
|
||
public JWTFilter(JWTUtil jwtUtil) { | ||
this.jwtUtil = jwtUtil; | ||
} | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, | ||
FilterChain filterChain) throws ServletException, IOException { | ||
try { | ||
String token = request.getHeader("Authorization"); | ||
|
||
if (token == null || !token.startsWith("Bearer ")) { | ||
filterChain.doFilter(request, response); | ||
return; | ||
} | ||
|
||
token = token.substring(7); | ||
if (jwtUtil.isExpired(token)) { | ||
filterChain.doFilter(request, response); | ||
return; | ||
} | ||
|
||
String username = jwtUtil.getUsername(token); | ||
|
||
CustomOAuth2User oAuth2User = new CustomOAuth2User(username); | ||
Authentication authToken = new UsernamePasswordAuthenticationToken(oAuth2User, null, null); | ||
SecurityContextHolder.getContext().setAuthentication(authToken); | ||
} catch (Exception e) { | ||
request.setAttribute("exception", e); | ||
} | ||
|
||
filterChain.doFilter(request, response); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
...hm/src/main/java/com/example/purithm/auth/handler/OAuth2AuthenticationFailureHandler.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,21 @@ | ||
package com.example.purithm.auth.handler; | ||
|
||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.authentication.AuthenticationFailureHandler; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class OAuth2AuthenticationFailureHandler implements AuthenticationFailureHandler { | ||
|
||
@Override | ||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, | ||
AuthenticationException exception) throws IOException, ServletException { | ||
response.setContentType("application/json"); | ||
response.setStatus(response.SC_UNAUTHORIZED); | ||
response.getOutputStream().println("{\"error\": \"Unauthorized\", \"message\": \"" + exception.getMessage() + "\"}"); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
...hm/src/main/java/com/example/purithm/auth/handler/OAuth2AuthenticationSuccessHandler.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,41 @@ | ||
package com.example.purithm.auth.handler; | ||
|
||
import com.example.purithm.auth.dto.response.LoginSuccessResponseDto; | ||
import com.example.purithm.auth.entity.CustomOAuth2User; | ||
import com.example.purithm.auth.jwt.JWTUtil; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
|
||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler { | ||
|
||
private final JWTUtil jwtUtil; | ||
|
||
public OAuth2AuthenticationSuccessHandler(JWTUtil jwtUtil) { | ||
this.jwtUtil = jwtUtil; | ||
} | ||
|
||
@Override | ||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { | ||
CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal(); | ||
|
||
String token = jwtUtil.createJwt(oAuth2User.getName(), 60 * 60 * 60 * 1000L); | ||
|
||
response.setContentType("application/json"); | ||
response.setStatus(response.SC_OK); | ||
LoginSuccessResponseDto body = LoginSuccessResponseDto.builder() | ||
.code(200).message("login success").token(token).build(); | ||
|
||
ObjectMapper objectMapper = new ObjectMapper(); | ||
String json = objectMapper.writeValueAsString(body); | ||
|
||
response.getOutputStream().println(json); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
purithm/src/main/java/com/example/purithm/auth/jwt/JWTUtil.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,36 @@ | ||
package com.example.purithm.auth.jwt; | ||
|
||
import io.jsonwebtoken.Jwts; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Date; | ||
import javax.crypto.SecretKey; | ||
import javax.crypto.spec.SecretKeySpec; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class JWTUtil { | ||
private SecretKey secretKey; | ||
|
||
public JWTUtil(@Value("${spring.jwt.secret}")String secret) { | ||
this.secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm()); | ||
} | ||
|
||
public String getUsername(String token) { | ||
|
||
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("username", String.class); | ||
} | ||
|
||
public Boolean isExpired(String token) { | ||
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); | ||
} | ||
|
||
public String createJwt(String nickname, Long expiredMs) { | ||
return Jwts.builder() | ||
.claim("nickname", nickname) | ||
.issuedAt(new Date(System.currentTimeMillis())) | ||
.expiration(new Date(System.currentTimeMillis() + expiredMs)) | ||
.signWith(secretKey) | ||
.compact(); | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
purithm/src/main/java/com/example/purithm/auth/service/OAuth2UserService.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,45 @@ | ||
package com.example.purithm.auth.service; | ||
|
||
import com.example.purithm.auth.entity.CustomOAuth2User; | ||
import com.example.purithm.user.entity.User; | ||
import com.example.purithm.user.repository.UserRepository; | ||
import java.util.Map; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
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; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class OAuth2UserService extends DefaultOAuth2UserService { | ||
|
||
private final UserRepository userRepository; | ||
|
||
@Override | ||
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { | ||
OAuth2User oAuth2User = super.loadUser(userRequest); | ||
|
||
String provider = userRequest.getClientRegistration().getRegistrationId().toUpperCase(); | ||
Map<String, Object> properties = oAuth2User.getAttribute("properties"); | ||
String id = oAuth2User.getAttributes().get("id").toString(); | ||
|
||
String username = provider+" "+id; | ||
User existUser = userRepository.findByUsername(username); | ||
|
||
if (existUser == null) { | ||
User user = User.builder() | ||
.profile((String) properties.get("thumbnail_image")) | ||
.nickname((String) properties.get("nickname")) | ||
.username(username) | ||
.build(); | ||
|
||
userRepository.save(user); | ||
} | ||
|
||
return new CustomOAuth2User(username); | ||
} | ||
|
||
} |
32 changes: 32 additions & 0 deletions
32
purithm/src/main/java/com/example/purithm/user/entity/User.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,32 @@ | ||
package com.example.purithm.user.entity; | ||
|
||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Entity | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class User { | ||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
private String username; | ||
|
||
private String nickname; | ||
|
||
private String profile; | ||
|
||
@Builder | ||
public User(String username, String nickname, String profile) { | ||
this.username = username; | ||
this.nickname = nickname; | ||
this.profile = profile; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
purithm/src/main/java/com/example/purithm/user/repository/UserRepository.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,8 @@ | ||
package com.example.purithm.user.repository; | ||
|
||
import com.example.purithm.user.entity.User; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface UserRepository extends JpaRepository<User, Long> { | ||
User findByUsername(String username); | ||
} |