From 5e202b122e0ed929d18349bbd3fa292762ad2ec2 Mon Sep 17 00:00:00 2001 From: takiguchi Date: Thu, 16 Jul 2020 14:06:41 +0200 Subject: [PATCH] Init api and security. --- .gitignore | 33 +++++++ pom.xml | 87 +++++++++++++++++++ .../takiguchi/starter/StarterApplication.java | 11 +++ .../config/security/JwtRequestFilter.java | 70 +++++++++++++++ .../security/SecurityConfiguration.java | 59 +++++++++++++ .../config/security/TokenProvider.java | 70 +++++++++++++++ .../controller/AuthenticationController.java | 47 ++++++++++ .../exception/BadRequestException.java | 15 ++++ .../starter/exception/BusinessException.java | 26 ++++++ .../starter/exception/ForbiddenException.java | 23 +++++ .../InternalServerErrorException.java | 15 ++++ .../starter/exception/NoContentException.java | 17 ++++ .../starter/exception/NotFoundException.java | 17 ++++ .../starter/exception/TechnicalException.java | 23 +++++ .../TournamentValidationException.java | 7 ++ .../exception/UnauthorizedException.java | 22 +++++ .../org/takiguchi/starter/model/dao/User.java | 26 ++++++ .../starter/model/dto/LoginRequest.java | 14 +++ .../starter/model/dto/LoginResponse.java | 14 +++ .../starter/repository/UserRepository.java | 13 +++ src/main/resources/application.yml | 36 ++++++++ .../starter/StarterApplicationTests.java | 13 +++ 22 files changed, 658 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/org/takiguchi/starter/StarterApplication.java create mode 100644 src/main/java/org/takiguchi/starter/config/security/JwtRequestFilter.java create mode 100644 src/main/java/org/takiguchi/starter/config/security/SecurityConfiguration.java create mode 100644 src/main/java/org/takiguchi/starter/config/security/TokenProvider.java create mode 100644 src/main/java/org/takiguchi/starter/controller/AuthenticationController.java create mode 100644 src/main/java/org/takiguchi/starter/exception/BadRequestException.java create mode 100644 src/main/java/org/takiguchi/starter/exception/BusinessException.java create mode 100644 src/main/java/org/takiguchi/starter/exception/ForbiddenException.java create mode 100644 src/main/java/org/takiguchi/starter/exception/InternalServerErrorException.java create mode 100644 src/main/java/org/takiguchi/starter/exception/NoContentException.java create mode 100644 src/main/java/org/takiguchi/starter/exception/NotFoundException.java create mode 100644 src/main/java/org/takiguchi/starter/exception/TechnicalException.java create mode 100644 src/main/java/org/takiguchi/starter/exception/TournamentValidationException.java create mode 100644 src/main/java/org/takiguchi/starter/exception/UnauthorizedException.java create mode 100644 src/main/java/org/takiguchi/starter/model/dao/User.java create mode 100644 src/main/java/org/takiguchi/starter/model/dto/LoginRequest.java create mode 100644 src/main/java/org/takiguchi/starter/model/dto/LoginResponse.java create mode 100644 src/main/java/org/takiguchi/starter/repository/UserRepository.java create mode 100644 src/main/resources/application.yml create mode 100644 src/test/java/org/takiguchi/starter/StarterApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c27df25 --- /dev/null +++ b/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.1.RELEASE + + + org.takiguchi + starter + 0.0.1-SNAPSHOT + starter + Demo project for Spring Boot + + + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.postgresql + postgresql + runtime + + + io.jsonwebtoken + jjwt + 0.9.1 + + + org.apache.httpcomponents + httpclient + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.springframework.security + spring-security-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/src/main/java/org/takiguchi/starter/StarterApplication.java b/src/main/java/org/takiguchi/starter/StarterApplication.java new file mode 100644 index 0000000..ec3f0f9 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/StarterApplication.java @@ -0,0 +1,11 @@ +package org.takiguchi.starter; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class StarterApplication { + public static void main(String[] args) { + SpringApplication.run(StarterApplication.class, args); + } +} diff --git a/src/main/java/org/takiguchi/starter/config/security/JwtRequestFilter.java b/src/main/java/org/takiguchi/starter/config/security/JwtRequestFilter.java new file mode 100644 index 0000000..53aaa87 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/config/security/JwtRequestFilter.java @@ -0,0 +1,70 @@ +package org.takiguchi.starter.config.security; + +import io.jsonwebtoken.Claims; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.takiguchi.tamotsu.client.constant.Constant.AUTHORITIES_KEY; + +public class JwtRequestFilter extends OncePerRequestFilter { + private static final String TOKEN_PREFIX = "Bearer "; + + private final TokenProvider tokenProvider; + + public JwtRequestFilter(TokenProvider tokenProvider) { + this.tokenProvider = tokenProvider; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { + String token = getTokenFromHeaders(request); + + // Once we get the token validate it. + if (SecurityContextHolder.getContext().getAuthentication() == null) { + String username = null; + try { + username = tokenProvider.getUsernameFromToken(token); + } catch (Exception e) { + // Do nothing + } + + if (username != null && !tokenProvider.isTokenExpired(token)) { + Claims claims = tokenProvider.getAllClaimsFromToken(token); + List roles = claims.get(AUTHORITIES_KEY, List.class); + + List authorities = roles.stream() + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, username, authorities); + SecurityContextHolder.getContext().setAuthentication(auth); + } + } + + chain.doFilter(request, response); + } + + private String getTokenFromHeaders(HttpServletRequest request) { + String token = null; + + String authHeader = request.getHeader(AUTHORIZATION); + if (authHeader != null && authHeader.startsWith(TOKEN_PREFIX)) { + token = authHeader.replace(TOKEN_PREFIX, ""); + } else { + logger.debug("Couldn't find bearer string, will ignore the header."); + } + + return token; + } +} diff --git a/src/main/java/org/takiguchi/starter/config/security/SecurityConfiguration.java b/src/main/java/org/takiguchi/starter/config/security/SecurityConfiguration.java new file mode 100644 index 0000000..06a4909 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/config/security/SecurityConfiguration.java @@ -0,0 +1,59 @@ +package org.takiguchi.starter.config.security; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import javax.servlet.http.HttpServletResponse; + +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.OPTIONS; +import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; + +@EnableWebSecurity +@Configuration +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http.cors().disable() + .exceptionHandling() + .authenticationEntryPoint((request, response, authResponse) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) + .accessDeniedHandler((request, response, accessDeniedException) -> response.sendError(HttpServletResponse.SC_FORBIDDEN)) + .and() + .addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class) + .sessionManagement().sessionCreationPolicy(STATELESS) + .and() + .requiresChannel() + .anyRequest() + .requiresSecure() + .and() + .csrf().disable() + .authorizeRequests() + .antMatchers( + "/api/auth/login", + "/api/health/check" + ).permitAll() + .antMatchers(OPTIONS).permitAll() + .anyRequest().authenticated(); + } + + @Bean + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + @Bean + public TokenProvider tokenProvider(@Value("${app.security.signing-key}") String signingKey, + @Value("${app.security.access-token-validity-seconds}") int accessTokenValiditySeconds) { + return new TokenProvider(signingKey, accessTokenValiditySeconds); + } + + @Bean + public JwtRequestFilter jwtRequestFilter() { + return new JwtRequestFilter(tokenProvider()); + } +} diff --git a/src/main/java/org/takiguchi/starter/config/security/TokenProvider.java b/src/main/java/org/takiguchi/starter/config/security/TokenProvider.java new file mode 100644 index 0000000..2fbca9a --- /dev/null +++ b/src/main/java/org/takiguchi/starter/config/security/TokenProvider.java @@ -0,0 +1,70 @@ +package org.takiguchi.starter.config.security; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.springframework.beans.factory.annotation.Value; +import org.takiguchi.starter.model.dao.User; + +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.function.Function; + +public class TokenProvider { + + private static final String AUTHORITIES_KEY = "scopes"; + private final String signingKey; + private final int accessTokenValiditySeconds; + + public TokenProvider(String signingKey, int accessTokenValiditySeconds) { + this.signingKey = signingKey; + this.accessTokenValiditySeconds = accessTokenValiditySeconds; + } + + + public String getUserEmailFromToken(String token) { + return getClaimFromToken(token, Claims::getSubject); + } + + public Date getExpirationDateFromToken(String token) { + return getClaimFromToken(token, Claims::getExpiration); + } + + public T getClaimFromToken(String token, Function claimsResolver) { + Claims claims = getAllClaimsFromToken(token); + return claimsResolver.apply(claims); + } + + public Boolean isTokenExpired(String token) { + final Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } + + public Claims getAllClaimsFromToken(String token) { + return Jwts.parser() + .setSigningKey(signingKey) + .parseClaimsJws(token) + .getBody(); + } + + public String generateToken(User user) { + return Jwts.builder() + .setSubject(user.getEmail()) + .claim(AUTHORITIES_KEY, getAuthorities(user)) + .signWith(SignatureAlgorithm.HS256, signingKey) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + accessTokenValiditySeconds * 1000)) + .compact(); + } + + private List getAuthorities(User user) { + List authorities = Collections.emptyList(); +// if (!CollectionUtils.isEmpty(user.getRoles())) { +// authorities = user.getRoles().stream() +// .map(Role::getName) +// .collect(Collectors.toList()); +// } + return authorities; + } +} diff --git a/src/main/java/org/takiguchi/starter/controller/AuthenticationController.java b/src/main/java/org/takiguchi/starter/controller/AuthenticationController.java new file mode 100644 index 0000000..828a8f5 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/controller/AuthenticationController.java @@ -0,0 +1,47 @@ +package org.takiguchi.starter.controller; + +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.takiguchi.starter.config.security.TokenProvider; +import org.takiguchi.starter.exception.BadRequestException; +import org.takiguchi.starter.model.dao.User; +import org.takiguchi.starter.model.dto.LoginRequest; +import org.takiguchi.starter.model.dto.LoginResponse; +import org.takiguchi.starter.repository.UserRepository; + +@RestController +@RequestMapping("/api/auth") +public class AuthenticationController { + + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + private final TokenProvider tokenProvider; + + public AuthenticationController(UserRepository userRepository, PasswordEncoder passwordEncoder, TokenProvider tokenProvider) { + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; + this.tokenProvider = tokenProvider; + } + + @PostMapping("/login") + public LoginResponse login(@RequestBody LoginRequest request) { + return userRepository.findByEmail(request.getEmail()) + .map(user -> checkCredentials(request, user)) + .map(LoginResponse::new) + .orElseThrow(() -> new BadRequestException("MSG_INVALID_CREDENTIALS")); + } + + /** + * If passwords match, this function generate a token. Otherwise, an {@link BadRequestException} is returned. + */ + private String checkCredentials(LoginRequest loginRequest, User user) { + if (passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) { + return tokenProvider.generateToken(user); + } else { + throw new BadRequestException("Invalid credentials"); + } + } +} diff --git a/src/main/java/org/takiguchi/starter/exception/BadRequestException.java b/src/main/java/org/takiguchi/starter/exception/BadRequestException.java new file mode 100644 index 0000000..ed25ed0 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/exception/BadRequestException.java @@ -0,0 +1,15 @@ +package org.takiguchi.starter.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.BAD_REQUEST) +public class BadRequestException extends BusinessException { + public BadRequestException(String message) { + super(message); + } + + public BadRequestException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/takiguchi/starter/exception/BusinessException.java b/src/main/java/org/takiguchi/starter/exception/BusinessException.java new file mode 100644 index 0000000..4992a39 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/exception/BusinessException.java @@ -0,0 +1,26 @@ +package org.takiguchi.starter.exception; + +/** + * Business exception. + */ +public class BusinessException extends RuntimeException { + + public BusinessException() {} + + /** + * Constructs an exception with a message. + * @param message The description of the error met. + */ + public BusinessException(final String message) { + super(message); + } + + /** + * Constructs an exception with a message and a code. + * @param message The description of the error met. + * @param cause The cause of the exception. + */ + public BusinessException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/takiguchi/starter/exception/ForbiddenException.java b/src/main/java/org/takiguchi/starter/exception/ForbiddenException.java new file mode 100644 index 0000000..3a425e9 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/exception/ForbiddenException.java @@ -0,0 +1,23 @@ +package org.takiguchi.starter.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when user attempt to access a resource that he has not rights. + */ +@ResponseStatus(value = HttpStatus.FORBIDDEN) +public class ForbiddenException extends BusinessException { + + public ForbiddenException() { + super(); + } + + public ForbiddenException(String message) { + super(message); + } + + public ForbiddenException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/takiguchi/starter/exception/InternalServerErrorException.java b/src/main/java/org/takiguchi/starter/exception/InternalServerErrorException.java new file mode 100644 index 0000000..cc60509 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/exception/InternalServerErrorException.java @@ -0,0 +1,15 @@ +package org.takiguchi.starter.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) +public class InternalServerErrorException extends TechnicalException { + public InternalServerErrorException(String message) { + super(message); + } + + public InternalServerErrorException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/takiguchi/starter/exception/NoContentException.java b/src/main/java/org/takiguchi/starter/exception/NoContentException.java new file mode 100644 index 0000000..0d31848 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/exception/NoContentException.java @@ -0,0 +1,17 @@ +package org.takiguchi.starter.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.NO_CONTENT) +public class NoContentException extends BusinessException { + public NoContentException() {} + + public NoContentException(String message) { + super(message); + } + + public NoContentException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/takiguchi/starter/exception/NotFoundException.java b/src/main/java/org/takiguchi/starter/exception/NotFoundException.java new file mode 100644 index 0000000..b035b86 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/exception/NotFoundException.java @@ -0,0 +1,17 @@ +package org.takiguchi.starter.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.NOT_FOUND) +public class NotFoundException extends BusinessException { + public NotFoundException() {} + + public NotFoundException(String message) { + super(message); + } + + public NotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/takiguchi/starter/exception/TechnicalException.java b/src/main/java/org/takiguchi/starter/exception/TechnicalException.java new file mode 100644 index 0000000..976395b --- /dev/null +++ b/src/main/java/org/takiguchi/starter/exception/TechnicalException.java @@ -0,0 +1,23 @@ +package org.takiguchi.starter.exception; + +/** + * Technical exception. + */ +public class TechnicalException extends RuntimeException { + /** + * Constructs an exception with a message. + * @param message The description of the error met. + */ + public TechnicalException(final String message) { + super(message); + } + + /** + * Constructs an exception with a message and a code. + * @param message The description of the error met. + * @param cause The cause of the exception. + */ + public TechnicalException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/takiguchi/starter/exception/TournamentValidationException.java b/src/main/java/org/takiguchi/starter/exception/TournamentValidationException.java new file mode 100644 index 0000000..4fbca71 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/exception/TournamentValidationException.java @@ -0,0 +1,7 @@ +package org.takiguchi.starter.exception; + +public class TournamentValidationException extends BusinessException { + public TournamentValidationException(String message) { + super(message); + } +} diff --git a/src/main/java/org/takiguchi/starter/exception/UnauthorizedException.java b/src/main/java/org/takiguchi/starter/exception/UnauthorizedException.java new file mode 100644 index 0000000..0dc5ec9 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/exception/UnauthorizedException.java @@ -0,0 +1,22 @@ +package org.takiguchi.starter.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when an anonymous user attempt to access to secured resource or if he failed to login. + */ +@ResponseStatus(value = HttpStatus.UNAUTHORIZED) +public class UnauthorizedException extends BusinessException { + public UnauthorizedException() { + super(); + } + + public UnauthorizedException(String message) { + super(message); + } + + public UnauthorizedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/takiguchi/starter/model/dao/User.java b/src/main/java/org/takiguchi/starter/model/dao/User.java new file mode 100644 index 0000000..d70b806 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/model/dao/User.java @@ -0,0 +1,26 @@ +package org.takiguchi.starter.model.dao; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Table(name = "`user`") +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class User { + @Id + @GeneratedValue(generator = "system-uuid") + private UUID id; + @Column(nullable = false) + private String email; + @Column(nullable = false) + private String password; + private String username; +} diff --git a/src/main/java/org/takiguchi/starter/model/dto/LoginRequest.java b/src/main/java/org/takiguchi/starter/model/dto/LoginRequest.java new file mode 100644 index 0000000..361075b --- /dev/null +++ b/src/main/java/org/takiguchi/starter/model/dto/LoginRequest.java @@ -0,0 +1,14 @@ +package org.takiguchi.starter.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter @Setter +@NoArgsConstructor +@AllArgsConstructor +public class LoginRequest { + private String email; + private String password; +} diff --git a/src/main/java/org/takiguchi/starter/model/dto/LoginResponse.java b/src/main/java/org/takiguchi/starter/model/dto/LoginResponse.java new file mode 100644 index 0000000..ea0ac67 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/model/dto/LoginResponse.java @@ -0,0 +1,14 @@ +package org.takiguchi.starter.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class LoginResponse { + private String token; +} diff --git a/src/main/java/org/takiguchi/starter/repository/UserRepository.java b/src/main/java/org/takiguchi/starter/repository/UserRepository.java new file mode 100644 index 0000000..5a03774 --- /dev/null +++ b/src/main/java/org/takiguchi/starter/repository/UserRepository.java @@ -0,0 +1,13 @@ +package org.takiguchi.starter.repository; + +import org.takiguchi.starter.model.dao.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface UserRepository extends JpaRepository { + Optional findByEmail(String email); +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..303c1c5 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,36 @@ +app: + security: + signing-key: SigningKeyValue! + # 5 * 60 * 60 -> 5 hours + access-token-validity-seconds: 18000 + +# ================================================= +# Spring configuration +# ================================================= +spring: + # ------------------------------------------------- + # Database configuration + # ------------------------------------------------- + datasource: + driverClassName: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/db_database + username: postgres + password: postgres + # Disable feature detection by this undocumented parameter. + # Check the org.hibernate.engine.jdbc.internal.JdbcServiceImpl.configure method for more details. + jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect + properties.hibernate.temp.use_jdbc_metadata_defaults: false + open-in-view: false + +server: + error: + whitelabel: + enabled: false # Disable html error responses. + +logging: + level: + org: + # hibernate: DEBUG + springframework: +# mail: DEBUG diff --git a/src/test/java/org/takiguchi/starter/StarterApplicationTests.java b/src/test/java/org/takiguchi/starter/StarterApplicationTests.java new file mode 100644 index 0000000..0d66298 --- /dev/null +++ b/src/test/java/org/takiguchi/starter/StarterApplicationTests.java @@ -0,0 +1,13 @@ +package org.takiguchi.starter; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class StarterApplicationTests { + + @Test + void contextLoads() { + } + +}