Skip to content

Commit

Permalink
feat: Login, Signup, getToken logic
Browse files Browse the repository at this point in the history
  • Loading branch information
dami0806 committed Jun 4, 2024
1 parent bea6d94 commit 9bb76c7
Show file tree
Hide file tree
Showing 22 changed files with 708 additions and 1 deletion.
9 changes: 9 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,22 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'mysql:mysql-connector-java:8.0.32'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'


// JWT
compileOnly group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.sparta.oneandzerobest.auth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class PasswordConfig { // passwordConfigg

@Bean
public PasswordEncoder passwordEncoder() { // passwordEncoder로 빈 등록
//BCrypt: 해시함수: 비밀번호 암호화 -> BCryptPasswordEncoder 암호화
return new BCryptPasswordEncoder(); // PasswordEncoder는 인터페이스로 주입받음 -> BCryptPasswordEncoder구현체


}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.sparta.oneandzerobest.auth.config;

import com.sparta.oneandzerobest.auth.filter.JwtAuthenticationFilter;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
/**
* SecurityConfig- Spring Security 설정
*/
public class SecurityConfig {

private final JwtAuthenticationFilter jwtAuthenticationFilter;

public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}

/**
* SecurityFilterChain: 보안 설정을 기반으로 SecurityFilterChain 생성
* @param http : HttpSecurity객체로, CSRF 비활성화, 세션 관리, 요청 권한, 필터 추가
* @return SecurityFilterChain객체 생성(http 요청의 보안 규칙이 적용된)
* @throws Exception
*/
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// CSRF 설정
http.csrf((csrf) -> csrf.disable()); //csrf 비활성화

http.sessionManagement((sessionManagement) ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // 세션 관리를 stateless설정

// 요청에 대한 권한 설정
http.authorizeHttpRequests(authorizeHttpRequests ->
authorizeHttpRequests
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // resources 접근 허용 설정
.requestMatchers("/", "/index.html", "/login.html", "/signup.html", "/api/auth/**").permitAll() // 특정 경로 접근 허용
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() // Swagger 접근 허용
.requestMatchers(HttpMethod.GET, "/api/schedules/**").permitAll() // 일정 조회는 모두 허용
.anyRequest().permitAll() // 그 외 모든 요청 접근 허용
//.anyRequest().authenticated() // 그 외 모든 요청 인증처리
);

// jwtAuthenticationFilter의 순서를 지정해주기위해 UsernamePasswordAuthenticationFilter전으로 위치 지정
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.sparta.oneandzerobest.auth.controller;


import com.sparta.oneandzerobest.auth.dto.LoginRequestDto;
import com.sparta.oneandzerobest.auth.dto.RefreshTokenRequestDto;
import com.sparta.oneandzerobest.auth.dto.SignupRequestDto;
import com.sparta.oneandzerobest.auth.dto.TokenResponseDto;
import com.sparta.oneandzerobest.auth.entity.LoginRequest;
import com.sparta.oneandzerobest.auth.entity.SignupRequest;
import com.sparta.oneandzerobest.auth.service.UserService;
import com.sparta.oneandzerobest.auth.util.JwtUtil;
import org.springframework.http.ResponseEntity;
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("/api/auth")
public class AuthRestController {
private final UserService userService;
private final JwtUtil jwtUtil;

public AuthRestController(UserService userService, JwtUtil jwtUtil) {
this.userService = userService;
this.jwtUtil = jwtUtil;
}

@PostMapping("/signup")
public ResponseEntity<String> signup(@RequestBody SignupRequestDto signupRequestDto) {
SignupRequest signupRequest = new SignupRequest(
signupRequestDto.getUsername(),
signupRequestDto.getPassword(),
signupRequestDto.getEmail(),
signupRequestDto.isAdmin(),
signupRequestDto.getAdminToken()
);
userService.signup(signupRequest);
return ResponseEntity.ok("회원가입 성공");
}

@PostMapping("/login")
public ResponseEntity<TokenResponseDto> login(@RequestBody LoginRequestDto loginRequestDto) {
LoginRequest loginRequest = new LoginRequest(
loginRequestDto.getUsername(),
loginRequestDto.getPassword()
);

String token = userService.login(loginRequest);
String refreshToken = jwtUtil.createRefreshToken(loginRequestDto.getUsername());

TokenResponseDto tokenResponseDto = new TokenResponseDto(token, refreshToken);
return ResponseEntity.ok(tokenResponseDto);
}

@PostMapping("/refresh")
public ResponseEntity<TokenResponseDto> refresh(@RequestBody RefreshTokenRequestDto refreshTokenRequestDto) {

String refreshToken = refreshTokenRequestDto.getRefreshToken();
String newAccessToken = jwtUtil.refreshToken(refreshToken);
TokenResponseDto tokenResponseDto = new TokenResponseDto(newAccessToken, refreshToken);
return ResponseEntity.ok(tokenResponseDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sparta.oneandzerobest.auth.dto;

import lombok.Getter;

@Getter
public class LoginRequestDto {
private String username;
private String password;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sparta.oneandzerobest.auth.dto;

import lombok.Getter;

@Getter
public class RefreshTokenRequestDto {
private String refreshToken;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sparta.oneandzerobest.auth.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class SignupRequestDto {
private String username;
private String password;
private String email;
private boolean admin = false;
private String adminToken = "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.sparta.oneandzerobest.auth.dto;


import lombok.Getter;

@Getter
public class TokenResponseDto {
private String accessToken;
private String refreshToken;

public TokenResponseDto(String accessToken, String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.sparta.oneandzerobest.auth.entity;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class LoginRequest {
private String username;
private String password;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sparta.oneandzerobest.auth.entity;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class SignupRequest {
private String username;
private String password;
private String email;
private boolean isAdmin;
private String adminToken;
}
77 changes: 77 additions & 0 deletions src/main/java/com/sparta/oneandzerobest/auth/entity/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.sparta.oneandzerobest.auth.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

@Entity
@Getter
@NoArgsConstructor
@Table(name = "users")
public class User implements UserDetails { // Spring Security의 UserDetails
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

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

@Column(nullable = false)
private String password;

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


@ElementCollection(fetch = FetchType.EAGER)
private Set<String> authorities;

@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserRoleEnum role;

public User(String username, String password, String email, UserRoleEnum role) {
this.username = username;
this.password = password;
this.email = email;
this.role = role;
this.authorities = Collections.singleton(role.name());
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (String authority : this.authorities) {
grantedAuthorities.add(() -> authority);
}
return grantedAuthorities;
}


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

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

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

@Override
public boolean isEnabled() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.sparta.oneandzerobest.auth.entity;

import org.springframework.security.core.GrantedAuthority;

public enum UserRoleEnum implements GrantedAuthority {
USER(Authority.USER), // 사용자 권한
ADMIN(Authority.ADMIN); // 관리자 권한

private final String authority;

UserRoleEnum(String authority) {
this.authority = authority;
}

@Override
public String getAuthority() {
return name();
}

public static class Authority {
public static final String USER = "ROLE_USER";
public static final String ADMIN = "ROLE_ADMIN";
}
}

Loading

0 comments on commit 9bb76c7

Please sign in to comment.