This commit is contained in:
2025-05-12 14:19:49 +09:00
50 changed files with 755 additions and 307 deletions

197
pom.xml
View File

@ -1,91 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version> <version>3.4.5</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>co.jp.app</groupId> <groupId>co.jp.app</groupId>
<artifactId>dog-2</artifactId> <artifactId>dog-2</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>dog-1</name> <name>dog-1</name>
<description>dog introduce project for Spring Boot</description> <description>dog introduce project for Spring Boot</description>
<url/> <url/>
<licenses> <licenses>
<license/> <license/>
</licenses> </licenses>
<developers> <developers>
<developer/> <developer/>
</developers> </developers>
<scm> <scm>
<connection/> <connection/>
<developerConnection/> <developerConnection/>
<tag/> <tag/>
<url/> <url/>
</scm> </scm>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>io.jsonwebtoken</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>jjwt-api</artifactId>
</dependency> <version>0.11.5</version>
<dependency> </dependency>
<groupId>com.mysql</groupId> <dependency>
<artifactId>mysql-connector-j</artifactId> <groupId>io.jsonwebtoken</groupId>
<scope>runtime</scope> <artifactId>jjwt-impl</artifactId>
</dependency> <version>0.11.5</version>
<dependency> <scope>runtime</scope>
<groupId>org.springframework.boot</groupId> </dependency>
<artifactId>spring-boot-starter-test</artifactId> <dependency>
<scope>test</scope> <groupId>io.jsonwebtoken</groupId>
</dependency> <artifactId>jjwt-jackson</artifactId>
<dependency> <version>0.11.5</version>
<groupId>org.springframework.boot</groupId> <scope>runtime</scope>
<artifactId>spring-boot-starter-security</artifactId> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.springframework.boot</groupId>
<groupId>com.fasterxml.jackson.core</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId>
<artifactId>jackson-databind</artifactId> </dependency>
</dependency> <dependency>
</dependencies> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <configuration>
<annotationProcessorPaths> <annotationProcessorPaths>
<path> <path>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</path> </path>
</annotationProcessorPaths> </annotationProcessorPaths>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <configuration>
<excludes> <excludes>
<exclude> <exclude>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</exclude> </exclude>
</excludes> </excludes>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -0,0 +1,61 @@
package co.jp.app.common;
import java.util.Objects;
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(true, null, data);
}
public static <T> ApiResponse<T> 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 +
'}';
}
}

View File

@ -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
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import co.jp.app.service.PetService; import co.jp.app.service.PetService;
@Controller @Controller
public class PetController { public class PetController {
@ -19,7 +18,7 @@ public class PetController {
@GetMapping("/api/dogs/pet") @GetMapping("/api/dogs/pet")
public String getListByEntities(@RequestParam List<Integer> id) { public String getListByEntities(@RequestParam List<Integer> id) {
service.getPetByID(id); service.getPetByID(id);
return "pet"; return "pet";
} }
} }

View File

@ -9,20 +9,19 @@ import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import co.jp.app.entity.PetEntity; import co.jp.app.entity.PetEntity;
import co.jp.app.service.UploadService;
@CrossOrigin("http://192.168.1.50:5173") @CrossOrigin("http://192.168.1.50:5173")
@Controller @Controller
public class UploadController { public class UploadController {
@Autowired @Autowired
private UploadService service; //private UploadService service;
@PostMapping("/api/dogs/upload") @PostMapping("/api/dogs/upload")
public String upload() { public String upload() {
List<PetEntity> list = new ArrayList<PetEntity>(); List<PetEntity> list = new ArrayList<PetEntity>();
service.saveAll(list); //service.saveAll(list);
return "upload"; return "upload";

View File

@ -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<String, String> 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("サーバーエラー。"));
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -5,10 +5,11 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Entity @Entity
@Table(name = "Pet") @Table(name = "Pet")
public class PetEntity{ public class PetEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@Id @Id
private int ID; private int ID;
@ -27,15 +28,15 @@ public class PetEntity{
//犬の健康状態 //犬の健康状態
private String status; private String status;
//犬の圖片 //犬の圖片
private String image; private String image;
public String getImage() {
return image;
}
public void setImage(String image) { public String getImage() {
this.image = image; return image;
} }
public void setImage(String image) {
this.image = image;
}
public String getName() { public String getName() {
return name; return name;
@ -92,6 +93,7 @@ public class PetEntity{
public void setStatus(String status) { public void setStatus(String status) {
this.status = status; this.status = status;
} }
public int getID() { public int getID() {
return ID; return ID;
} }
@ -99,5 +101,5 @@ public class PetEntity{
public void setID(int iD) { public void setID(int iD) {
ID = iD; ID = iD;
} }
} }

View File

@ -7,9 +7,10 @@ import org.springframework.data.repository.query.Param;
import co.jp.app.entity.ErrorEntity; import co.jp.app.entity.ErrorEntity;
public interface ErraRepository extends JpaRepository<ErrorEntity, Integer>{ public interface ErrRepository extends JpaRepository<ErrorEntity, Integer>{
public default ErrorEntity getById(@Param("id") int id) { public default ErrorEntity getById(@Param("id") int id) {
return getById(id); return getById(id);
} }
} }

View File

@ -1,6 +1,5 @@
package co.jp.app.repository; package co.jp.app.repository;
import java.util.List; import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
@ -9,10 +8,8 @@ import co.jp.app.entity.PetEntity;
public interface PetRepository extends JpaRepository<PetEntity, Integer> { public interface PetRepository extends JpaRepository<PetEntity, Integer> {
@Override @Override
default List<PetEntity> findAllById(Iterable<Integer> id) { default List<PetEntity> findAllById(Iterable<Integer> id) {
return findAllById(id); return findAllById(id);
} }
} }

View File

@ -4,14 +4,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import co.jp.app.entity.ErrorEntity; import co.jp.app.entity.ErrorEntity;
import co.jp.app.repository.ErraRepository; import co.jp.app.repository.ErrRepository;
@Service @Service
public class ErraService { public class ErraService {
@Autowired @Autowired
ErraRepository erraRepository; ErrRepository erraRepository;
public ErrorEntity getStatusById(int id) { public ErrorEntity getStatusById(int id) {
return erraRepository.getById(id); return erraRepository.getById(id);

View File

@ -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> T extractClaim(String token, @NotNull Function<Claims, T> 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<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> 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);
}
}

View File

@ -8,7 +8,6 @@ import org.springframework.stereotype.Service;
import co.jp.app.entity.PetEntity; import co.jp.app.entity.PetEntity;
import co.jp.app.repository.PetRepository; import co.jp.app.repository.PetRepository;
@Service @Service
public class PetService { public class PetService {
@Autowired @Autowired
@ -17,6 +16,6 @@ public class PetService {
public List<PetEntity> getPetByID(Iterable<Integer> id) { public List<PetEntity> getPetByID(Iterable<Integer> id) {
return dao.findAllById(id); return dao.findAllById(id);
} }
} }

View File

@ -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<PetEntity> saveAll(Iterable<PetEntity> entities) {
return uploadDao.saveAll(entities);
}
}

View File

@ -1,5 +1,6 @@
package co.jp.app.service; package co.jp.app.service;
<<<<<<< HEAD
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -8,10 +9,33 @@ import co.jp.app.entity.UserEntity;
import co.jp.app.repository.userRepository; import co.jp.app.repository.userRepository;
import jakarta.transaction.Transactional; 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 @Service
public class UserService { public class UserService implements UserDetailsService {
<<<<<<< HEAD
private final userRepository userEntityRepository; private final userRepository userEntityRepository;
@Autowired @Autowired
@ -20,35 +44,47 @@ public class UserService {
this.userEntityRepository = userEntityRepository; this.userEntityRepository = userEntityRepository;
this.passwordEncoder= passwordEncoder; 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 // 整个注册过程应该是一个事务 @Transactional
public UserEntity registerNewUser(String name, String email, String rawPassword) throws Exception { public UserEntity registerNewUser(@NotNull RegistrationDto registrationDto) throws Exception {
// 1. 检查邮箱是否已被注册
if (userEntityRepository.existsByEmail(email)) { if (userRepository.existsByEmail(registrationDto.getEmail())) {
throw new Exception("错误:该邮箱地址已被注册!"); // 或者自定义异常 throw new Exception("エラー:メール:" + registrationDto.getEmail() + " はすでに登録されました。");
} }
// (可选) 检查用户名是否已被注册 (如果您有用户名字段)
// if (userEntityRepository.existsByUsername(username)) {
// throw new Exception("错误:该用户名已被注册!");
// }
// 2. 创建新的 UserEntity 对象
UserEntity newUser = new UserEntity(); UserEntity newUser = new UserEntity();
newUser.setName(name); newUser.setName(registrationDto.getName());
newUser.setEmail(email); newUser.setEmail(registrationDto.getEmail());
newUser.setPassword(passwordEncoder.encode(registrationDto.getPassword()));
// 3. 对密码进行哈希加密 (非常重要!) return userRepository.save(newUser);
// String hashedPassword = passwordEncoder.encode(rawPassword); }
// newUser.setPassword(hashedPassword);
newUser.setPassword(rawPassword); // 实际项目中必须加密!这里为了简化先直接赋值
// 4. 设置其他默认属性,例如账户状态、角色等 (如果需要) @Override
// newUser.setActive(true); @Transactional(readOnly = true)
// newUser.setRoles(...); public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
UserEntity userEntity = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(email + " not found"));
// 5. 保存新用户到数据库 Collection<? extends GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")); // 示例给所有用户一个ROLE_USER权限
return userEntityRepository.save(newUser);
return new User(
userEntity.getEmail(),
userEntity.getPassword(),
true, // enabled
true, // accountNonExpired
true, // credentialsNonExpired
true, // accountNonLocked
authorities // 用户的权限集合
);
} }
} }

View File

@ -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.url=jdbc:mysql://192.168.1.192:3306/dog
spring.datasource.username=coder spring.datasource.username=coder
spring.datasource.password=coder spring.datasource.password=coder
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
jwt.secret=wM7Pz4BxvZ5NcLaBpgJm0eRQ5ztc3W5+OPH0E7g3gcQ=
jwt.token-expiration-ms=900000

View File

@ -1,5 +1,5 @@
#Generated by Maven Integration for Eclipse #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.projectLocation=C\:\\Users\\ichbi\\OneDrive\\\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7\\dog-1
m2e.projectName=dog-1 m2e.projectName=dog-1
groupId=co.jp.app groupId=co.jp.app

View File

@ -1,91 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version> <version>3.4.5</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>co.jp.app</groupId> <groupId>co.jp.app</groupId>
<artifactId>dog-2</artifactId> <artifactId>dog-2</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>dog-1</name> <name>dog-1</name>
<description>dog introduce project for Spring Boot</description> <description>dog introduce project for Spring Boot</description>
<url/> <url/>
<licenses> <licenses>
<license/> <license/>
</licenses> </licenses>
<developers> <developers>
<developer/> <developer/>
</developers> </developers>
<scm> <scm>
<connection/> <connection/>
<developerConnection/> <developerConnection/>
<tag/> <tag/>
<url/> <url/>
</scm> </scm>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>io.jsonwebtoken</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>jjwt-api</artifactId>
</dependency> <version>0.11.5</version>
<dependency> </dependency>
<groupId>com.mysql</groupId> <dependency>
<artifactId>mysql-connector-j</artifactId> <groupId>io.jsonwebtoken</groupId>
<scope>runtime</scope> <artifactId>jjwt-impl</artifactId>
</dependency> <version>0.11.5</version>
<dependency> <scope>runtime</scope>
<groupId>org.springframework.boot</groupId> </dependency>
<artifactId>spring-boot-starter-test</artifactId> <dependency>
<scope>test</scope> <groupId>io.jsonwebtoken</groupId>
</dependency> <artifactId>jjwt-jackson</artifactId>
<dependency> <version>0.11.5</version>
<groupId>org.springframework.boot</groupId> <scope>runtime</scope>
<artifactId>spring-boot-starter-security</artifactId> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.springframework.boot</groupId>
<groupId>com.fasterxml.jackson.core</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId>
<artifactId>jackson-databind</artifactId> </dependency>
</dependency> <dependency>
</dependencies> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <configuration>
<annotationProcessorPaths> <annotationProcessorPaths>
<path> <path>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</path> </path>
</annotationProcessorPaths> </annotationProcessorPaths>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <configuration>
<excludes> <excludes>
<exclude> <exclude>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</exclude> </exclude>
</excludes> </excludes>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB