Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3814309
1st
sooooooobinn Sep 25, 2025
7ddc8ca
였λ₯˜ μˆ˜μ •
sooooooobinn Sep 25, 2025
4dddf69
λΉ„λ°€λ²ˆν˜Έ 쑰건 μΆ”κ°€
sooooooobinn Sep 28, 2025
8295c7d
μ—­ν•  선택 κΈ°λŠ₯ μΆ”κ°€
sooooooobinn Oct 16, 2025
2b31fc2
μ—­ν•  선택 κΈ°λŠ₯ μ—°κ²°
sooooooobinn Oct 17, 2025
bd4d19c
전곡,ν”„λ‘œν•„ κΈ°λŠ₯ μΆ”κ°€
sooooooobinn Oct 18, 2025
a122c66
μˆ˜μ •
sooooooobinn Oct 27, 2025
af923a7
μˆ˜μ •
sooooooobinn Oct 27, 2025
0c4d7d6
1μ°¨ μˆ˜μ •(였λ₯˜ μˆ˜μ • ν•„μš”)
sooooooobinn Nov 25, 2025
d3f21f4
νšŒμ›κ°€μž… 둜직 κ΅¬ν˜„
sooooooobinn Nov 26, 2025
7a52b46
Login Filter κ΅¬ν˜„
sooooooobinn Nov 26, 2025
f3dbd38
customUserDetails μΆ”κ°€
sooooooobinn Nov 27, 2025
2e3eed6
LoginFilter μˆ˜μ •
sooooooobinn Nov 27, 2025
5e231ad
JWTFilter μΆ”κ°€
sooooooobinn Nov 28, 2025
5891e87
CORS μ„€μ • μΆ”κ°€
sooooooobinn Dec 1, 2025
dd65475
둜그인 였λ₯˜ μˆ˜μ •
sooooooobinn Dec 2, 2025
3f543e3
였λ₯˜ μˆ˜μ •
sooooooobinn Dec 2, 2025
ea4b581
fix: 둜그인 였λ₯˜ μˆ˜μ •
sooooooobinn Dec 2, 2025
99ad9bf
Merge origin/main into feat/user
sooooooobinn Dec 2, 2025
6d9f2a7
refactor: classNumber type λ³€κ²½
sooooooobinn Dec 3, 2025
e5cda72
refactor: 토큰 λ§Œλ£Œμ‹œκ°„ μž¬μ„€μ •
sooooooobinn Dec 3, 2025
b063876
refactor: application.properties μˆ˜μ •
sooooooobinn Dec 3, 2025
2270094
refector: application.properties μˆ˜μ •
sooooooobinn Dec 3, 2025
4d11056
Resolve merge conflicts in application.properties
sooooooobinn Dec 3, 2025
97b3529
refector: ν™˜κ²½λ³€μˆ˜ 였λ₯˜ μˆ˜μ •
sooooooobinn Dec 3, 2025
b2f5b9a
feat: LoginController μΆ”κ°€
sooooooobinn Dec 3, 2025
af4f22d
feat: LoginDto μΆ”κ°€
sooooooobinn Dec 3, 2025
ffb28d2
refactor: securityConfig 파일 μˆ˜μ •
sooooooobinn Dec 3, 2025
e2563ba
refactor: LoginController 파일 μˆ˜μ •
sooooooobinn Dec 3, 2025
72d7a73
feat: form-data -> JSON λ³€κ²½
sooooooobinn Dec 3, 2025
b826455
refactor: entity κ°’ μˆ˜μ •
sooooooobinn Dec 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ plugins {
group = 'com.example'
version = '0.0.1-SNAPSHOT'
description = 'NODE'

java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
Expand All @@ -27,6 +26,14 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-test'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3'
implementation 'com.mysql:mysql-connector-j'
runtimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand All @@ -35,4 +42,4 @@ dependencies {

tasks.named('test') {
useJUnitPlatform()
}
}
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1 +1 @@
rootProject.name = 'NODE'
rootProject.name = 'NODE'
13 changes: 13 additions & 0 deletions src/main/java/com/example/nodelogin/NodeLoginApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.nodelogin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class NodeLoginApplication {

public static void main(String[] args) {
SpringApplication.run(NodeLoginApplication.class, args);
}

}
103 changes: 103 additions & 0 deletions src/main/java/com/example/nodelogin/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.example.nodelogin.config;

import com.example.nodelogin.jwt.JWTFilter;
import com.example.nodelogin.jwt.JWTUtil;
import com.example.nodelogin.jwt.LoginFilter;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

import java.util.Collections;

import static com.fasterxml.jackson.databind.type.LogicalType.Collection;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

private final AuthenticationConfiguration authenticationConfiguration;

private final JWTUtil jwtUtil;

public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JWTUtil jwtUtil) {
this.authenticationConfiguration = authenticationConfiguration;
this.jwtUtil = jwtUtil;
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}

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

http
.cors((cors) -> cors
.configurationSource(new CorsConfigurationSource() {

@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {

CorsConfiguration configuration = new CorsConfiguration();

configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setMaxAge(3600L);

configuration.setExposedHeaders(Collections.singletonList("Authorization"));

return configuration;
}
}));

http
.csrf(auth -> auth.disable());

http
.formLogin((auth) -> auth.disable());

http
.httpBasic((auth) -> auth.disable());

http
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/user/login", "/", "/user/join").permitAll()
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated());

http
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

http.addFilterAt(
new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil),
UsernamePasswordAuthenticationFilter.class
);


http.addFilterBefore(
new JWTFilter(jwtUtil),
UsernamePasswordAuthenticationFilter.class
);

return http.build();
}
}
60 changes: 60 additions & 0 deletions src/main/java/com/example/nodelogin/jwt/JWTFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.example.nodelogin.jwt;

import com.example.nodelogin.user.dto.CustomUserDetails;
import com.example.nodelogin.user.entity.UserEntity;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

public class JWTFilter extends OncePerRequestFilter {

private final JWTUtil jwtUtil;

public JWTFilter(JWTUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws java.io.IOException, jakarta.servlet.ServletException {

String authorization = request.getHeader("Authorization");

if (authorization == null || !authorization.startsWith("Bearer ")) {

System.out.println("token null");
filterChain.doFilter(request, response);
return;

}

String token = authorization.split(" ")[1];

if (jwtUtil.isExpired(token)) {
System.out.println("token expired");
filterChain.doFilter(request, response);

return;
}

String username = jwtUtil.getUsername(token);
String role = jwtUtil.getRole(token);

UserEntity userEntity = new UserEntity();
userEntity.setUsername(username);
userEntity.setPassword("temppassword");
userEntity.setRole(UserEntity.Role.valueOf(role));

CustomUserDetails customUserDetails = new CustomUserDetails(userEntity);

Authentication authToken = new UsernamePasswordAuthenticationToken(customUserDetails, null, customUserDetails.getAuthorities());

SecurityContextHolder.getContext().setAuthentication(authToken);

filterChain.doFilter(request, response);
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/example/nodelogin/jwt/JWTUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example.nodelogin.jwt;

import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Date;

@Component
public class JWTUtil {

private SecretKey secretKey;
private Long accessExpiration;

public JWTUtil(@Value("${JWT_SECRET}")String secret, @Value("${JWT_ACCESS_EXPIRATION}") Long accessExpiration) {
this.secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm());
this.accessExpiration = accessExpiration;
}

public String getUsername(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("username", String.class);
}

public String getEmail(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("email", String.class);
}

public String getRole(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class);
}

public boolean isExpired(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date());
}

public String createJwt(UserDetails userDetails) {
String username = userDetails.getUsername();
String role = userDetails.getAuthorities().iterator().next().getAuthority();
return createJwt(username, role, accessExpiration);
}

public String createJwt(String email, String role, Long expiredMs) {
return Jwts.builder()
.claim("email", email)
.claim("role", role)
.issuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiredMs))
.signWith(secretKey)
.compact();
}
}
73 changes: 73 additions & 0 deletions src/main/java/com/example/nodelogin/jwt/LoginFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.example.nodelogin.jwt;

import com.example.nodelogin.user.dto.CustomUserDetails;
import com.example.nodelogin.user.dto.LoginDto;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;

public class LoginFilter extends UsernamePasswordAuthenticationFilter {

private final AuthenticationManager authenticationManager;

private final JWTUtil jwtUtil;

public LoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) {

this.authenticationManager = authenticationManager;
this.jwtUtil = jwtUtil;

setFilterProcessesUrl("/user/login");

}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

try{
LoginDto loginDto = new ObjectMapper().readValue(request.getInputStream(), LoginDto.class);

UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(loginDto.getEmail(), loginDto.getPassword());

return authenticationManager.authenticate(authToken);
}catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {

CustomUserDetails customUserDetails = (CustomUserDetails)authentication.getPrincipal();

String username = customUserDetails.getUsername();

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
GrantedAuthority auth = iterator.next();

String role = auth.getAuthority();

String token = jwtUtil.createJwt(customUserDetails);

response.addHeader("Authorization", "Bearer " + token);
}

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {

response.setStatus(401);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.nodelogin.user.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;


@Controller
public class AdminController {

@PostMapping("/admin")
public String adminP() {
return "admin Controller";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.nodelogin.user.controller;

import com.example.nodelogin.user.dto.JoinDto;
import com.example.nodelogin.user.service.JoinService;
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("/user")
public class JoinController {

private final JoinService joinService;

public JoinController(JoinService joinService) {
this.joinService = joinService;
}

@PostMapping("/join")
public String join(@RequestBody JoinDto joinDto) {
joinService.joinProcess(joinDto);
return "ok";
}
}
Loading