Skip to content

Commit

Permalink
feat: 앱팀 API v2 배포 (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
ozzing authored Apr 17, 2023
2 parents e61dea0 + 1765f0d commit ae06ea0
Show file tree
Hide file tree
Showing 121 changed files with 3,807 additions and 943 deletions.
Binary file added .DS_Store
Binary file not shown.
10 changes: 10 additions & 0 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## 🚀 Issue Summary


#### 🚗 Implement TODO
- [ ]
- [ ]


#### 🚓 Remarks

13 changes: 13 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## 📝 PR Summary


#### 🌵 Working Branch


#### 🌴 Works
- [ ]
- [ ]


#### 🌱 Related Issue

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ out/

### application.yml 파일들에 대한 gitignore 처리
application-local.yml
application-prod.yml

23 changes: 22 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,32 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'

// swagger
implementation 'org.springdoc:springdoc-openapi-ui:1.6.12'
implementation 'org.springdoc:springdoc-openapi-security:1.6.12'

// sentry
implementation 'io.sentry:sentry-spring-boot-starter:6.17.0'
implementation 'io.sentry:sentry-logback:6.17.0'

// lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

// mapper
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor "org.mapstruct:mapstruct-processor:1.4.2.Final"

// jwt
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2'

// DB
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'

// postgres
implementation group: 'org.postgresql', name: 'postgresql', version: '42.2.23'
runtimeOnly 'org.postgresql:postgresql'

Expand All @@ -56,6 +75,8 @@ dependencies {
//aws-s3
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-aws', version: '2.2.5.RELEASE'

// retry
implementation 'org.springframework.retry:spring-retry:1.2.5.RELEASE'

// test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand All @@ -75,7 +96,7 @@ querydsl {
sourceSets {
main.java.srcDir querydslDir
}
compileQuerydsl{
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}

Expand Down
57 changes: 0 additions & 57 deletions src/main/java/org/sopt/app/application/auth/AuthUseCaseImpl.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.sopt.app.application.auth;

import lombok.RequiredArgsConstructor;
import org.sopt.app.common.exception.NotFoundException;
import org.sopt.app.common.response.ErrorCode;
import org.sopt.app.interfaces.postgres.UserRepository;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
public class CustomUserDetailService implements UserDetailsService {

private final UserRepository userRepository;

public UserDetails loadUserByUsername(String username) throws NotFoundException {
return (UserDetails) userRepository.findUserById(Long.parseLong(username))
.orElseThrow(() -> new NotFoundException(ErrorCode.USER_NOT_FOUND.getMessage()));
}
}
104 changes: 104 additions & 0 deletions src/main/java/org/sopt/app/application/auth/JwtTokenService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package org.sopt.app.application.auth;

import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.joda.time.LocalDateTime;
import org.sopt.app.application.auth.PlaygroundAuthInfo.AppToken;
import org.sopt.app.application.user.UserInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class JwtTokenService {

private final CustomUserDetailService customUserDetailsService;
@Value("${jwt.secret}")
private String JWT_SECRET;

private String encodeKey(String secretKey) {
return Base64.getEncoder().encodeToString(secretKey.getBytes(StandardCharsets.UTF_8));
}

private Key getSigningKey(String keyString) {
val secretKey = this.encodeKey(keyString);
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey));
}

public PlaygroundAuthInfo.AppToken issueNewTokens(UserInfo.Id userId,
PlaygroundAuthInfo.PlaygroundMain playgroundMember) {
val accessToken = this.encodeJwtToken(userId, playgroundMember.getId());
val refreshToken = this.encodeJwtRefreshToken(userId);
return AppToken.builder().accessToken(accessToken).refreshToken(refreshToken).build();
}

private String encodeJwtToken(UserInfo.Id userId, Long playgroundId) {
val now = LocalDateTime.now();
return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setIssuer("sopt-makers")
.setIssuedAt(now.toDate())
.setSubject(userId.getId().toString())
.setExpiration(now.plusDays(1).toDate())
.claim("id", userId.getId())
.claim("playgroundId", playgroundId)
.claim("roles", "USER")
.signWith(getSigningKey(JWT_SECRET), SignatureAlgorithm.HS256)
.compact();
}

private String encodeJwtRefreshToken(UserInfo.Id userId) {
val now = LocalDateTime.now();
return Jwts.builder()
.setIssuedAt(now.toDate())
.setSubject(userId.getId().toString())
.setExpiration(now.plusDays(30).toDate())
.claim("id", userId.getId())
.claim("roles", "USER")
.signWith(getSigningKey(JWT_SECRET), SignatureAlgorithm.HS256)
.compact();
}

public UserInfo.Id getUserIdFromJwtToken(String token) {
val claims = Jwts.parser()
.setSigningKey(this.encodeKey(JWT_SECRET))
.parseClaimsJws(token)
.getBody();
return UserInfo.Id.builder().id(Long.parseLong(claims.getSubject())).build();
}

public Authentication getAuthentication(String token) {
val userDetails = customUserDetailsService.loadUserByUsername(
this.getUserIdFromJwtToken(token).getId().toString());
return new UsernamePasswordAuthenticationToken(userDetails, "",
userDetails.getAuthorities());
}


public Boolean validateToken(String token) {
try {
val claims = Jwts.parser()
.setSigningKey(this.encodeKey(JWT_SECRET)).parseClaimsJws(token);

return !claims.getBody().getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}

public String getToken(HttpServletRequest request) {
return request.getHeader("Authorization");
}
}
109 changes: 109 additions & 0 deletions src/main/java/org/sopt/app/application/auth/PlaygroundAuthInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package org.sopt.app.application.auth;

import java.util.List;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.sopt.app.domain.enums.UserStatus;

public class PlaygroundAuthInfo {

@Getter
@Builder
@ToString
public static class PlaygroundAccessToken {

private String accessToken;
}

@Getter
@Builder
@ToString
public static class AppToken {

private String accessToken;
private String refreshToken;
}

@Getter
@Setter
@ToString
public static class PlaygroundMain {

private Long id;
private String name;
private Long generation;
private String profileImage;
private Boolean hasProfile;
private String accessToken;
private UserStatus status;
}

@Getter
@Setter
@ToString
public static class PlaygroundProfile {

private String name;
private String profileImage;
private List<PlaygroundActivity> activities;
}

@Getter
@Setter
@ToString
public static class PlaygroundActivity {

private String cardinalInfo;
private List<PlaygroundCardinalActivity> cardinalActivities;
}

@Getter
@Setter
@ToString
public static class PlaygroundCardinalActivity {

private Long id;
private Long generation;
private String team;
private String part;
private Boolean isProject;
}

@Getter
@Builder
@ToString
public static class MainView {

private MainViewUser user;
}

@Getter
@Builder
@ToString
public static class MainViewUser {

private UserStatus status;
private String name;
private String profileImage;
private List<Long> generationList;
}

@Getter
@Setter
@ToString
public static class AccessToken {

private String accessToken;
}

@Getter
@Setter
@ToString
public static class RefreshedToken {

private String accessToken;
private String errorCode;
}
}
Loading

0 comments on commit ae06ea0

Please sign in to comment.