From 795606334a9509908d76d5bba3a25e83d6ce3438 Mon Sep 17 00:00:00 2001 From: hoon7566 Date: Fri, 18 Feb 2022 14:01:39 +0900 Subject: [PATCH] =?UTF-8?q?feat(user-service):=20Oauth=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=EC=8B=9C=20token=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Oauth 로그인시 response header에 access token, refresh token을 담아 보냄. --- .../jwt/service/RefreshTokenService.java | 1 - .../jwt/service/RefreshTokenServiceImpl.java | 20 ++-- .../domain/jwt/utils/JwtTokenProvider.java | 8 +- .../domain/user/entity/Customer.java | 4 +- .../userservice/domain/user/entity/Role.java | 14 --- .../userservice/domain/user/entity/User.java | 15 +-- .../domain/user/service/UserService.java | 2 + .../domain/user/service/UserServiceImpl.java | 113 +++++++++--------- .../security/LoginAuthenticationFilter.java | 5 +- .../global/security/SecurityConfig.java | 10 +- 10 files changed, 85 insertions(+), 107 deletions(-) delete mode 100644 user-service/src/main/java/com/justpickup/userservice/domain/user/entity/Role.java diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenService.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenService.java index 3475a20..24cb6db 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenService.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenService.java @@ -3,6 +3,5 @@ package com.justpickup.userservice.domain.jwt.service; import com.justpickup.userservice.domain.user.dto.JwtTokenDto; public interface RefreshTokenService { - void updateRefreshToken(Long id, String refreshToken); JwtTokenDto refreshJwtToken(String accessToken, String refreshToken); } diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenServiceImpl.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenServiceImpl.java index e5706cb..00dea5a 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenServiceImpl.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenServiceImpl.java @@ -8,8 +8,12 @@ import com.justpickup.userservice.domain.user.repository.UserRepository; import com.justpickup.userservice.domain.jwt.utils.JwtTokenProvider; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; +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; import org.springframework.transaction.annotation.Transactional; @@ -21,17 +25,11 @@ import java.util.stream.Collectors; @Transactional(readOnly = true) @Slf4j public class RefreshTokenServiceImpl implements RefreshTokenService { + private final UserDetailsService userDetailsService; private final UserRepository userRepository; private final JwtTokenProvider jwtTokenProvider; - @Transactional - @Override - public void updateRefreshToken(Long id, String refreshToken) { - User user = userRepository.findById(id) - .orElseThrow(() -> new NotExistUserException("사용자 고유번호 : " + id + "는 없는 사용자입니다.")); - user.changeRefreshToken(refreshToken); - } @Transactional @Override @@ -56,7 +54,7 @@ public class RefreshTokenServiceImpl implements RefreshTokenService { ", refreshToken = " + refreshToken); } - Authentication authentication = jwtTokenProvider.getAuthentication(user.getEmail()); + Authentication authentication = getAuthentication(user.getEmail()); List roles = authentication.getAuthorities() .stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); @@ -70,4 +68,10 @@ public class RefreshTokenServiceImpl implements RefreshTokenService { .refreshToken(newRefreshToken) .build(); } + + public Authentication getAuthentication(String email) { + UserDetails userDetails = userDetailsService.loadUserByUsername(email); + return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); + } + } diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/utils/JwtTokenProvider.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/utils/JwtTokenProvider.java index ab6e357..5b4c21e 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/utils/JwtTokenProvider.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/utils/JwtTokenProvider.java @@ -16,12 +16,9 @@ import java.util.List; import java.util.UUID; @Component -@RequiredArgsConstructor @Slf4j public class JwtTokenProvider { - private final UserDetailsService userDetailsService; - @Value("${token.access-expired-time}") private long ACCESS_EXPIRED_TIME; @@ -60,10 +57,7 @@ public class JwtTokenProvider { .compact(); } - public Authentication getAuthentication(String email) { - UserDetails userDetails = userDetailsService.loadUserByUsername(email); - return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); - } + public String getUserId(String token) { try { diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/Customer.java b/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/Customer.java index 679db16..d22c522 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/Customer.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/Customer.java @@ -16,9 +16,9 @@ public class Customer extends User { @Enumerated(EnumType.STRING) private AuthType oauthType; - public Customer(String email, String password, String name, String phoneNumber, AuthType oauthType) { - super(email, password, name, phoneNumber,Role.USER); + super(email, password, name, phoneNumber,null); + this.dtype = Customer.class.getSimpleName(); this.oauthType = oauthType; } } diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/Role.java b/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/Role.java deleted file mode 100644 index ad49513..0000000 --- a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/Role.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.justpickup.userservice.domain.user.entity; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public enum Role { - GUEST("ROLE_GUEST", "손님"), - USER("ROLE_USER", "일반 사용자"); - - private final String key; - private final String title; -} diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/User.java b/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/User.java index bfce461..af0760a 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/User.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/User.java @@ -12,7 +12,7 @@ import javax.persistence.*; @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn(name = "DTYPE") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class User extends BaseEntity { +public abstract class User extends BaseEntity { @Id @GeneratedValue @Column(name = "user_id") @@ -28,19 +28,8 @@ public class User extends BaseEntity { private String refreshTokenId; - @Enumerated(EnumType.STRING) - private Role role; - @Column(insertable = false,updatable = false) - private String dtype; - - public User(String email, String password, String name, String phoneNumber, Role role) { - this.email = email; - this.password = password; - this.name = name; - this.phoneNumber = phoneNumber; - this.role = role; - } + protected String dtype; public User(String email, String password, String name, String phoneNumber, String refreshTokenId) { this.email = email; diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/user/service/UserService.java b/user-service/src/main/java/com/justpickup/userservice/domain/user/service/UserService.java index 1e412b5..fd6136d 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/user/service/UserService.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/user/service/UserService.java @@ -9,6 +9,8 @@ import com.justpickup.userservice.domain.user.dto.StoreOwnerDto; import com.justpickup.userservice.domain.user.entity.StoreOwner; public interface UserService extends OAuth2UserService { + + void updateRefreshToken(Long id, String refreshToken); CustomerDto findCustomerByUserId(Long userId); StoreOwner saveStoreOwner(StoreOwnerDto storeOwnerDto); } diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/user/service/UserServiceImpl.java b/user-service/src/main/java/com/justpickup/userservice/domain/user/service/UserServiceImpl.java index 517cc9b..c39d4d0 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/user/service/UserServiceImpl.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/user/service/UserServiceImpl.java @@ -1,60 +1,54 @@ package com.justpickup.userservice.domain.user.service; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.justpickup.userservice.domain.jwt.utils.JwtTokenProvider; import com.justpickup.userservice.domain.user.dto.CustomerDto; +import com.justpickup.userservice.domain.user.dto.OAuthAttributeDto; import com.justpickup.userservice.domain.user.dto.StoreOwnerDto; -import com.justpickup.userservice.domain.user.dto.OAuthAttributeDto; -import com.justpickup.userservice.domain.user.dto.OAuthAttributeDto; import com.justpickup.userservice.domain.user.entity.Customer; import com.justpickup.userservice.domain.user.entity.StoreOwner; import com.justpickup.userservice.domain.user.entity.User; import com.justpickup.userservice.domain.user.exception.NotExistUserException; import com.justpickup.userservice.domain.user.repository.CustomerRepository; -import lombok.*; import com.justpickup.userservice.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.core.env.Environment; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.core.env.Environment; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.Serializable; -import java.util.Collections; - -import javax.servlet.http.HttpServletResponse; -import java.io.Serializable; -import java.util.Collections; - import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor -@Transactional(readOnly = true) +@Transactional(readOnly = true,propagation = Propagation.SUPPORTS) @Slf4j public class UserServiceImpl implements UserService, UserDetailsService { private final CustomerRepository customerRepository; private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; - private final HttpServletResponse response; - private final Environment env; + private final HttpServletRequest request; + private final JwtTokenProvider jwtTokenProvider; + + @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { @@ -85,31 +79,11 @@ public class UserServiceImpl implements UserService, UserDetailsService { return userRepository.save(storeOwner); } - @Data - @NoArgsConstructor - @AllArgsConstructor - @JsonIgnoreProperties(ignoreUnknown = true) - public static class AuthResponse{ - private String id; - private String name; - - } - - @Data - @NoArgsConstructor - @AllArgsConstructor - @Builder - static class AuthRequest { - private String code; - private String client_id; - private String client_secret; - } - @Override - @Transactional(readOnly = false) + @Transactional public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { OAuth2UserService delegate = new DefaultOAuth2UserService(); OAuth2User oAuth2User = delegate.loadUser(userRequest); @@ -124,28 +98,53 @@ public class UserServiceImpl implements UserService, UserDetailsService { // OAuth2UserService OAuthAttributeDto attributeDto = OAuthAttributeDto.of(registrationId, userNameAttributeName,oAuth2User.getAttributes()); - Customer customer = customerRepository.save( - customerRepository.findByEmail(attributeDto.getEmail()) - .orElse(attributeDto.toEntity(attributeDto)) - ); + + Customer customer = saveCustomer(attributeDto); + // TODO: 2022/02/16 Response에 token 담아 보내기 + String userEmail = customer.getEmail(); + + + Collection authorities = loadUserByUsername(userEmail).getAuthorities(); + List roles = authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); + + String accessToken = jwtTokenProvider.createJwtAccessToken(userEmail, request.getRequestURI(), roles); + String refreshToken = jwtTokenProvider.createJwtRefreshToken(); + + updateRefreshToken(customer.getId(), jwtTokenProvider.getRefreshTokenId(refreshToken)); + + customer.changeRefreshToken(refreshToken); + + response.setHeader("Access-token",accessToken); + response.setHeader("refresh-token",refreshToken); + + + return new DefaultOAuth2User( - Collections.singleton(new SimpleGrantedAuthority(customer.getRole().getKey())) + authorities , attributeDto.getAttributes() , attributeDto.getNameAttributeKey()); } - @Getter - public static class UserPayload implements Serializable { - private String name; - private String email; - - public UserPayload(Customer user){ - this.name = user.getName(); - this.email = user.getEmail(); - } + @Transactional + public Customer saveCustomer(OAuthAttributeDto attributeDto){ + return customerRepository.save( + customerRepository.findByEmail(attributeDto.getEmail()) + .orElse(attributeDto.toEntity(attributeDto)) + ); } + + + @Transactional + @Override + public void updateRefreshToken(Long id, String refreshToken) { + User user = userRepository.findById(id) + .orElseThrow(() -> new NotExistUserException("사용자 고유번호 : " + id + "는 없는 사용자입니다.")); + + user.changeRefreshToken(refreshToken); + } + } diff --git a/user-service/src/main/java/com/justpickup/userservice/global/security/LoginAuthenticationFilter.java b/user-service/src/main/java/com/justpickup/userservice/global/security/LoginAuthenticationFilter.java index 311a6a1..fa537fd 100644 --- a/user-service/src/main/java/com/justpickup/userservice/global/security/LoginAuthenticationFilter.java +++ b/user-service/src/main/java/com/justpickup/userservice/global/security/LoginAuthenticationFilter.java @@ -3,6 +3,7 @@ package com.justpickup.userservice.global.security; import com.fasterxml.jackson.databind.ObjectMapper; import com.justpickup.userservice.domain.jwt.service.RefreshTokenServiceImpl; import com.justpickup.userservice.domain.jwt.utils.JwtTokenProvider; +import com.justpickup.userservice.domain.user.service.UserService; import com.justpickup.userservice.global.dto.LoginRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -31,7 +32,7 @@ public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFil private final AuthenticationManager authenticationManager; private final JwtTokenProvider jwtTokenProvider; - private final RefreshTokenServiceImpl refreshTokenServiceImpl; + private final UserService userService; // login 리퀘스트 패스로 오는 요청을 판단 @Override @@ -67,7 +68,7 @@ public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFil String accessToken = jwtTokenProvider.createJwtAccessToken(userId, request.getRequestURI(), roles); String refreshToken = jwtTokenProvider.createJwtRefreshToken(); - refreshTokenServiceImpl.updateRefreshToken(Long.valueOf(userId), jwtTokenProvider.getRefreshTokenId(refreshToken)); + userService.updateRefreshToken(Long.valueOf(userId), jwtTokenProvider.getRefreshTokenId(refreshToken)); Map tokens = Map.of( "access_token", accessToken, diff --git a/user-service/src/main/java/com/justpickup/userservice/global/security/SecurityConfig.java b/user-service/src/main/java/com/justpickup/userservice/global/security/SecurityConfig.java index 7c73707..ca22404 100644 --- a/user-service/src/main/java/com/justpickup/userservice/global/security/SecurityConfig.java +++ b/user-service/src/main/java/com/justpickup/userservice/global/security/SecurityConfig.java @@ -4,6 +4,7 @@ import com.justpickup.userservice.domain.jwt.service.RefreshTokenServiceImpl; import com.justpickup.userservice.domain.jwt.utils.JwtTokenProvider; import com.justpickup.userservice.domain.user.service.UserService; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -19,10 +20,10 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig extends WebSecurityConfigurerAdapter { + + private final ApplicationContext applicationContext; private final UserDetailsService userDetailsService; private final BCryptPasswordEncoder bCryptPasswordEncoder; - private final JwtTokenProvider jwtTokenProvider; - private final RefreshTokenServiceImpl refreshTokenServiceImpl; private final UserService userService; @@ -34,7 +35,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { LoginAuthenticationFilter loginAuthenticationFilter = - new LoginAuthenticationFilter(authenticationManagerBean(), jwtTokenProvider, refreshTokenServiceImpl); + new LoginAuthenticationFilter(authenticationManagerBean() + ,applicationContext.getBean(JwtTokenProvider.class) + ,applicationContext.getBean(UserService.class)); + loginAuthenticationFilter.setFilterProcessesUrl("/login"); http.csrf().disable();