Skip to content
This repository has been archived by the owner on Aug 28, 2024. It is now read-only.

Commit

Permalink
feat :: jwt
Browse files Browse the repository at this point in the history
  • Loading branch information
osoohynn committed Aug 26, 2024
1 parent 7125bd1 commit 6d26b49
Show file tree
Hide file tree
Showing 36 changed files with 954 additions and 17 deletions.
Binary file modified .DS_Store
Binary file not shown.
33 changes: 32 additions & 1 deletion crescendo-server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,43 @@ repositories {

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

// db
// runtimeOnly 'com.mysql:mysql-connector-j'
implementation group: 'org.postgresql', name: 'postgresql', version: '42.7.3'

// 편의
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-devtools'

// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'

// 유효성 검사
implementation 'org.springframework.boot:spring-boot-starter-validation'

// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// 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'

// test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

// spring AI
// implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter:1.0.0-SNAPSHOT'

// 소켓 통신
// implementation 'org.springframework.boot:spring-boot-starter-websocket'

}

tasks.named('test') {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@ConfigurationPropertiesScan
@EnableJpaAuditing
public class CrescendoServerApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.example.crescendoserver.domain.auth.controller;

import com.example.crescendoserver.domain.auth.dto.LoginRequest;
import com.example.crescendoserver.domain.auth.dto.ReissueRequest;
import com.example.crescendoserver.domain.auth.dto.SignUpRequest;
import com.example.crescendoserver.domain.auth.service.AuthService;
import com.example.crescendoserver.global.security.jwt.dto.Jwt;
import com.example.crescendoserver.domain.user.dto.UserResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;

@PostMapping("/signup")
public void signup(@RequestBody SignUpRequest request) {
authService.signup(request);
}

@PostMapping("/login")
public Jwt login(@RequestBody LoginRequest request) {
return authService.login(request);
}

@PostMapping("/reissue")
public Jwt reissue(@RequestBody ReissueRequest request) {
return authService.reissue(request);
}

@GetMapping("/me")
public UserResponse me() {
return authService.me();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.crescendoserver.domain.auth.dto;

public record LoginRequest(String username, String password) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.crescendoserver.domain.auth.dto;

public record ReissueRequest(
String refreshToken
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.crescendoserver.domain.auth.dto;

import jakarta.validation.constraints.NotBlank;

public record SignUpRequest(
@NotBlank
String username,
@NotBlank
String password
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.crescendoserver.domain.auth.repository;

public interface RefreshTokenRepository {
void save(String username, String refreshToken);

void deleteByUserName(String username);

String findByUsername(String username);

Boolean existsByUserName(String username);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.example.crescendoserver.domain.auth.repository;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class RefreshTokenRepositoryImpl implements RefreshTokenRepository {
private final RedisTemplate<String, String> redisTemplate;

@Override
public void save(String username, String refreshToken) {
redisTemplate.opsForValue().set("refreshToken:" + username, refreshToken);
}

@Override
public void deleteByUserName(String username) {
redisTemplate.delete("refreshToken:" + username);
}

@Override
public String findByUsername(String username) {
return redisTemplate.opsForValue().get("refreshToken:" + username);
}

@Override
public Boolean existsByUserName(String username) {
return redisTemplate.hasKey("refreshToken:" + username);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.example.crescendoserver.domain.auth.service;

import com.example.crescendoserver.domain.auth.dto.LoginRequest;
import com.example.crescendoserver.domain.auth.dto.ReissueRequest;
import com.example.crescendoserver.domain.auth.dto.SignUpRequest;
import com.example.crescendoserver.domain.user.dto.UserResponse;
import com.example.crescendoserver.global.security.jwt.dto.Jwt;

public interface AuthService {
void signup(SignUpRequest signUpRequest);

Jwt login(LoginRequest request);

Jwt reissue(ReissueRequest request);

UserResponse me();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.example.crescendoserver.domain.auth.service;

import com.example.crescendoserver.domain.auth.dto.LoginRequest;
import com.example.crescendoserver.domain.auth.dto.ReissueRequest;
import com.example.crescendoserver.domain.auth.dto.SignUpRequest;
import com.example.crescendoserver.domain.auth.repository.RefreshTokenRepository;
import com.example.crescendoserver.domain.user.domain.User;
import com.example.crescendoserver.domain.user.domain.UserRole;
import com.example.crescendoserver.domain.user.dto.UserResponse;
import com.example.crescendoserver.domain.user.repository.UserRepository;
import com.example.crescendoserver.global.exception.CustomErrorCode;
import com.example.crescendoserver.global.exception.CustomException;
import com.example.crescendoserver.global.security.jwt.dto.Jwt;
import com.example.crescendoserver.global.security.jwt.enums.JwtType;
import com.example.crescendoserver.global.security.jwt.provider.JwtProvider;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final RefreshTokenRepository refreshTokenRepository;
private final JwtProvider jwtProvider;

@Transactional
@Override
public void signup(SignUpRequest request) {
String username = request.username();
String password = request.password();

if (userRepository.existsByUsername(username)) throw new CustomException(CustomErrorCode.USERNAME_DUPLICATION);

User user = User.builder()
.username(username)
.password(passwordEncoder.encode(password))
.role(UserRole.USER)
.build();

userRepository.save(user);
}

@Transactional
@Override
public Jwt login(LoginRequest request) {
String username = request.username();
String password = request.password();

User user = userRepository.findByUsername(username)
.orElseThrow(() -> new CustomException(CustomErrorCode.USER_NOT_FOUND));

if (!passwordEncoder.matches(password, user.getPassword()))
throw new CustomException(CustomErrorCode.WRONG_PASSWORD);

Jwt token = jwtProvider.generateToken(username, user.getRole());

refreshTokenRepository.save(username, token.refreshToken());

return token;
}

@Override
public Jwt reissue(ReissueRequest request) {
String refreshToken = request.refreshToken();

if (jwtProvider.getType(refreshToken) != JwtType.REFRESH)
throw new CustomException(CustomErrorCode.INVALID_TOKEN_TYPE);

String username = jwtProvider.getSubject(refreshToken);

if (!refreshTokenRepository.existsByUserName(username))
throw new CustomException(CustomErrorCode.INVALID_REFRESH_TOKEN);

if (!refreshTokenRepository.findByUsername(username).equals(refreshToken))
throw new CustomException(CustomErrorCode.INVALID_REFRESH_TOKEN);


User user = userRepository.findByUsername(username)
.orElseThrow(() -> new CustomException(CustomErrorCode.USER_NOT_FOUND));

Jwt token = jwtProvider.generateToken(username, user.getRole());

refreshTokenRepository.save(username, token.refreshToken());

return token;
}

@Override
public UserResponse me() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();

User user = userRepository.findByUsername(username)
.orElseThrow(() -> new CustomException(CustomErrorCode.USER_NOT_FOUND));

return new UserResponse(user.getId(), user.getUsername(), user.getRole());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.example.crescendoserver.domain.user.domain;

import com.example.crescendoserver.global.common.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@Entity
@Table(name = "users")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SuperBuilder
public class User extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(unique = true, nullable = false)
private String username;

@Column(nullable = false)
private String password;

@Enumerated(EnumType.STRING)
private UserRole role;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.crescendoserver.domain.user.domain;

public enum UserRole {
USER,
ADMIN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.crescendoserver.domain.user.dto;

import com.example.crescendoserver.domain.user.domain.User;
import com.example.crescendoserver.domain.user.domain.UserRole;

public record UserResponse(
Long id,
String username,
UserRole role) {

public static UserResponse of(User user) {
return new UserResponse(user.getId(), user.getUsername(), user.getRole());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.crescendoserver.domain.user.repository;

import com.example.crescendoserver.domain.user.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);

boolean existsByUsername(String username);
}
Loading

0 comments on commit 6d26b49

Please sign in to comment.