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 extends GrantedAuthority> 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