Skip to content

Commit

Permalink
[#3] chore: PrincipalDetails 생성 및 Authentication에 저장되도록 개발
Browse files Browse the repository at this point in the history
  • Loading branch information
dl-00-e8 committed Jan 28, 2024
1 parent ad024a5 commit ac2f9db
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.member.service.MemberService;
import com.gongjakso.server.global.common.ApplicationResponse;
import com.gongjakso.server.global.security.PrincipalDetails;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
Expand All @@ -24,7 +25,7 @@ public class MemberController {
private final MemberService memberService;

@PutMapping("")
public ApplicationResponse<MemberRes> update(@AuthenticationPrincipal Member member, @Valid @RequestBody MemberReq memberReq) {
return ApplicationResponse.ok(memberService.update(member, memberReq));
public ApplicationResponse<MemberRes> update(@AuthenticationPrincipal PrincipalDetails principalDetails, @Valid @RequestBody MemberReq memberReq) {
return ApplicationResponse.ok(memberService.update(principalDetails.getMember(), memberReq));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.gongjakso.server.domain.member.dto;

public record MemberReq(String name,
import jakarta.validation.constraints.NotNull;
import lombok.Builder;

@Builder
public record MemberReq(@NotNull String name,
String status,
String major,
String job) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ public static MemberRes of(Member member) {
return MemberRes.builder()
.memberId(member.getMemberId())
.email(member.getEmail())
.name(member.getName())
.profileUrl(member.getProfileUrl())
.memberType(member.getMemberType())
.loginType(member.getLoginType())
.status(member.getStatus())
.major(member.getMajor())
.job(member.getJob())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package com.gongjakso.server.domain.member.enumerate;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum MemberType {
GENERAL, ADMIN
GENERAL("ROLE_GENERAL", "일반"),
ADMIN("ROLE_ADMIN", "관리자");

private final String role;
private final String title;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.gongjakso.server.domain.member.repository;

import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.member.enumerate.MemberType;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {

Optional<Member> findMemberByEmailAndDeletedAtIsNull(String email);

Optional<Member> findMemberByEmailAndMemberTypeAndDeletedAtIsNull(String email, MemberType memberType);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.gongjakso.server.global.security;

import com.gongjakso.server.domain.member.entity.Member;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;

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

public class PrincipalDetails implements UserDetails, OAuth2User {

@Getter
private final Member member;
private Map<String, Object> attributes;

// 일반 로그인
public PrincipalDetails(Member member) {
this.member = member;
}

// OAuth 로그인
public PrincipalDetails(Member member, Map<String, Object> attributes) {
this.member = member;
this.attributes = attributes;
}

// 권한 정보 반환 (GENERAL, ADMIN 중 하나)
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(member.getMemberType().getRole()));

return authorities;
}

// 사용자의 비밀번호 반환
@Override
public String getPassword() {
return member.getPassword();
}

// 사용자의 이름 반환
@Override
public String getUsername() {
return member.getName();
}

// 계정이 잠기지 않았으므로 true 반환
@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

// 패스워드가 만료되지 않았으므로 true 반환
@Override
public boolean isCredentialsNonExpired() {
return true;
}

// 계속 사용 가능한 것이기에 true 반환
@Override
public boolean isEnabled() {
return true;
}

@Override
public String getName() {
return null;
}

@Override
public Map<String, Object> getAttributes() {
return attributes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.member.enumerate.MemberType;
import com.gongjakso.server.domain.member.repository.MemberRepository;
import com.gongjakso.server.global.exception.ApplicationException;
import com.gongjakso.server.global.exception.ErrorCode;
import com.gongjakso.server.global.security.PrincipalDetails;
import com.gongjakso.server.global.security.jwt.dto.TokenDto;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
Expand All @@ -13,19 +16,12 @@
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
Expand All @@ -35,7 +31,6 @@ public class TokenProvider {
private String secretKey;
private Key key;
private final MemberRepository memberRepository;
private final RedisTemplate<String, Object> redisTemplate;

// ATK 만료시간: 1일
private static final long accessTokenExpirationTime = 7 * 24 * 60 * 60 * 1000L;
Expand Down Expand Up @@ -122,14 +117,9 @@ public boolean validateToken(String token) {
*/
public TokenDto accessTokenReissue(String token) {
String email = getEmail(token);
MemberType type = getType(token);

Member member = memberRepository.findMemberByEmailAndDeletedAtIsNull(email).orElseThrow(RuntimeException::new); // Exception은 실제 개발에서는 커스텀 필요
String storedRefreshToken = (String) redisTemplate.opsForValue().get(email + type.toString()); // Key는 email + role로 저장되어 있으며, value가 해당 정보에 대한 refreshToken임.
if(storedRefreshToken == null || !storedRefreshToken.equals(token)) {
throw new RuntimeException();
}
MemberType memberType = getType(token);

Member member = memberRepository.findMemberByEmailAndMemberTypeAndDeletedAtIsNull(email, memberType).orElseThrow(() -> new ApplicationException(ErrorCode.NOT_FOUND_EXCEPTION));
String accessToken = createAccessToken(member);

// 해당 부분에 refreshToken의 만료기간이 얼마 남지 않았을 때, 자동 재발급하는 로직을 추가할 수 있음.
Expand All @@ -147,17 +137,11 @@ public TokenDto accessTokenReissue(String token) {
*/
public Authentication getAuthentication(String token) {
String email = getEmail(token);
MemberType type = getType(token);

Member member = memberRepository.findMemberByEmailAndDeletedAtIsNull(email).orElseThrow(RuntimeException::new); // Exception은 실제 개발에서는 커스텀 필요
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(member.getMemberType().toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());

UserDetails details = new org.springframework.security.core.userdetails.User(email, "", authorities);
MemberType memberType = getType(token);
Member member = memberRepository.findMemberByEmailAndMemberTypeAndDeletedAtIsNull(email, memberType).orElseThrow(() -> new ApplicationException(ErrorCode.NOT_FOUND_EXCEPTION));
PrincipalDetails principalDetails = new PrincipalDetails(member);

return new UsernamePasswordAuthenticationToken(details, "", authorities);
return new UsernamePasswordAuthenticationToken(principalDetails, "", principalDetails.getAuthorities());
}

/**
Expand Down

0 comments on commit ac2f9db

Please sign in to comment.