Skip to content

Commit

Permalink
Merge pull request #32 from survey-mate/feat/25
Browse files Browse the repository at this point in the history
feat: 회원가입, 로그인 일부 구현 #25
  • Loading branch information
mingmingmon authored Jan 23, 2024
2 parents 3932094 + 39dc99d commit 6a7ccea
Show file tree
Hide file tree
Showing 36 changed files with 684 additions and 35 deletions.
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,18 @@ dependencies {
implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
implementation 'io.springfox:springfox-swagger2:3.0.0'
implementation 'io.springfox:springfox-boot-starter:3.0.0'
implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
implementation 'io.springfox:springfox-swagger-ui:3.0.0'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'io.projectreactor:reactor-test'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package uk.jinhy.survey_mate_api.auth.application.dto;

public class AuthServiceDTO {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package uk.jinhy.survey_mate_api.auth.application.service;

import com.amazonaws.services.kms.model.NotFoundException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import uk.jinhy.survey_mate_api.auth.domain.entity.Member;
import uk.jinhy.survey_mate_api.auth.domain.repository.MemberRepository;
import uk.jinhy.survey_mate_api.auth.presentation.dto.LoginControllerDTO;
import uk.jinhy.survey_mate_api.auth.presentation.dto.MemberControllerDTO;
import uk.jinhy.survey_mate_api.common.auth.AuthProvider;
import uk.jinhy.survey_mate_api.jwt.JwtTokenProvider;

@RequiredArgsConstructor
@Service
@Slf4j
public class AuthService {

@Qualifier("AuthenticationManager")
private final AuthenticationManager authenticationManager;

private final JwtTokenProvider jwtTokenProvider;

private final MemberRepository memberRepository;

private final PasswordEncoder passwordEncoder;

public Member join(MemberControllerDTO.MemberRequestDTO requestDTO){
Member member = Member.builder()
.memberId(requestDTO.getMemberId())
.nickname(requestDTO.getNickname())
.password(passwordEncoder.encode(requestDTO.getPassword()))
.messageConsent(requestDTO.isMessageConsent())
.marketingConsent(requestDTO.isMarketingConsent())
.point(0L)
.profileUrl(null)
.build();

memberRepository.save(member);
return member;
}

public String login(LoginControllerDTO requestDTO){
String id = requestDTO.getId();
String password = requestDTO.getPassword();

UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(id, password);

Authentication authentication = authenticationManager.authenticate(authenticationToken);
String jwtToken = jwtTokenProvider.createToken(authentication);

return jwtToken;

}

public Member getCurrentMember() {
String memberId = AuthProvider.getAuthenticationInfoMemberId();
return memberRepository.findById(memberId)
.orElseThrow(() -> new NotFoundException("해당 유저가 없습니다."));
}


}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uk.jinhy.survey_mate_api.member;
package uk.jinhy.survey_mate_api.auth.domain.entity;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
Expand Down Expand Up @@ -33,15 +33,11 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long memberId;
private String memberId;

@NotNull
private String nickname;

@NotNull
private String email;

@NotNull
private String password;

Expand All @@ -51,12 +47,6 @@ public class Member {
@NotNull
private boolean marketingConsent;

@NotNull
private String school;

@NotNull
private String yearOfAdmission;

private Long point;

private String profileUrl;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package uk.jinhy.survey_mate_api.auth.domain.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import uk.jinhy.survey_mate_api.auth.domain.entity.Member;

public interface MemberRepository extends JpaRepository<Member, String> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package uk.jinhy.survey_mate_api.auth.presentation;

import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import uk.jinhy.survey_mate_api.auth.application.service.AuthService;
import uk.jinhy.survey_mate_api.auth.domain.entity.Member;
import uk.jinhy.survey_mate_api.auth.presentation.dto.LoginControllerDTO;
import uk.jinhy.survey_mate_api.auth.presentation.dto.MemberControllerDTO;
import uk.jinhy.survey_mate_api.common.response.ApiResponse;
import uk.jinhy.survey_mate_api.common.response.Status;


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

@PostMapping("/join")
@Operation(summary = "회원가입")
public ApiResponse<?> join(MemberControllerDTO.MemberRequestDTO requestDTO){
Member member = authService.join(requestDTO);
return ApiResponse.onSuccess(Status.CREATED.getHttpStatus().toString(),
Status.CREATED.getMessage(), member.getMemberId());
}

@PostMapping("/login")
@Operation(summary = "로그인")
public ApiResponse<?> login(LoginControllerDTO requestDTO){
String jwtTokenInfo = authService.login(requestDTO);
return ApiResponse.onSuccess(Status.OK.getHttpStatus().toString(),
Status.OK.getMessage(), jwtTokenInfo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package uk.jinhy.survey_mate_api.auth.presentation.dto;

import lombok.Builder;
import lombok.Getter;

@Builder
@Getter
public class LoginControllerDTO {
private String id;

private String password;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package uk.jinhy.survey_mate_api.auth.presentation.dto;

import lombok.Builder;
import lombok.Getter;

public class MemberControllerDTO {
@Builder
@Getter
public static class MemberRequestDTO{
private String memberId;

private String nickname;

private String password;

private boolean messageConsent;

private boolean marketingConsent;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package uk.jinhy.survey_mate_api.common.auth;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;

public class AuthProvider {
public static User getAuthenticationInfo() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
throw new RuntimeException("인증정보가 없습니다.");
}
return (User) authentication.getPrincipal();
}

public static String getAuthenticationInfoMemberId() {
return getAuthenticationInfo().getUsername();
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,92 @@
package uk.jinhy.survey_mate_api.common.config;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import uk.jinhy.survey_mate_api.jwt.CustomAuthenticationProvider;
import uk.jinhy.survey_mate_api.jwt.JwtAccessDeniedHandler;
import uk.jinhy.survey_mate_api.jwt.JwtAuthenticationEntryPoint;
import uk.jinhy.survey_mate_api.jwt.UserDetailsServiceImpl;
import uk.jinhy.survey_mate_api.jwt.JwtAuthenticationFilter;
import uk.jinhy.survey_mate_api.jwt.JwtTokenProvider;

@RequiredArgsConstructor
@Configuration
public class SecurityConfig {

private final JwtTokenProvider jwtTokenProvider;

private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

private final JwtAccessDeniedHandler jwtAccessDeniedHandler;

private final UserDetailsServiceImpl userDetailsService;

private final String[] allowedUrls = {
"/",
"/swagger-ui/**",
"/v3/**",
"/auth/**",
"/error",
"/v2/api-docs",
"/swagger-resources/**",
"/v3/api-docs/**",
"/configuration/ui",
"/configuration/security",
"/webjars/**",
};

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public AuthenticationProvider authenticationProvider() {
return new CustomAuthenticationProvider(userDetailsService, passwordEncoder());
}

@Bean(name = "AuthenticationManager")
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(authenticationProvider()));
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {

httpSecurity
.authorizeHttpRequests(
auth -> { auth
.requestMatchers(allowedUrls).permitAll()
.anyRequest().hasRole("USER");
}
)
.cors(
cors -> cors.configurationSource(
corsConfigurationSource()
Expand All @@ -27,7 +96,12 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
.headers(httpSecurityHeadersConfigurer ->
httpSecurityHeadersConfigurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)
)
;
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(
exception -> exception.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.accessDeniedHandler(jwtAccessDeniedHandler)
);


return httpSecurity.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
package uk.jinhy.survey_mate_api.common.config;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import java.util.Arrays;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI SpringBootAPI() {

Info info = new Info()
.title("썰매 API 문서")
.description("썰매 스프링 부트 서버 API 문저")
.description("썰매 스프링 부트 서버 API 문서")
.version("1.0.0");

SecurityScheme securityScheme = new SecurityScheme()
.type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
.in(SecurityScheme.In.HEADER).name("Authorization");
SecurityRequirement securityRequirement = new SecurityRequirement().addList("bearerAuth");

return new OpenAPI()
.addServersItem(new Server().url("/"))
.info(info);
.info(info)
.components(new Components().addSecuritySchemes("bearerAuth", securityScheme))
.security(Arrays.asList(securityRequirement));
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ public enum Status {
FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."),
CONFLICT(HttpStatus.CONFLICT, "COMMON409", "이미 생성되었습니다."),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버에 오류가 발생했습니다."),

MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER404", "해당 아이디는 존재하지 않습니다."),
PASSWORD_INCORRECT(HttpStatus.BAD_REQUEST, "MEMBER400", "비밀번호가 틀렸습니다."),

JWT_NULL(HttpStatus.UNAUTHORIZED, "JWT401", "JWT가 NULL입니다."),
JWT_INVALID(HttpStatus.FORBIDDEN, "JWT404", "JWT가 유효하지 않습니다."),

;
private final HttpStatus httpStatus;
private final String code;
Expand Down
Loading

0 comments on commit 6a7ccea

Please sign in to comment.