diff --git a/pom.xml b/pom.xml index 58faca1..b3d201c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,91 +1,118 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.4.5 - - - co.jp.app - dog-2 - 0.0.1-SNAPSHOT - dog-1 - dog introduce project for Spring Boot - - - - - - - - - - - - - - - 17 - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.4.5 + + + co.jp.app + dog-2 + 0.0.1-SNAPSHOT + dog-1 + dog introduce project for Spring Boot + + + + + + + + + + + + + + + 17 + + - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jpa - - - com.mysql - mysql-connector-j - runtime - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-security - - - com.fasterxml.jackson.core - jackson-databind - - + + org.springframework.boot + spring-boot-starter-web + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.mysql + mysql-connector-j + runtime + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-security + + + com.fasterxml.jackson.core + jackson-databind + + + org.jetbrains + annotations + 13.0 + compile + + - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.projectlombok - lombok - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + \ No newline at end of file diff --git a/src/main/java/co/jp/app/common/ApiResponse.java b/src/main/java/co/jp/app/common/ApiResponse.java new file mode 100644 index 0000000..33bbe4f --- /dev/null +++ b/src/main/java/co/jp/app/common/ApiResponse.java @@ -0,0 +1,61 @@ +package co.jp.app.common; + +import java.util.Objects; + +public class ApiResponse { + private boolean success; + private String message; + private T data; + + public static ApiResponse success(T data) { + + return new ApiResponse<>(true, null, data); + } + + public static ApiResponse fail(String message) { + + return new ApiResponse<>(false, message, null); + } + + public ApiResponse() { + } + + public ApiResponse(boolean success, String message, T data) { + this.success = success; + this.message = message; + this.data = data; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + @Override + public String toString() { + return "ApiResponse{" + + "success=" + success + + ", message='" + message + '\'' + + ", data=" + data + + '}'; + } +} diff --git a/src/main/java/co/jp/app/config/CorsConfig.java b/src/main/java/co/jp/app/config/CorsConfig.java new file mode 100644 index 0000000..a28c48b --- /dev/null +++ b/src/main/java/co/jp/app/config/CorsConfig.java @@ -0,0 +1,18 @@ +package co.jp.app.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/api/**") // 允许 /api/ 下的所有请求 + .allowedOrigins("http://192.168.1.50:5173") // 允许来自该域的请求 + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的 HTTP 方法 + .allowedHeaders("*") // 允许所有头部 + .allowCredentials(true); // 允许发送 Cookie + } +} diff --git a/src/main/java/co/jp/app/config/SecurityConfig.java b/src/main/java/co/jp/app/config/SecurityConfig.java deleted file mode 100644 index 5a7fcb2..0000000 --- a/src/main/java/co/jp/app/config/SecurityConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package co.jp.app.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 SecurityConfig { - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} diff --git a/src/main/java/co/jp/app/config/security/SecurityConfig.java b/src/main/java/co/jp/app/config/security/SecurityConfig.java new file mode 100644 index 0000000..131727c --- /dev/null +++ b/src/main/java/co/jp/app/config/security/SecurityConfig.java @@ -0,0 +1,63 @@ +package co.jp.app.config.security; + +import co.jp.app.config.security.filter.JwtAuthenticationFilter; +import co.jp.app.service.UserService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +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.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +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.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +public class SecurityConfig { + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final UserDetailsService userDetailsService; + + public SecurityConfig(@Lazy JwtAuthenticationFilter jwtAuthenticationFilter, @Lazy UserDetailsService userDetailsService) { + this.jwtAuthenticationFilter = jwtAuthenticationFilter; + this.userDetailsService = userDetailsService; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public AuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { + return authenticationConfiguration.getAuthenticationManager(); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf(AbstractHttpConfigurer::disable) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/user/login", "/api/user/register").permitAll() + .anyRequest().authenticated() + ) + .authenticationProvider(authenticationProvider()) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } +} diff --git a/src/main/java/co/jp/app/config/security/filter/JwtAuthenticationFilter.java b/src/main/java/co/jp/app/config/security/filter/JwtAuthenticationFilter.java new file mode 100644 index 0000000..7102e75 --- /dev/null +++ b/src/main/java/co/jp/app/config/security/filter/JwtAuthenticationFilter.java @@ -0,0 +1,60 @@ +package co.jp.app.config.security.filter; + +import co.jp.app.service.JwtService; +import java.io.IOException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.context.annotation.Lazy; +import org.springframework.lang.NonNull; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + + +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtService jwtService; + private final UserDetailsService userDetailsService; + + public JwtAuthenticationFilter(JwtService jwtService, UserDetailsService userDetailsService) { + this.jwtService = jwtService; + this.userDetailsService = userDetailsService; + } + + @Override + protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) + throws ServletException, IOException { + + final String authHeader = request.getHeader("Authorization"); + final String jwt; + final String username; + + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + + jwt = authHeader.substring(7); + username = jwtService.extractUsername(jwt); + + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); + if (jwtService.isTokenValid(jwt, userDetails)) { + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } + + filterChain.doFilter(request, response); + } +} \ No newline at end of file diff --git a/src/main/java/co/jp/app/controller/LoginController.java b/src/main/java/co/jp/app/controller/LoginController.java deleted file mode 100644 index c54bcbd..0000000 --- a/src/main/java/co/jp/app/controller/LoginController.java +++ /dev/null @@ -1,37 +0,0 @@ -package co.jp.app.controller; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import co.jp.app.entity.ErrorEntity; -import co.jp.app.entity.UserEntity; -import co.jp.app.service.ErraService; -import co.jp.app.service.UserService; - - -@CrossOrigin("http://192.168.1.50:5173") -@RestController("/api/login") -public class LoginController { - - @Autowired - private UserService userService; - - @Autowired - private ErraService erraService; - - @GetMapping("/status") - public String getStatusByNameOrEmail() { - String input="aaa"; - - if (userByName == null && userByEmail == null) { - return "全項目に入力してください"; - } - - // 如果有找到,就固定使用 ID 1001 去查 erraEntity - ErrorEntity erra = erraService.getStatusById(1001); - - return erra.getStatus(); - } -} diff --git a/src/main/java/co/jp/app/controller/PetController.java b/src/main/java/co/jp/app/controller/PetController.java index 530a677..8be5908 100644 --- a/src/main/java/co/jp/app/controller/PetController.java +++ b/src/main/java/co/jp/app/controller/PetController.java @@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.RequestParam; import co.jp.app.service.PetService; - @Controller public class PetController { @@ -19,7 +18,7 @@ public class PetController { @GetMapping("/api/dogs/pet") public String getListByEntities(@RequestParam List id) { - service.getPetByID(id); + service.getPetByID(id); return "pet"; } } diff --git a/src/main/java/co/jp/app/controller/UploadController.java b/src/main/java/co/jp/app/controller/UploadController.java index c76027a..1ef704a 100644 --- a/src/main/java/co/jp/app/controller/UploadController.java +++ b/src/main/java/co/jp/app/controller/UploadController.java @@ -9,20 +9,19 @@ import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import co.jp.app.entity.PetEntity; -import co.jp.app.service.UploadService; @CrossOrigin("http://192.168.1.50:5173") @Controller public class UploadController { @Autowired - private UploadService service; + //private UploadService service; @PostMapping("/api/dogs/upload") public String upload() { List list = new ArrayList(); - service.saveAll(list); + //service.saveAll(list); return "upload"; diff --git a/src/main/java/co/jp/app/controller/UserController.java b/src/main/java/co/jp/app/controller/UserController.java new file mode 100644 index 0000000..803c305 --- /dev/null +++ b/src/main/java/co/jp/app/controller/UserController.java @@ -0,0 +1,77 @@ +package co.jp.app.controller; + +import co.jp.app.common.ApiResponse; +import co.jp.app.dto.LoginDto; +import co.jp.app.dto.RegistrationDto; +import co.jp.app.service.JwtService; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.*; + +import co.jp.app.entity.ErrorEntity; +import co.jp.app.entity.UserEntity; +import co.jp.app.service.ErraService; +import co.jp.app.service.UserService; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/api/user") +public class UserController { + + private final UserService userService; + private final AuthenticationManager authenticationManager; + private final JwtService jwtService; + + public UserController(UserService userService, AuthenticationManager authenticationManager, JwtService jwtService) { + this.userService = userService; + this.authenticationManager = authenticationManager; + this.jwtService = jwtService; + } + + @PostMapping("/register") + public ResponseEntity registerUser(@Valid @RequestBody RegistrationDto registrationDto) { + try { + + UserEntity registeredUser = userService.registerNewUser(registrationDto); + + return ResponseEntity.status(HttpStatus.CREATED).body(ApiResponse.success(registeredUser.getEmail())); + } catch (Exception e) { + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.fail("ユーザー登録失敗しました。")); + } + } + + @PostMapping("/login") + public ResponseEntity authenticateUser(@Valid @RequestBody LoginDto loginDto) { + try { + Authentication authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(loginDto.getEmail(), loginDto.getPassword()) + ); + SecurityContextHolder.getContext().setAuthentication(authentication); + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + + String jwtToken = jwtService.generateToken(userDetails); // 生成单一的Token + + Map tokenResponse = new HashMap<>(); + tokenResponse.put("token", jwtToken); + + return ResponseEntity.ok(ApiResponse.success(tokenResponse)); + + } catch (BadCredentialsException e) { + + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ApiResponse.fail("メールアドレスまたはパスワードが間違っています。")); + } catch (Exception e) { + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.fail("サーバーエラー。")); + } + } +} diff --git a/src/main/java/co/jp/app/dto/LoginDto.java b/src/main/java/co/jp/app/dto/LoginDto.java new file mode 100644 index 0000000..d288aa1 --- /dev/null +++ b/src/main/java/co/jp/app/dto/LoginDto.java @@ -0,0 +1,24 @@ +package co.jp.app.dto; + +public class LoginDto { + + private String email; + + private String password; + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/src/main/java/co/jp/app/dto/RegistrationDto.java b/src/main/java/co/jp/app/dto/RegistrationDto.java new file mode 100644 index 0000000..e93533f --- /dev/null +++ b/src/main/java/co/jp/app/dto/RegistrationDto.java @@ -0,0 +1,34 @@ +package co.jp.app.dto; + +public class RegistrationDto { + + private String name; + + private String email; + + private String password; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/src/main/java/co/jp/app/entity/PetEntity.java b/src/main/java/co/jp/app/entity/PetEntity.java index 8b8f9ed..b10d7d2 100644 --- a/src/main/java/co/jp/app/entity/PetEntity.java +++ b/src/main/java/co/jp/app/entity/PetEntity.java @@ -5,10 +5,11 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; + @Entity @Table(name = "Pet") -public class PetEntity{ -@GeneratedValue(strategy = GenerationType.IDENTITY) +public class PetEntity { + @GeneratedValue(strategy = GenerationType.IDENTITY) @Id private int ID; @@ -27,15 +28,15 @@ public class PetEntity{ //犬の健康状態 private String status; //犬の圖片 - private String image; - - public String getImage() { - return image; - } + private String image; - public void setImage(String image) { - this.image = image; - } + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } public String getName() { return name; @@ -92,6 +93,7 @@ public class PetEntity{ public void setStatus(String status) { this.status = status; } + public int getID() { return ID; } @@ -99,5 +101,5 @@ public class PetEntity{ public void setID(int iD) { ID = iD; } - + } diff --git a/src/main/java/co/jp/app/repository/ErraRepository.java b/src/main/java/co/jp/app/repository/ErrRepository.java similarity index 77% rename from src/main/java/co/jp/app/repository/ErraRepository.java rename to src/main/java/co/jp/app/repository/ErrRepository.java index 18dc5ee..336cea5 100644 --- a/src/main/java/co/jp/app/repository/ErraRepository.java +++ b/src/main/java/co/jp/app/repository/ErrRepository.java @@ -7,9 +7,10 @@ import org.springframework.data.repository.query.Param; import co.jp.app.entity.ErrorEntity; -public interface ErraRepository extends JpaRepository{ +public interface ErrRepository extends JpaRepository{ public default ErrorEntity getById(@Param("id") int id) { + return getById(id); } } \ No newline at end of file diff --git a/src/main/java/co/jp/app/repository/PetRepository.java b/src/main/java/co/jp/app/repository/PetRepository.java index 9c5b065..ecb3674 100644 --- a/src/main/java/co/jp/app/repository/PetRepository.java +++ b/src/main/java/co/jp/app/repository/PetRepository.java @@ -1,6 +1,5 @@ package co.jp.app.repository; - import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; @@ -9,10 +8,8 @@ import co.jp.app.entity.PetEntity; public interface PetRepository extends JpaRepository { -@Override -default List findAllById(Iterable id) { - return findAllById(id); + @Override + default List findAllById(Iterable id) { + return findAllById(id); } } - - diff --git a/src/main/java/co/jp/app/service/ErraService.java b/src/main/java/co/jp/app/service/ErraService.java index 239ab18..1d3f019 100644 --- a/src/main/java/co/jp/app/service/ErraService.java +++ b/src/main/java/co/jp/app/service/ErraService.java @@ -4,14 +4,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import co.jp.app.entity.ErrorEntity; -import co.jp.app.repository.ErraRepository; +import co.jp.app.repository.ErrRepository; @Service public class ErraService { @Autowired -ErraRepository erraRepository; +ErrRepository erraRepository; public ErrorEntity getStatusById(int id) { return erraRepository.getById(id); diff --git a/src/main/java/co/jp/app/service/JwtService.java b/src/main/java/co/jp/app/service/JwtService.java new file mode 100644 index 0000000..8f68ae2 --- /dev/null +++ b/src/main/java/co/jp/app/service/JwtService.java @@ -0,0 +1,106 @@ +package co.jp.app.service; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; +import java.util.function.Function; + +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; + +import java.security.Key; +import java.util.Base64; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Service +public class JwtService { + + private static final Logger logger = LoggerFactory.getLogger(JwtService.class); + //log + + @Value("${jwt.secret}") + private String secretKey; + + @Value("${jwt.token-expiration-ms}") + private long tokenExpirationMs; + + @org.jetbrains.annotations.NotNull + private Key getSignKey() { + byte[] keyBytes = Base64.getDecoder().decode(secretKey); + return Keys.hmacShaKeyFor(keyBytes); + } + + public String extractUsername(String token) { + try { + return extractClaim(token, Claims::getSubject); + } catch (ExpiredJwtException e) { + logger.warn("JWT token is expired when extracting username: {}", e.getMessage()); + return e.getClaims().getSubject(); + } catch (MalformedJwtException | SignatureException | UnsupportedJwtException | IllegalArgumentException e) { + logger.error("Invalid JWT token when extracting username: {}", e.getMessage()); + return null; + } + } + + public T extractClaim(String token, @NotNull Function claimsResolver) { + final Claims claims = extractAllClaims(token); + return claimsResolver.apply(claims); + } + + private Claims extractAllClaims(String token) { + return Jwts + .parserBuilder() + .setSigningKey(getSignKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + public String generateToken(@NotNull UserDetails userDetails) { + Map claims = new HashMap<>(); + return createToken(claims, userDetails.getUsername()); + } + + private String createToken(Map claims, String subject) { + return Jwts.builder() + .setClaims(claims) + .setSubject(subject) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + tokenExpirationMs)) // 使用配置的过期时间 + .signWith(getSignKey(), SignatureAlgorithm.HS256) + .compact(); + } + + public boolean isTokenValid(String token, UserDetails userDetails) { + try { + final String username = extractUsername(token); + if (username == null) { + return false; + } + + return (username.equals(userDetails.getUsername()) && !isTokenActuallyExpired(token)); + } catch (ExpiredJwtException e) { + + logger.warn("Token validation failed: Expired JWT - {}", e.getMessage()); + return false; + } catch (MalformedJwtException | SignatureException | UnsupportedJwtException | IllegalArgumentException e) { + + logger.error("Token validation failed: Invalid JWT (format, signature, etc.) - {}", e.getMessage()); + return false; + } + } + + private boolean isTokenActuallyExpired(String token) { + return extractExpiration(token).before(new Date()); + } + + private Date extractExpiration(String token) { + return extractClaim(token, Claims::getExpiration); + } +} diff --git a/src/main/java/co/jp/app/service/PetService.java b/src/main/java/co/jp/app/service/PetService.java index 97d6c7c..a820cda 100644 --- a/src/main/java/co/jp/app/service/PetService.java +++ b/src/main/java/co/jp/app/service/PetService.java @@ -8,7 +8,6 @@ import org.springframework.stereotype.Service; import co.jp.app.entity.PetEntity; import co.jp.app.repository.PetRepository; - @Service public class PetService { @Autowired @@ -17,6 +16,6 @@ public class PetService { public List getPetByID(Iterable id) { return dao.findAllById(id); - + } } diff --git a/src/main/java/co/jp/app/service/UploadService.java b/src/main/java/co/jp/app/service/UploadService.java deleted file mode 100644 index d1fd207..0000000 --- a/src/main/java/co/jp/app/service/UploadService.java +++ /dev/null @@ -1,23 +0,0 @@ -package co.jp.app.service; - -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import co.jp.app.entity.PetEntity; -import co.jp.app.repository.UploadRepository; - -@Service -public class UploadService { - - @Autowired - private UploadRepository uploadDao; - - public List saveAll(Iterable entities) { - - return uploadDao.saveAll(entities); - - } - -} diff --git a/src/main/java/co/jp/app/service/UserService.java b/src/main/java/co/jp/app/service/UserService.java index 1f4807e..f52d7db 100644 --- a/src/main/java/co/jp/app/service/UserService.java +++ b/src/main/java/co/jp/app/service/UserService.java @@ -1,5 +1,6 @@ package co.jp.app.service; +<<<<<<< HEAD import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -8,10 +9,33 @@ import co.jp.app.entity.UserEntity; import co.jp.app.repository.userRepository; import jakarta.transaction.Transactional; +======= +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import co.jp.app.dto.RegistrationDto; +import co.jp.app.entity.UserEntity; +import co.jp.app.repository.UserRepository; +import org.jetbrains.annotations.NotNull; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import co.jp.app.entity.PetEntity; +import co.jp.app.repository.UploadRepository; +import org.springframework.transaction.annotation.Transactional; +>>>>>>> 34de018ef0606dec4f6de48c8cc5c3f073a1fdc6 @Service -public class UserService { +public class UserService implements UserDetailsService { +<<<<<<< HEAD private final userRepository userEntityRepository; @Autowired @@ -20,35 +44,47 @@ public class UserService { this.userEntityRepository = userEntityRepository; this.passwordEncoder= passwordEncoder; +======= + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + + public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) { + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; +>>>>>>> 34de018ef0606dec4f6de48c8cc5c3f073a1fdc6 } - @Transactional // 整个注册过程应该是一个事务 - public UserEntity registerNewUser(String name, String email, String rawPassword) throws Exception { - // 1. 检查邮箱是否已被注册 - if (userEntityRepository.existsByEmail(email)) { - throw new Exception("错误:该邮箱地址已被注册!"); // 或者自定义异常 + @Transactional + public UserEntity registerNewUser(@NotNull RegistrationDto registrationDto) throws Exception { + + if (userRepository.existsByEmail(registrationDto.getEmail())) { + throw new Exception("エラー:メール:" + registrationDto.getEmail() + " はすでに登録されました。"); } - // (可选) 检查用户名是否已被注册 (如果您有用户名字段) - // if (userEntityRepository.existsByUsername(username)) { - // throw new Exception("错误:该用户名已被注册!"); - // } - - // 2. 创建新的 UserEntity 对象 UserEntity newUser = new UserEntity(); - newUser.setName(name); - newUser.setEmail(email); + newUser.setName(registrationDto.getName()); + newUser.setEmail(registrationDto.getEmail()); + newUser.setPassword(passwordEncoder.encode(registrationDto.getPassword())); - // 3. 对密码进行哈希加密 (非常重要!) - // String hashedPassword = passwordEncoder.encode(rawPassword); - // newUser.setPassword(hashedPassword); - newUser.setPassword(rawPassword); // 实际项目中必须加密!这里为了简化先直接赋值 + return userRepository.save(newUser); + } - // 4. 设置其他默认属性,例如账户状态、角色等 (如果需要) - // newUser.setActive(true); - // newUser.setRoles(...); + @Override + @Transactional(readOnly = true) + public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + UserEntity userEntity = userRepository.findByEmail(email) + .orElseThrow(() -> new UsernameNotFoundException(email + " not found")); - // 5. 保存新用户到数据库 - return userEntityRepository.save(newUser); + Collection authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")); // 示例:给所有用户一个ROLE_USER权限 + + return new User( + userEntity.getEmail(), + userEntity.getPassword(), + true, // enabled + true, // accountNonExpired + true, // credentialsNonExpired + true, // accountNonLocked + authorities // 用户的权限集合 + ); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e435626..cc45d08 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -7,4 +7,7 @@ spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect spring.datasource.url=jdbc:mysql://192.168.1.192:3306/dog spring.datasource.username=coder spring.datasource.password=coder -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver \ No newline at end of file +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + +jwt.secret=wM7Pz4BxvZ5NcLaBpgJm0eRQ5ztc3W5+OPH0E7g3gcQ= +jwt.token-expiration-ms=900000 \ No newline at end of file diff --git a/target/classes/META-INF/maven/co.jp.app/dog-2/pom.properties b/target/classes/META-INF/maven/co.jp.app/dog-2/pom.properties index 2af9eff..132537c 100644 --- a/target/classes/META-INF/maven/co.jp.app/dog-2/pom.properties +++ b/target/classes/META-INF/maven/co.jp.app/dog-2/pom.properties @@ -1,5 +1,5 @@ #Generated by Maven Integration for Eclipse -#Mon May 12 11:48:07 JST 2025 +#Mon May 12 14:19:16 JST 2025 m2e.projectLocation=C\:\\Users\\ichbi\\OneDrive\\\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7\\dog-1 m2e.projectName=dog-1 groupId=co.jp.app diff --git a/target/classes/META-INF/maven/co.jp.app/dog-2/pom.xml b/target/classes/META-INF/maven/co.jp.app/dog-2/pom.xml index 58faca1..b3d201c 100644 --- a/target/classes/META-INF/maven/co.jp.app/dog-2/pom.xml +++ b/target/classes/META-INF/maven/co.jp.app/dog-2/pom.xml @@ -1,91 +1,118 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.4.5 - - - co.jp.app - dog-2 - 0.0.1-SNAPSHOT - dog-1 - dog introduce project for Spring Boot - - - - - - - - - - - - - - - 17 - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.4.5 + + + co.jp.app + dog-2 + 0.0.1-SNAPSHOT + dog-1 + dog introduce project for Spring Boot + + + + + + + + + + + + + + + 17 + + - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jpa - - - com.mysql - mysql-connector-j - runtime - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-security - - - com.fasterxml.jackson.core - jackson-databind - - + + org.springframework.boot + spring-boot-starter-web + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.mysql + mysql-connector-j + runtime + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-security + + + com.fasterxml.jackson.core + jackson-databind + + + org.jetbrains + annotations + 13.0 + compile + + - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.projectlombok - lombok - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + \ No newline at end of file diff --git a/target/classes/application.properties b/target/classes/application.properties deleted file mode 100644 index e435626..0000000 --- a/target/classes/application.properties +++ /dev/null @@ -1,10 +0,0 @@ -spring.application.name=dog-1 -spring.sql.init.platform=mysql -spring.jpa.hibernate.ddl-auto=update -spring.jpa.show-sql=true -spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect - -spring.datasource.url=jdbc:mysql://192.168.1.192:3306/dog -spring.datasource.username=coder -spring.datasource.password=coder -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver \ No newline at end of file diff --git a/target/classes/co/jp/app/Dog1Application.class b/target/classes/co/jp/app/Dog1Application.class index 9c3b473..30bb284 100644 Binary files a/target/classes/co/jp/app/Dog1Application.class and b/target/classes/co/jp/app/Dog1Application.class differ diff --git a/target/classes/co/jp/app/common/ApiResponse.class b/target/classes/co/jp/app/common/ApiResponse.class new file mode 100644 index 0000000..906a064 Binary files /dev/null and b/target/classes/co/jp/app/common/ApiResponse.class differ diff --git a/target/classes/co/jp/app/config/CorsConfig.class b/target/classes/co/jp/app/config/CorsConfig.class new file mode 100644 index 0000000..e3e0bf6 Binary files /dev/null and b/target/classes/co/jp/app/config/CorsConfig.class differ diff --git a/target/classes/co/jp/app/config/SecurityConfig.class b/target/classes/co/jp/app/config/SecurityConfig.class deleted file mode 100644 index 6b2088c..0000000 Binary files a/target/classes/co/jp/app/config/SecurityConfig.class and /dev/null differ diff --git a/target/classes/co/jp/app/config/security/SecurityConfig.class b/target/classes/co/jp/app/config/security/SecurityConfig.class new file mode 100644 index 0000000..fb3a4d7 Binary files /dev/null and b/target/classes/co/jp/app/config/security/SecurityConfig.class differ diff --git a/target/classes/co/jp/app/config/security/filter/JwtAuthenticationFilter.class b/target/classes/co/jp/app/config/security/filter/JwtAuthenticationFilter.class new file mode 100644 index 0000000..4d41360 Binary files /dev/null and b/target/classes/co/jp/app/config/security/filter/JwtAuthenticationFilter.class differ diff --git a/target/classes/co/jp/app/controller/PetController.class b/target/classes/co/jp/app/controller/PetController.class index a68aaf5..3e8803f 100644 Binary files a/target/classes/co/jp/app/controller/PetController.class and b/target/classes/co/jp/app/controller/PetController.class differ diff --git a/target/classes/co/jp/app/controller/UserController.class b/target/classes/co/jp/app/controller/UserController.class new file mode 100644 index 0000000..92f642b Binary files /dev/null and b/target/classes/co/jp/app/controller/UserController.class differ diff --git a/target/classes/co/jp/app/controller/loginController.class b/target/classes/co/jp/app/controller/loginController.class deleted file mode 100644 index ad6c03e..0000000 Binary files a/target/classes/co/jp/app/controller/loginController.class and /dev/null differ diff --git a/target/classes/co/jp/app/controller/uploadController.class b/target/classes/co/jp/app/controller/uploadController.class index 3f091be..8b868f1 100644 Binary files a/target/classes/co/jp/app/controller/uploadController.class and b/target/classes/co/jp/app/controller/uploadController.class differ diff --git a/target/classes/co/jp/app/dto/LoginDto.class b/target/classes/co/jp/app/dto/LoginDto.class new file mode 100644 index 0000000..04f8554 Binary files /dev/null and b/target/classes/co/jp/app/dto/LoginDto.class differ diff --git a/target/classes/co/jp/app/dto/RegistrationDto.class b/target/classes/co/jp/app/dto/RegistrationDto.class new file mode 100644 index 0000000..2b384f4 Binary files /dev/null and b/target/classes/co/jp/app/dto/RegistrationDto.class differ diff --git a/target/classes/co/jp/app/entity/PetEntity.class b/target/classes/co/jp/app/entity/PetEntity.class index d110b1e..0bd3793 100644 Binary files a/target/classes/co/jp/app/entity/PetEntity.class and b/target/classes/co/jp/app/entity/PetEntity.class differ diff --git a/target/classes/co/jp/app/repository/ErrRepository.class b/target/classes/co/jp/app/repository/ErrRepository.class new file mode 100644 index 0000000..c8802c9 Binary files /dev/null and b/target/classes/co/jp/app/repository/ErrRepository.class differ diff --git a/target/classes/co/jp/app/repository/PetRepository.class b/target/classes/co/jp/app/repository/PetRepository.class index 7b03a44..0605933 100644 Binary files a/target/classes/co/jp/app/repository/PetRepository.class and b/target/classes/co/jp/app/repository/PetRepository.class differ diff --git a/target/classes/co/jp/app/repository/erraRepository.class b/target/classes/co/jp/app/repository/erraRepository.class deleted file mode 100644 index 87a64db..0000000 Binary files a/target/classes/co/jp/app/repository/erraRepository.class and /dev/null differ diff --git a/target/classes/co/jp/app/service/JwtService.class b/target/classes/co/jp/app/service/JwtService.class new file mode 100644 index 0000000..3ebabfa Binary files /dev/null and b/target/classes/co/jp/app/service/JwtService.class differ diff --git a/target/classes/co/jp/app/service/PetService.class b/target/classes/co/jp/app/service/PetService.class index b31b3cf..257099f 100644 Binary files a/target/classes/co/jp/app/service/PetService.class and b/target/classes/co/jp/app/service/PetService.class differ diff --git a/target/classes/co/jp/app/service/erraService.class b/target/classes/co/jp/app/service/erraService.class index a418ff6..6e7e760 100644 Binary files a/target/classes/co/jp/app/service/erraService.class and b/target/classes/co/jp/app/service/erraService.class differ diff --git a/target/classes/co/jp/app/service/uploadService.class b/target/classes/co/jp/app/service/uploadService.class deleted file mode 100644 index 5261e89..0000000 Binary files a/target/classes/co/jp/app/service/uploadService.class and /dev/null differ diff --git a/target/classes/co/jp/app/service/userService.class b/target/classes/co/jp/app/service/userService.class index 72aa68e..00b78d2 100644 Binary files a/target/classes/co/jp/app/service/userService.class and b/target/classes/co/jp/app/service/userService.class differ diff --git a/target/classes/static/border-collie.jpg b/target/classes/static/border-collie.jpg deleted file mode 100644 index b438f3f..0000000 Binary files a/target/classes/static/border-collie.jpg and /dev/null differ diff --git a/target/classes/static/chiwawa.jpg b/target/classes/static/chiwawa.jpg deleted file mode 100644 index 7a45973..0000000 Binary files a/target/classes/static/chiwawa.jpg and /dev/null differ diff --git a/target/classes/static/frenchboxdog.jpg b/target/classes/static/frenchboxdog.jpg deleted file mode 100644 index cdf870e..0000000 Binary files a/target/classes/static/frenchboxdog.jpg and /dev/null differ diff --git a/target/classes/static/pagu.jpg b/target/classes/static/pagu.jpg deleted file mode 100644 index 1b0b580..0000000 Binary files a/target/classes/static/pagu.jpg and /dev/null differ diff --git a/target/classes/static/sherff.jpg b/target/classes/static/sherff.jpg deleted file mode 100644 index 49a34d6..0000000 Binary files a/target/classes/static/sherff.jpg and /dev/null differ