Skip to content

Commit

Permalink
Merge pull request #14 from Tave-13th-Project-Team-4-Fiurinee/feature…
Browse files Browse the repository at this point in the history
…/kakao-login

Feature/kakao login
  • Loading branch information
qormoon authored Jun 15, 2024
2 parents 5aaaad1 + fa6f545 commit 80243eb
Show file tree
Hide file tree
Showing 23 changed files with 834 additions and 36 deletions.
24 changes: 21 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,32 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
// Web
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'

// JPA & DB
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
runtimeOnly 'org.postgresql:postgresql'

// Security
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

// GSON
implementation 'com.google.code.gson:gson:2.10.1'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

// Lombok
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'

// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/example/fiurinee/FiurineeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ public static void main(String[] args) {
}

}

//http://localhost:8080/oauth2/authorization/kakao
32 changes: 0 additions & 32 deletions src/main/java/com/example/fiurinee/SecurityConfig.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.fiurinee.domain.jwt.exception;

import io.jsonwebtoken.ExpiredJwtException;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@Getter
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public class CustomExpiredJwtException extends ExpiredJwtException {
private final String message;

public CustomExpiredJwtException(String message, ExpiredJwtException source) {
super(source.getHeader(), source.getClaims(), source.getMessage());
this.message = message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.fiurinee.domain.jwt.utils;

public class JwtConstants {
public static final String key = "DG3K2NG9lK3T2FLfnO283HO1NFLAy9FGJ23UM9Rv923YRV923HT";
public static final int ACCESS_EXP_TIME = 60;
public static final int REFRESH_EXP_TIME = 60 * 24;

public static final String JWT_HEADER = "Authorization";
public static final String JWT_TYPE = "Bearer ";
}
94 changes: 94 additions & 0 deletions src/main/java/com/example/fiurinee/domain/jwt/utils/JwtUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.example.fiurinee.domain.jwt.utils;

import com.example.fiurinee.domain.jwt.exception.CustomExpiredJwtException;
import com.example.fiurinee.domain.member.data.PrincipalDetail;
import com.example.fiurinee.domain.member.data.Role;
import com.example.fiurinee.domain.member.dto.MemberDto;
import com.example.fiurinee.global.exception.CustomException;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Set;

public class JwtUtils {
public static String secretKey = JwtConstants.key;

public static String getTokenFromHeader(String header) {
return header.split(" ")[1];
}

public static String generateToken(Map<String, Object> valueMap, int validTime) {
SecretKey key = null;
try {
key = Keys.hmacShaKeyFor(JwtUtils.secretKey.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return Jwts.builder()
.setHeader(Map.of("typ", "JWT"))
.setClaims(valueMap)
.setIssuedAt(Date.from(ZonedDateTime.now().toInstant()))
.setExpiration(Date.from(ZonedDateTime.now().plusMinutes(validTime).toInstant()))
.signWith(key)
.compact();
}

public static Authentication getAuthentication(String token) {
Map<String, Object> claims = validateToken(token);

Long id = ((Integer) claims.get("id")).longValue();
String email = (String) claims.get("email");
String name = (String) claims.get("name");
String socialId = (String) claims.get("socialId");
String role = (String) claims.get("role");
Role memberRole = Role.valueOf(role);

MemberDto memberDto = new MemberDto(id, email, name, socialId, memberRole);
Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority(memberDto.role().getValue()));
PrincipalDetail principalDetail = new PrincipalDetail(memberDto, authorities);

return new UsernamePasswordAuthenticationToken(principalDetail, "", authorities);
}

public static Map<String, Object> validateToken(String token) {
Map<String, Object> claim = null;
try {
SecretKey key = Keys.hmacShaKeyFor(JwtUtils.secretKey.getBytes(StandardCharsets.UTF_8));
claim = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException expiredJwtException) {
throw new CustomExpiredJwtException("토큰이 만료되었습니다", expiredJwtException);
} catch (Exception e) {
throw new CustomException("Error");
}
return claim;
}

public static boolean isExpired(String token) {
try {
validateToken(token);
} catch (Exception e) {
return (e instanceof CustomExpiredJwtException);
}
return false;
}

public static long tokenRemainTime(Integer expTime) {
Date expDate = new Date((long) expTime * (1000));
long remainMs = expDate.getTime() - System.currentTimeMillis();
return remainMs / (1000 * 60);
}
}
46 changes: 46 additions & 0 deletions src/main/java/com/example/fiurinee/domain/member/data/Member.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.example.fiurinee.domain.member.data;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.security.oauth2.core.OAuth2AccessToken;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "member")
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String email;

private String name;

private String socialId;

@Enumerated(EnumType.STRING)
private Role role;

private String kakaoAccessToken;

@Builder
private Member(String email, String name, String socialId, Role role, String kakaoAccessToken) {
this.email = email;
this.name = name;
this.socialId = socialId;
this.role = role;
this.kakaoAccessToken = kakaoAccessToken;
}

public static Member createMember(String email, String name, String socialId, Role role, String kakaoAccessToken) {
return new Member(email, name, socialId, role, kakaoAccessToken);
}

public void updateKakaoToken(String kakaoAccessToken){
this.kakaoAccessToken = kakaoAccessToken;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.example.fiurinee.domain.member.data;

import com.example.fiurinee.domain.member.dto.MemberDto;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Data
public class PrincipalDetail implements UserDetails, OAuth2User {
private MemberDto memberDto;
private Collection<? extends GrantedAuthority> authorities;
private Map<String, Object> attributes;

public PrincipalDetail(MemberDto memberDto, Collection<? extends GrantedAuthority> authorities) {
this.memberDto = memberDto;
this.authorities = authorities;
}

public PrincipalDetail(MemberDto memberDto, Collection<? extends GrantedAuthority> authorities, Map<String, Object> attributes) {
this.memberDto = memberDto;
this.authorities = authorities;
this.attributes = attributes;
}

public Map<String, Object> getMemberInfo() {
Map<String, Object> info = new HashMap<>();
info.put("id", memberDto.id());
info.put("email", memberDto.email());
info.put("name", memberDto.name());
info.put("socialId", memberDto.socialId());
info.put("role", memberDto.role().getValue());
return info;
}

@Override
public String getName() {
return memberDto.email();
}

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

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

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

@Override
public String getUsername() {
return memberDto.name();
}

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

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

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

@Override
public boolean isEnabled() {
return true;
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/example/fiurinee/domain/member/data/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.fiurinee.domain.member.data;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum Role {
USER("USER"),
ADMIN("ADMIN");

private final String value;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.fiurinee.domain.member.dto;

import com.example.fiurinee.domain.member.data.Role;

public record MemberDto(
Long id,
String email,
String name,
String socialId,
Role role
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.fiurinee.domain.member.repository;

import com.example.fiurinee.domain.member.data.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> findByEmail(String email);

Optional<Member> findBySocialId(String email);

}
Loading

0 comments on commit 80243eb

Please sign in to comment.