Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/user argument resolver #36

Merged
merged 7 commits into from
Oct 31, 2024
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
13 changes: 13 additions & 0 deletions src/main/java/online/pictz/api/common/annotation/CurrentUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package online.pictz.api.common.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurrentUser {
}
25 changes: 25 additions & 0 deletions src/main/java/online/pictz/api/common/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package online.pictz.api.common.config;

import online.pictz.api.common.resolver.CurrentUserArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
* Spring MVC 설정을 담당하는 클래스
* 커스텀 Argument Resolver를 등록
*/
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

private final CurrentUserArgumentResolver currentUserArgumentResolver;

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(currentUserArgumentResolver);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package online.pictz.api.common.resolver;

import lombok.RequiredArgsConstructor;
import online.pictz.api.common.annotation.CurrentUser;
import online.pictz.api.user.dto.UserDto;
import online.pictz.api.user.entity.CustomOAuth2User;
import online.pictz.api.user.exception.UserUnauthorized;
import org.springframework.core.MethodParameter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
@RequiredArgsConstructor
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(CurrentUser.class) &&
UserDto.class.isAssignableFrom(parameter.getParameterType());
}

@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory){

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
throw UserUnauthorized.of();
}

var principal = authentication.getPrincipal();

if (principal instanceof CustomOAuth2User) {
CustomOAuth2User customUser = (CustomOAuth2User) principal;
return new UserDto(customUser.getSiteUserId(), customUser.getProviderId());
}

throw UserUnauthorized.of();
}
}
11 changes: 11 additions & 0 deletions src/main/java/online/pictz/api/user/dto/UserDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package online.pictz.api.user.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class UserDto {
private final Long id;
private final String providerId;
}
44 changes: 44 additions & 0 deletions src/main/java/online/pictz/api/user/entity/CustomOAuth2User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package online.pictz.api.user.entity;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.user.OAuth2User;

public class CustomOAuth2User implements OAuth2User {

private final SiteUser siteUser;
private final Map<String, Object> attributes;
private final Collection<? extends GrantedAuthority> authorities;

public CustomOAuth2User(SiteUser siteUser, Map<String, Object> attributes) {
this.siteUser = siteUser;
this.attributes = Collections.unmodifiableMap(attributes);
this.authorities = Collections.singleton(new SimpleGrantedAuthority(siteUser.getRole().name()));
}

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

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}

@Override
public String getName() {
return siteUser.getProviderId();
}

public Long getSiteUserId() {
return siteUser.getId();
}

public String getProviderId() {
return siteUser.getProviderId();
}
}
16 changes: 8 additions & 8 deletions src/main/java/online/pictz/api/user/entity/SiteUser.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,11 @@
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@ToString
@Entity
@Table(name = "site_users")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SiteUser {

@Id
Expand All @@ -33,4 +25,12 @@ public class SiteUser {
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private SiteUserRole role;

protected SiteUser() {}

public SiteUser(String providerId) {
this.providerId = providerId;
this.role = SiteUserRole.ROLE_USER;
}

}
23 changes: 23 additions & 0 deletions src/main/java/online/pictz/api/user/exception/UserNotFound.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package online.pictz.api.user.exception;

import online.pictz.api.common.exception.domain.ErrorType;
import online.pictz.api.common.exception.domain.PictzException;
import org.springframework.http.HttpStatus;

public class UserNotFound extends PictzException {

public UserNotFound(String providerId) {
super(String.format("User with providerId '%s' not found", providerId));
}

@Override
public HttpStatus getHttpStatus() {
return HttpStatus.NOT_FOUND;
}

@Override
public String getErrorCode() {
return ErrorType.RESOURCE_NOT_FOUND.getCode();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package online.pictz.api.user.exception;

import online.pictz.api.common.exception.domain.ErrorType;
import online.pictz.api.common.exception.domain.PictzException;
import org.springframework.http.HttpStatus;

public class UserUnauthorized extends PictzException {

public UserUnauthorized(String message) {
super(message);
}

public static UserUnauthorized of() {
return new UserUnauthorized("User Unauthorized access");
}

@Override
public HttpStatus getHttpStatus() {
return HttpStatus.UNAUTHORIZED;
}

@Override
public String getErrorCode() {
return ErrorType.UNAUTHORIZED.getCode();
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package online.pictz.api.user.service;

import java.util.Collections;
import java.util.Optional;

import lombok.RequiredArgsConstructor;
import online.pictz.api.user.entity.CustomOAuth2User;
import online.pictz.api.user.entity.SiteUser;
import online.pictz.api.user.entity.SiteUserRole;
import online.pictz.api.user.repository.SiteUserRepository;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.stereotype.Service;

/**
* OAuth2 인증 후 사용자 회원가입 or 로그인
*/
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
Expand All @@ -34,23 +34,10 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic

String providerId = (String) attributes.get(userNameAttributeName);

Optional<SiteUser> optionalUser = siteUserRepository.findByProviderId(providerId);

SiteUser siteUser;
if (optionalUser.isPresent()) {
siteUser = optionalUser.get();
} else {
siteUser = SiteUser.builder()
.providerId(providerId)
.role(SiteUserRole.ROLE_USER)
.build();
siteUserRepository.save(siteUser);
}

return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority(siteUser.getRole().name())),
attributes,
userNameAttributeName
);
SiteUser siteUser = siteUserRepository.findByProviderId(providerId)
.orElseGet(() -> siteUserRepository.save(new SiteUser(providerId)));

return new CustomOAuth2User(siteUser, attributes);
}

}