diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 64645bc..72a3666 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -9,6 +9,7 @@ + @@ -26,6 +27,7 @@ + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 1c4676c..47964ca 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -2,9 +2,12 @@ + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 823060c..e555a0a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -7,6 +7,8 @@ diff --git a/api-gateway-service/.gitignore b/api-gateway-service/.gitignore index 153c933..549e00a 100644 --- a/api-gateway-service/.gitignore +++ b/api-gateway-service/.gitignore @@ -1,6 +1,8 @@ HELP.md -/target/ +target/ !.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ ### STS ### .apt_generated @@ -23,7 +25,9 @@ HELP.md /dist/ /nbdist/ /.nb-gradle/ -/build/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ ### VS Code ### .vscode/ diff --git a/api-gateway-service/.mvn/wrapper/MavenWrapperDownloader.java b/api-gateway-service/.mvn/wrapper/MavenWrapperDownloader.java index 72308aa..e76d1f3 100644 --- a/api-gateway-service/.mvn/wrapper/MavenWrapperDownloader.java +++ b/api-gateway-service/.mvn/wrapper/MavenWrapperDownloader.java @@ -1,38 +1,31 @@ /* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URL; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; import java.util.Properties; public class MavenWrapperDownloader { + private static final String WRAPPER_VERSION = "0.5.6"; /** * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ - private static final String DEFAULT_DOWNLOAD_URL = - "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; /** * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to @@ -80,13 +73,13 @@ public static void main(String args[]) { } } } - System.out.println("- Downloading from: : " + url); + System.out.println("- Downloading from: " + url); File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); if(!outputFile.getParentFile().exists()) { if(!outputFile.getParentFile().mkdirs()) { System.out.println( - "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); } } System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); @@ -102,6 +95,16 @@ public static void main(String args[]) { } private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } URL website = new URL(urlString); ReadableByteChannel rbc; rbc = Channels.newChannel(website.openStream()); diff --git a/api-gateway-service/.mvn/wrapper/maven-wrapper.jar b/api-gateway-service/.mvn/wrapper/maven-wrapper.jar index 01e6799..2cc7d4a 100644 Binary files a/api-gateway-service/.mvn/wrapper/maven-wrapper.jar and b/api-gateway-service/.mvn/wrapper/maven-wrapper.jar differ diff --git a/api-gateway-service/.mvn/wrapper/maven-wrapper.properties b/api-gateway-service/.mvn/wrapper/maven-wrapper.properties index cd0d451..abd303b 100644 --- a/api-gateway-service/.mvn/wrapper/maven-wrapper.properties +++ b/api-gateway-service/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/api-gateway-service/Dockerfile b/api-gateway-service/Dockerfile deleted file mode 100644 index f3b5dbc..0000000 --- a/api-gateway-service/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -#stage 1 -#Start with a base image containing Java runtime -FROM openjdk:11-slim as build - -# Add Maintainer Info -LABEL maintainer="Nasruddin " - -# The application's jar file -ARG JAR_FILE - -# Add the application's jar to the container -COPY ${JAR_FILE} app.jar - -#unpackage jar file -RUN mkdir -p target/dependency && (cd target/dependency; jar -xf /app.jar) - -#stage 2 -#Same Java runtime -FROM openjdk:11-slim - -#Add volume pointing to /tmp -VOLUME /tmp - -#Copy unpackaged application to new container -ARG DEPENDENCY=/target/dependency -COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib -COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF -COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app - -#execute the application -ENTRYPOINT ["java","-cp","app:app/lib/*","com.javatab.apigatewayservice.ApiGatewayServiceApplication"] \ No newline at end of file diff --git a/api-gateway-service/mvnw b/api-gateway-service/mvnw index 8b9da3b..a16b543 100755 --- a/api-gateway-service/mvnw +++ b/api-gateway-service/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script +# Maven Start Up Batch script # # Required ENV vars: # ------------------ @@ -114,7 +114,6 @@ if $mingw ; then M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then @@ -212,7 +211,11 @@ else if [ "$MVNW_VERBOSE" = true ]; then echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi while IFS="=" read key value; do case "$key" in (wrapperUrl) jarUrl="$value"; break ;; esac @@ -221,22 +224,38 @@ else echo "Downloading from: $jarUrl" fi wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi if command -v wget > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" fi - wget "$jarUrl" -O "$wrapperJarPath" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi elif command -v curl > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" fi - curl -o "$wrapperJarPath" "$jarUrl" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi if [ -e "$javaClass" ]; then if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then if [ "$MVNW_VERBOSE" = true ]; then @@ -277,6 +296,11 @@ if $cygwin; then MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ diff --git a/api-gateway-service/mvnw.cmd b/api-gateway-service/mvnw.cmd index fef5a8f..c8d4337 100644 --- a/api-gateway-service/mvnw.cmd +++ b/api-gateway-service/mvnw.cmd @@ -18,7 +18,7 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script +@REM Maven Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @@ -26,7 +26,7 @@ @REM Optional ENV vars @REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -37,7 +37,7 @@ @echo off @REM set title of command window title %0 -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME @@ -120,23 +120,44 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" -FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @REM This allows using the maven wrapper in projects that prohibit checking in binary data. if exist %WRAPPER_JAR% ( - echo Found %WRAPPER_JAR% + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) ) else ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" - echo Finished downloading %WRAPPER_JAR% + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) ) @REM End of extension +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end diff --git a/api-gateway-service/pom.xml b/api-gateway-service/pom.xml index a237e43..31111b1 100644 --- a/api-gateway-service/pom.xml +++ b/api-gateway-service/pom.xml @@ -1,80 +1,93 @@ + 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 - 2.2.5.RELEASE + 2.5.4 - com.javatab api-gateway-service - 1.0.0 + 0.0.1-SNAPSHOT api-gateway-service - Demo project for Spring Boot - + Gateway Service using spring cloud gateway 11 javatab - Hoxton.SR1 + 2020.0.3 - org.springframework.boot - spring-boot-starter-security + spring-boot-starter-actuator - org.springframework.boot - spring-boot-starter-web + org.springframework.cloud + spring-cloud-starter-config org.springframework.cloud - spring-cloud-starter-netflix-eureka-client + spring-cloud-starter-gateway org.springframework.cloud - spring-cloud-starter-netflix-zuul + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-ribbon + + + com.netflix.ribbon + ribbon-eureka + + - org.springframework.cloud - spring-cloud-starter-config + org.json + json + 20190722 - org.springframework.boot - spring-boot-devtools - runtime + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin io.jsonwebtoken - jjwt - 0.9.0 + jjwt-api + 0.11.1 - com.javatab - common-service - 0.0.1-SNAPSHOT + io.jsonwebtoken + jjwt-impl + 0.11.1 + runtime - org.projectlombok - lombok - true + io.jsonwebtoken + jjwt-jackson + 0.11.1 + runtime + org.springframework.boot spring-boot-starter-test test - - - org.springframework.security - spring-security-test - test + + + org.junit.vintage + junit-vintage-engine + + - @@ -118,5 +131,4 @@ - diff --git a/api-gateway-service/src/main/java/com/javatab/apigatewayservice/security/JwtTokenAuthenticationFilter.java b/api-gateway-service/src/main/java/com/javatab/apigatewayservice/security/JwtTokenAuthenticationFilter.java deleted file mode 100644 index 43561ea..0000000 --- a/api-gateway-service/src/main/java/com/javatab/apigatewayservice/security/JwtTokenAuthenticationFilter.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.javatab.apigatewayservice.security; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -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; - -public class JwtTokenAuthenticationFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - - String header = request.getHeader("Authorization"); - - if(header == null || !header.startsWith("Bearer")) { - chain.doFilter(request, response); // If not valid, go to the next filter. - return; - } - String token = header.replace("Bearer", ""); - - try { - Claims claims = Jwts.parser() - .setSigningKey("JwtSecretKey".getBytes()) - .parseClaimsJws(token) - .getBody(); - - String username = claims.getSubject(); - if(username != null) { - @SuppressWarnings("unchecked") - List authorities = (List) claims.get("authorities"); - UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( - username, null, authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())); - SecurityContextHolder.getContext().setAuthentication(auth); - } - - } catch (Exception e) { - SecurityContextHolder.clearContext(); - } - chain.doFilter(request, response); - } - -} \ No newline at end of file diff --git a/api-gateway-service/src/main/java/com/javatab/apigatewayservice/security/SecurityTokenConfig.java b/api-gateway-service/src/main/java/com/javatab/apigatewayservice/security/SecurityTokenConfig.java deleted file mode 100644 index 6303fc4..0000000 --- a/api-gateway-service/src/main/java/com/javatab/apigatewayservice/security/SecurityTokenConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.javatab.apigatewayservice.security; - -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpMethod; -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.config.http.SessionCreationPolicy; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -import javax.servlet.http.HttpServletResponse; -import java.util.Arrays; -import java.util.Collections; -import java.util.stream.Collectors; - -@EnableWebSecurity -public class SecurityTokenConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .cors().and() - .csrf().disable() - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() - .exceptionHandling().authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED)) - .and() - .addFilterAfter(new JwtTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) - .authorizeRequests() - .antMatchers(HttpMethod.POST, "/auth/**").permitAll() - .antMatchers("/admin/**").hasRole("ADMIN") - .anyRequest().authenticated(); - - } - - @Bean - public CorsFilter corsFilter() { - final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - final CorsConfiguration config = new CorsConfiguration(); - config.setAllowCredentials(true); - config.setAllowedOrigins(Collections.singletonList("*")); - config.setAllowedHeaders(Collections.singletonList("*")); - config.setAllowedMethods(Arrays.stream(HttpMethod.values()).map(HttpMethod::name).collect(Collectors.toList())); - source.registerCorsConfiguration("/**", config); - return new CorsFilter(source); - } -} diff --git a/api-gateway-service/src/main/java/com/javatab/apigatewayservice/ApiGatewayServiceApplication.java b/api-gateway-service/src/main/java/com/javatab/gatewayservice/ApiGatewayServiceApplication.java similarity index 58% rename from api-gateway-service/src/main/java/com/javatab/apigatewayservice/ApiGatewayServiceApplication.java rename to api-gateway-service/src/main/java/com/javatab/gatewayservice/ApiGatewayServiceApplication.java index 28bd900..64d05fa 100644 --- a/api-gateway-service/src/main/java/com/javatab/apigatewayservice/ApiGatewayServiceApplication.java +++ b/api-gateway-service/src/main/java/com/javatab/gatewayservice/ApiGatewayServiceApplication.java @@ -1,13 +1,11 @@ -package com.javatab.apigatewayservice; +package com.javatab.gatewayservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication -@EnableDiscoveryClient -@EnableZuulProxy +@EnableEurekaClient public class ApiGatewayServiceApplication { public static void main(String[] args) { diff --git a/api-gateway-service/src/main/java/com/javatab/gatewayservice/filters/AuthenticationFilter.java b/api-gateway-service/src/main/java/com/javatab/gatewayservice/filters/AuthenticationFilter.java new file mode 100644 index 0000000..076fc1f --- /dev/null +++ b/api-gateway-service/src/main/java/com/javatab/gatewayservice/filters/AuthenticationFilter.java @@ -0,0 +1,69 @@ +package com.javatab.gatewayservice.filters; + +import io.jsonwebtoken.Claims; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +@RefreshScope +@Component +@Order(1) +public class AuthenticationFilter implements GlobalFilter { + + @Autowired + private RouterValidator routerValidator;//custom route validator + @Autowired + private JwtUtil jwtUtil; + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + + if (routerValidator.isSecured.test(request)) { + if (this.isAuthMissing(request)) + return this.onError(exchange, "Authorization header is missing in request", HttpStatus.UNAUTHORIZED); + + final String token = this.getAuthHeader(request); + + if (jwtUtil.isInvalid(token)) + return this.onError(exchange, "Authorization header is invalid", HttpStatus.UNAUTHORIZED); + + this.populateRequestWithHeaders(exchange, token); + } + return chain.filter(exchange); + } + + + /*PRIVATE*/ + + private Mono onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(httpStatus); + return response.setComplete(); + } + + private String getAuthHeader(ServerHttpRequest request) { + return request.getHeaders().getOrEmpty("Authorization").get(0).replace("Bearer ", ""); + } + + private boolean isAuthMissing(ServerHttpRequest request) { + return !request.getHeaders().containsKey("Authorization"); + } + + private void populateRequestWithHeaders(ServerWebExchange exchange, String token) { + Claims claims = jwtUtil.getAllClaimsFromToken(token); + exchange.getRequest().mutate() + .header("id", String.valueOf(claims.get("id"))) + .header("role", String.valueOf(claims.get("role"))) + .build(); + } +} \ No newline at end of file diff --git a/api-gateway-service/src/main/java/com/javatab/gatewayservice/filters/JwtUtil.java b/api-gateway-service/src/main/java/com/javatab/gatewayservice/filters/JwtUtil.java new file mode 100644 index 0000000..be4de47 --- /dev/null +++ b/api-gateway-service/src/main/java/com/javatab/gatewayservice/filters/JwtUtil.java @@ -0,0 +1,39 @@ +package com.javatab.gatewayservice.filters; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.security.Key; +import java.util.Date; + + +@Component +public class JwtUtil { + + @Value("${jwt.secret}") + private String secret; + + private Key key; + + @PostConstruct + public void init(){ + this.key = Keys.hmacShaKeyFor(secret.getBytes()); + } + + public Claims getAllClaimsFromToken(String token) { + return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); + } + + private boolean isTokenExpired(String token) { + return this.getAllClaimsFromToken(token).getExpiration().before(new Date()); + } + + public boolean isInvalid(String token) { + return this.isTokenExpired(token); + } + +} \ No newline at end of file diff --git a/api-gateway-service/src/main/java/com/javatab/gatewayservice/filters/RouterValidator.java b/api-gateway-service/src/main/java/com/javatab/gatewayservice/filters/RouterValidator.java new file mode 100644 index 0000000..641cb32 --- /dev/null +++ b/api-gateway-service/src/main/java/com/javatab/gatewayservice/filters/RouterValidator.java @@ -0,0 +1,22 @@ +package com.javatab.gatewayservice.filters; + +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.function.Predicate; + +@Component +public class RouterValidator { + + public static final List openApiEndpoints= List.of( + "/register", + "/login" + ); + + public Predicate isSecured = + request -> openApiEndpoints + .stream() + .noneMatch(uri -> request.getURI().getPath().contains(uri)); + +} \ No newline at end of file diff --git a/api-gateway-service/src/main/resources/application.yml b/api-gateway-service/src/main/resources/application.yml new file mode 100644 index 0000000..289c7e3 --- /dev/null +++ b/api-gateway-service/src/main/resources/application.yml @@ -0,0 +1,63 @@ +server: + port: 8072 + +eureka: + instance: + prefer-ip-address: true + client: + service-url: + default-zone: http://discoveryservice:8761/eureka/ + fetch-registry: true + register-with-eureka: true + +spring: + config: + import: optional:configserver:config-server + cloud: + config: + discovery: + enabled: true + service-id: config-server + loadbalancer: + ribbon: + enabled: false + gateway: + discovery: + locator: + enabled: true + lower-case-service-id: true + routes: + - id: course-service + uri: lb://course-service + predicates: + - Path=/course/** + filters: + - RewritePath=/course/(?.*), /$\{path} + - RemoveRequestHeader= Cookie,Set-Cookie + + - id: auth-service + uri: lb://auth-service + predicates: + - Path=/auth/** + filters: + - RewritePath=/auth/(?.*), /$\{path} + - RemoveRequestHeader= Cookie,Set-Cookie + + application: + name: gateway-servlice + +logging: + level: + com.netflix: WARN + org.springframework.web: WARN + com.javatab: DEBUG + + +jwt: + secret: BvPHGM8C0ia4uOuxxqPD5DTbWC9F9TWvPStp3pb7ARo0oK2mJ3pd3YG4lxA9i8bj6OTbadwezxgeEByY + +management: + endpoints: + web: + exposure: + include: '*' \ No newline at end of file diff --git a/api-gateway-service/src/main/resources/bootstrap.yml b/api-gateway-service/src/main/resources/bootstrap.yml deleted file mode 100644 index 9de1543..0000000 --- a/api-gateway-service/src/main/resources/bootstrap.yml +++ /dev/null @@ -1,9 +0,0 @@ -spring: - application: - name: api-gateway-service - cloud: - config: - uri: http://config-server:8071 # using old spring config, upgrade! - discovery: - service-id: config-server - enabled: true \ No newline at end of file diff --git a/api-gateway-service/src/test/java/com/javatab/apigatewayservice/ApiGatewayServiceApplicationTests.java b/api-gateway-service/src/test/java/com/javatab/gatewayservice/ApiGatewayServiceApplicationTests.java similarity index 50% rename from api-gateway-service/src/test/java/com/javatab/apigatewayservice/ApiGatewayServiceApplicationTests.java rename to api-gateway-service/src/test/java/com/javatab/gatewayservice/ApiGatewayServiceApplicationTests.java index 268b96a..644fd1a 100644 --- a/api-gateway-service/src/test/java/com/javatab/apigatewayservice/ApiGatewayServiceApplicationTests.java +++ b/api-gateway-service/src/test/java/com/javatab/gatewayservice/ApiGatewayServiceApplicationTests.java @@ -1,13 +1,14 @@ -package com.javatab.apigatewayservice; +package com.javatab.gatewayservice; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -public class ApiGatewayServiceApplicationTests { +class ApiGatewayServiceApplicationTests { @Disabled - public void contextLoads() { + void contextLoads() { } } diff --git a/auth-service/pom.xml b/auth-service/pom.xml index e738b5f..7aa52ff 100644 --- a/auth-service/pom.xml +++ b/auth-service/pom.xml @@ -20,15 +20,12 @@ 11 javatab 2020.0.3 + 0.4 - - - org.springframework.boot - spring-boot-starter-security - + org.springframework.boot spring-boot-starter-web @@ -39,8 +36,39 @@ io.jsonwebtoken - jjwt - 0.9.0 + jjwt-api + 0.11.1 + + + io.jsonwebtoken + jjwt-impl + 0.11.1 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.1 + runtime + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.postgresql + postgresql + runtime + + + + org.mindrot + jbcrypt + ${jbcrypt.version} com.google.code.gson @@ -66,6 +94,20 @@ org.springframework.cloud spring-cloud-starter-config + + + + jakarta.xml.bind + jakarta.xml.bind-api + 2.3.2 + + + + + org.glassfish.jaxb + jaxb-runtime + 2.3.2 + diff --git a/auth-service/src/main/java/com/javatab/authservice/AuthServiceApplication.java b/auth-service/src/main/java/com/javatab/authservice/AuthServiceApplication.java index 10c4dcb..5ffe6e3 100644 --- a/auth-service/src/main/java/com/javatab/authservice/AuthServiceApplication.java +++ b/auth-service/src/main/java/com/javatab/authservice/AuthServiceApplication.java @@ -3,7 +3,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableEurekaClient @@ -13,4 +16,10 @@ public static void main(String[] args) { SpringApplication.run(AuthServiceApplication.class, args); } + @Bean + @LoadBalanced + public RestTemplate restTemplate() { + return new RestTemplate(); + } + } diff --git a/auth-service/src/main/java/com/javatab/authservice/controllers/AuthController.java b/auth-service/src/main/java/com/javatab/authservice/controllers/AuthController.java new file mode 100644 index 0000000..a13174d --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/controllers/AuthController.java @@ -0,0 +1,27 @@ +package com.javatab.authservice.controllers; + +import com.javatab.authservice.domain.AuthRequest; +import com.javatab.authservice.domain.AuthResponse; +import com.javatab.authservice.domain.User; +import com.javatab.authservice.security.JwtUserDetailService; +import com.javatab.authservice.services.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +public class AuthController { + + private final JwtUserDetailService userDetailService; + private final UserService userService; + + @PostMapping("/login") + public AuthResponse login(@RequestBody AuthRequest request) throws Exception { + return userDetailService.createJwtToken(request); + } + + @PostMapping("/register") + public User registerNewUser(@RequestBody User user) { + return userService.createNewUser(user); + } +} diff --git a/auth-service/src/main/java/com/javatab/authservice/controllers/RoleController.java b/auth-service/src/main/java/com/javatab/authservice/controllers/RoleController.java new file mode 100644 index 0000000..d461711 --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/controllers/RoleController.java @@ -0,0 +1,22 @@ +package com.javatab.authservice.controllers; + +import com.javatab.authservice.domain.Role; +import com.javatab.authservice.repository.RoleRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/roles") +public class RoleController { + + @Autowired + private RoleRepository roleRepository; + + @PostMapping + public Role createNewRole(@RequestBody Role role) { + return roleRepository.save(role); + } +} diff --git a/auth-service/src/main/java/com/javatab/authservice/domain/AuthRequest.java b/auth-service/src/main/java/com/javatab/authservice/domain/AuthRequest.java new file mode 100644 index 0000000..7cdfaac --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/domain/AuthRequest.java @@ -0,0 +1,17 @@ +package com.javatab.authservice.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AuthRequest { + + private String email; + private String password; + private String username; +} diff --git a/auth-service/src/main/java/com/javatab/authservice/domain/AuthResponse.java b/auth-service/src/main/java/com/javatab/authservice/domain/AuthResponse.java new file mode 100644 index 0000000..a87d98a --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/domain/AuthResponse.java @@ -0,0 +1,17 @@ +package com.javatab.authservice.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AuthResponse { + + private String accessToken; + private String refreshToken; + +} diff --git a/auth-service/src/main/java/com/javatab/authservice/domain/Role.java b/auth-service/src/main/java/com/javatab/authservice/domain/Role.java new file mode 100644 index 0000000..5363902 --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/domain/Role.java @@ -0,0 +1,22 @@ +package com.javatab.authservice.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.Set; + +@Entity +@Table(name = "roles") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Role { + + @Id + @Column(name = "role") + private String roleName; + + private String roleDescription; +} diff --git a/auth-service/src/main/java/com/javatab/authservice/domain/User.java b/auth-service/src/main/java/com/javatab/authservice/domain/User.java new file mode 100644 index 0000000..8bb2fb6 --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/domain/User.java @@ -0,0 +1,36 @@ +package com.javatab.authservice.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.Set; + +@Entity +@Table(name = "Users") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(name = "username") + private String username; + + @Column(name = "password") + private String password; + + @Column(name = "email") + private String email; + + @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}) + @JoinTable(name = "user_role", + joinColumns = @JoinColumn(name = "user_id"), + inverseJoinColumns = @JoinColumn(name = "role_id")) + private Set roles; +} + diff --git a/auth-service/src/main/java/com/javatab/authservice/model/LoggedInUserDetails.java b/auth-service/src/main/java/com/javatab/authservice/model/LoggedInUserDetails.java deleted file mode 100644 index 218b520..0000000 --- a/auth-service/src/main/java/com/javatab/authservice/model/LoggedInUserDetails.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.javatab.authservice.model; - -import lombok.Data; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -@Data -public class LoggedInUserDetails { - private final String username; - private final String token; -} diff --git a/auth-service/src/main/java/com/javatab/authservice/repository/RoleRepository.java b/auth-service/src/main/java/com/javatab/authservice/repository/RoleRepository.java new file mode 100644 index 0000000..be8ad36 --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/repository/RoleRepository.java @@ -0,0 +1,7 @@ +package com.javatab.authservice.repository; + +import com.javatab.authservice.domain.Role; +import org.springframework.data.repository.CrudRepository; + +public interface RoleRepository extends CrudRepository { +} diff --git a/auth-service/src/main/java/com/javatab/authservice/repository/UserRepository.java b/auth-service/src/main/java/com/javatab/authservice/repository/UserRepository.java new file mode 100644 index 0000000..5a576f3 --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/repository/UserRepository.java @@ -0,0 +1,14 @@ +package com.javatab.authservice.repository; + +import com.javatab.authservice.domain.User; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface UserRepository extends CrudRepository { + + Optional findByUsername(String username); +} + diff --git a/api-gateway-service/src/main/java/com/javatab/apigatewayservice/security/JwtAuthenticationEntryPoint.java b/auth-service/src/main/java/com/javatab/authservice/security/JwtAuthenticationEntryPoint.java similarity index 50% rename from api-gateway-service/src/main/java/com/javatab/apigatewayservice/security/JwtAuthenticationEntryPoint.java rename to auth-service/src/main/java/com/javatab/authservice/security/JwtAuthenticationEntryPoint.java index 09f150d..cc04ef1 100644 --- a/api-gateway-service/src/main/java/com/javatab/apigatewayservice/security/JwtAuthenticationEntryPoint.java +++ b/auth-service/src/main/java/com/javatab/authservice/security/JwtAuthenticationEntryPoint.java @@ -1,5 +1,7 @@ -package com.javatab.apigatewayservice.security; +package com.javatab.authservice.security; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; @@ -10,14 +12,12 @@ import java.io.IOException; @Component +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Override - public void commence(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "UNAUTHORIZED"); - String json = String.format("{\"message\": \"%s\"}", e.getMessage()); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response.setContentType("application/json"); - response.setCharacterEncoding("UTF-8"); - response.getWriter().write(json); + public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { + httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); } -} \ No newline at end of file +} diff --git a/auth-service/src/main/java/com/javatab/authservice/security/JwtConfig.java b/auth-service/src/main/java/com/javatab/authservice/security/JwtConfig.java deleted file mode 100644 index 9d3ed86..0000000 --- a/auth-service/src/main/java/com/javatab/authservice/security/JwtConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.javatab.authservice.security; - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import org.springframework.beans.factory.annotation.Value; - -@Getter // lombok will create getters auto. -@Setter -@ToString // [IMP] You need to install lombok jar file: https://stackoverflow.com/a/11807022 -public class JwtConfig { - - // Spring doesn't inject/autowire to "static" fields. - // Link: https://stackoverflow.com/a/6897406 - @Value("${security.jwt.uri:/auth/**}") - private String Uri; - - @Value("${security.jwt.header:Authorization}") - private String header; - - @Value("${security.jwt.prefix:Bearer }") - private String prefix; - - @Value("${security.jwt.expiration:#{24*60*60}}") - private int expiration; - - @Value("${security.jwt.secret:JwtSecretKey}") - private String secret; - -} \ No newline at end of file diff --git a/auth-service/src/main/java/com/javatab/authservice/security/JwtRequestFilter.java b/auth-service/src/main/java/com/javatab/authservice/security/JwtRequestFilter.java new file mode 100644 index 0000000..c4e258c --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/security/JwtRequestFilter.java @@ -0,0 +1,60 @@ +package com.javatab.authservice.security; + +import io.jsonwebtoken.ExpiredJwtException; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +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; + +@Component +public class JwtRequestFilter extends OncePerRequestFilter { + + private final JwtUtil jwtUtil; + private final JwtUserDetailService userDetailService; + + public JwtRequestFilter(JwtUserDetailService userDetailService, JwtUtil jwtUtil) { + this.userDetailService = userDetailService; + this.jwtUtil = jwtUtil; + } + + @Override + protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { + final String header = httpServletRequest.getHeader("Authorization"); + String jwtToken = null; + String userName = null; + if (header != null && header.startsWith("Bearer ")) { + jwtToken = header.substring(7); + try { + userName = jwtUtil.getUserNameFromToken(jwtToken); + } catch (IllegalArgumentException e) { + System.out.println("Unable to get JWT token"); + } catch (ExpiredJwtException e) { + System.out.println("Jwt token is expired"); + } + } else { + System.out.println("Jwt token does not start with Bearer"); + } + + if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = userDetailService.loadUserByUsername(userName); + + if (jwtUtil.validateToken(jwtToken, userDetails)) { + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); + SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); + } + } + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + +} diff --git a/auth-service/src/main/java/com/javatab/authservice/security/JwtUserDetailService.java b/auth-service/src/main/java/com/javatab/authservice/security/JwtUserDetailService.java new file mode 100644 index 0000000..881d810 --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/security/JwtUserDetailService.java @@ -0,0 +1,74 @@ +package com.javatab.authservice.security; + +import com.javatab.authservice.domain.AuthRequest; +import com.javatab.authservice.domain.AuthResponse; +import com.javatab.authservice.domain.User; +import com.javatab.authservice.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Set; + +@Component +public class JwtUserDetailService implements UserDetailsService { + + private final UserRepository userRepository; + private final JwtUtil jwtUtil; + private final AuthenticationManager authenticationManager; + + public JwtUserDetailService(UserRepository userRepository, JwtUtil jwtUtil, @Lazy AuthenticationManager authenticationManager) { + this.userRepository = userRepository; + this.jwtUtil = jwtUtil; + this.authenticationManager = authenticationManager; + } + + public AuthResponse createJwtToken(AuthRequest jwtRequest) throws Exception{ + String userName = jwtRequest.getUsername(); + String userPassword = jwtRequest.getPassword(); + authenticate(userName, userPassword); + final UserDetails userDetails = loadUserByUsername(userName); + String accessToken = jwtUtil.generate(userDetails, "ACCESS"); + String refreshToken = jwtUtil.generate(userDetails, "REFRESH"); + + return new AuthResponse(accessToken, refreshToken); + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + User user = userRepository.findByUsername(username).get(); + if (user != null) { + return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getAuthorities(user)); + } else { + throw new UsernameNotFoundException("Username is not valid"); + } + } + + private Set getAuthorities(User user) { + Set authorities = new HashSet<>(); + user.getRoles().forEach(role -> { + authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName())); + }); + return authorities; + } + + private void authenticate(String userName, String userPassword) throws Exception{ + try { + authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, userPassword)); + } catch (DisabledException e) { + throw new Exception("User is disabled"); + } catch(BadCredentialsException e) { + throw new Exception("Bad credentials from user"); + } + } +} diff --git a/auth-service/src/main/java/com/javatab/authservice/security/JwtUsernameAndPasswordAuthenticationFilter.java b/auth-service/src/main/java/com/javatab/authservice/security/JwtUsernameAndPasswordAuthenticationFilter.java deleted file mode 100644 index 2a2b849..0000000 --- a/auth-service/src/main/java/com/javatab/authservice/security/JwtUsernameAndPasswordAuthenticationFilter.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.javatab.authservice.security; - -import com.javatab.authservice.model.LoggedInUserDetails; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.Gson; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.sql.Date; -import java.util.Collections; -import java.util.stream.Collectors; - -public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { - - private AuthenticationManager authManager; - - private final JwtConfig jwtConfig; - - public JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authManager, JwtConfig jwtConfig) { - this.authManager = authManager; - this.jwtConfig = jwtConfig; - this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(jwtConfig.getUri(), "POST")); - } - - @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) - throws AuthenticationException { - - try { - UserCredentials creds = new ObjectMapper().readValue(request.getInputStream(), UserCredentials.class); - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( - creds.getUsername(), creds.getPassword(), Collections.emptyList()); - return authManager.authenticate(authToken); - - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, - Authentication auth) throws IOException, ServletException { - - Long now = System.currentTimeMillis(); - String token = Jwts.builder() - .setSubject(auth.getName()) - .claim("authorities", auth.getAuthorities().stream() - .map(GrantedAuthority::getAuthority).collect(Collectors.toList())) - .setIssuedAt(new Date(now)) - .setExpiration(new Date(now + jwtConfig.getExpiration() * 1000)) - .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret().getBytes()) - .compact(); - //response.addHeader(jwtConfig.getHeader(), jwtConfig.getPrefix() + token); - LoggedInUserDetails userDetails = new LoggedInUserDetails(auth.getName(), token); - String loggedInUserDetails = new Gson().toJson(userDetails); - PrintWriter out = response.getWriter(); - response.setContentType("application/json"); - response.setCharacterEncoding("UTF-8"); - out.print(loggedInUserDetails); - out.flush(); - - } - - // A (temporary) class just to represent the user credentials - private static class UserCredentials { - private String username, password; - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - public String getPassword() { - return password; - } - public void setPassword(String password) { - this.password = password; - } - } -} \ No newline at end of file diff --git a/auth-service/src/main/java/com/javatab/authservice/security/JwtUtil.java b/auth-service/src/main/java/com/javatab/authservice/security/JwtUtil.java new file mode 100644 index 0000000..b052954 --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/security/JwtUtil.java @@ -0,0 +1,86 @@ +package com.javatab.authservice.security; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.security.Key; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +@Component +public class JwtUtil { + + @Value("${jwt.secret}") + private String secret; + + @Value("${jwt.expiration}") + private String expirationTime; + + private Key key; + + @PostConstruct + public void init() { + this.key = Keys.hmacShaKeyFor(secret.getBytes()); + } + + public Claims getAllClaimsFromToken(String token) { + return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); + } + + + public Date getExpirationDateFromToken(String token) { + return getAllClaimsFromToken(token).getExpiration(); + } + + private Boolean isTokenExpired(String token) { + final Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } + + public String generate(UserDetails user, String type) { + Map claims = new HashMap<>(); + claims.put("id", user.getUsername()); + claims.put("role", user.getAuthorities()); + return doGenerateToken(claims, user.getUsername(), type); + } + + private String doGenerateToken(Map claims, String username, String type) { + long expirationTimeLong; + if ("ACCESS".equals(type)) { + expirationTimeLong = Long.parseLong(expirationTime) * 1000; + } else { + expirationTimeLong = Long.parseLong(expirationTime) * 1000 * 5; + } + final Date createdDate = new Date(); + final Date expirationDate = new Date(createdDate.getTime() + expirationTimeLong); + + return Jwts.builder() + .setClaims(claims) + .setSubject(username) + .setIssuedAt(createdDate) + .setExpiration(expirationDate) + .signWith(key) + .compact(); + } + + public Boolean validateToken(String token, UserDetails userDetails) { + String userName = getUserNameFromToken(token); + return (userName.equals(userDetails.getUsername()) && !isTokenExpired(token)); + } + + public String getUserNameFromToken(String token) { + return getClaimFromToken(token, Claims::getSubject); + } + + private T getClaimFromToken(String token, Function claimResolver) { + final Claims claims = getAllClaimsFromToken(token); + return claimResolver.apply(claims); + } +} \ No newline at end of file diff --git a/auth-service/src/main/java/com/javatab/authservice/security/SecurityCredentialsConfig.java b/auth-service/src/main/java/com/javatab/authservice/security/SecurityCredentialsConfig.java deleted file mode 100644 index b96b9d4..0000000 --- a/auth-service/src/main/java/com/javatab/authservice/security/SecurityCredentialsConfig.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.javatab.authservice.security; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -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.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; - -import javax.servlet.http.HttpServletResponse; - - -@EnableWebSecurity // Enable security config. This annotation denotes config for spring security. -public class SecurityCredentialsConfig extends WebSecurityConfigurerAdapter { - - @Autowired - private UserDetailsService userDetailsService; - - @Autowired - private JwtConfig jwtConfig; - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .csrf().disable() - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() - .exceptionHandling().authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED)) - .and() - .addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtConfig)) - .authorizeRequests() - .antMatchers(HttpMethod.POST, jwtConfig.getUri()).permitAll() - .anyRequest().authenticated(); - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); - } - - @Bean - public JwtConfig jwtConfig() { - return new JwtConfig(); - } - - @Bean - public BCryptPasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} \ No newline at end of file diff --git a/auth-service/src/main/java/com/javatab/authservice/security/UserDetailsServiceImpl.java b/auth-service/src/main/java/com/javatab/authservice/security/UserDetailsServiceImpl.java deleted file mode 100644 index 6dcf026..0000000 --- a/auth-service/src/main/java/com/javatab/authservice/security/UserDetailsServiceImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.javatab.authservice.security; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -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.bcrypt.BCryptPasswordEncoder; -import org.springframework.stereotype.Service; - -import java.util.Arrays; -import java.util.List; - -@Service // It has to be annotated with @Service. -public class UserDetailsServiceImpl implements UserDetailsService { - - @Autowired - private BCryptPasswordEncoder encoder; - - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - - // hard coding the users. All passwords must be encoded. - final List users = Arrays.asList( - new AppUser(1, "nasruddin", encoder.encode("password"), "USER"), - new AppUser(2, "user", encoder.encode("password"), "USER"), - new AppUser(3, "admin", encoder.encode("password"), "ADMIN") - ); - - - for(AppUser appUser: users) { - if(appUser.getUsername().equals(username)) { - List grantedAuthorities = AuthorityUtils - .commaSeparatedStringToAuthorityList("ROLE_" + appUser.getRole()); - return new User(appUser.getUsername(), appUser.getPassword(), grantedAuthorities); - } - } - throw new UsernameNotFoundException("Username: " + username + " not found"); - } - - private static class AppUser { - private Integer id; - private String username, password; - private String role; - - public AppUser(Integer id, String username, String password, String role) { - this.id = id; - this.username = username; - this.password = password; - this.role = role; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - public String getRole() { - return role; - } - - public void setRole(String role) { - this.role = role; - } - } -} \ No newline at end of file diff --git a/auth-service/src/main/java/com/javatab/authservice/security/WebSecurityConfiguration.java b/auth-service/src/main/java/com/javatab/authservice/security/WebSecurityConfiguration.java new file mode 100644 index 0000000..eac2da9 --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/security/WebSecurityConfiguration.java @@ -0,0 +1,57 @@ +package com.javatab.authservice.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +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.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@RequiredArgsConstructor +public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + private final JwtAuthenticationEntryPoint jwtAuthenticationEntityPoint; + private final JwtRequestFilter jwtRequestFilter; + private final UserDetailsService jwtService; + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.cors(); + http.csrf().disable() + .authorizeRequests() + .antMatchers("/register", "/login").permitAll() + //.antMatchers(HttpHeaders.ALLOW).permitAll() + .anyRequest().authenticated() + .and() + .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntityPoint) + .and() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + + http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); + + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder builder) throws Exception { + builder.userDetailsService(jwtService).passwordEncoder(passwordEncoder()); + } +} diff --git a/auth-service/src/main/java/com/javatab/authservice/services/UserService.java b/auth-service/src/main/java/com/javatab/authservice/services/UserService.java new file mode 100644 index 0000000..03f21de --- /dev/null +++ b/auth-service/src/main/java/com/javatab/authservice/services/UserService.java @@ -0,0 +1,34 @@ +package com.javatab.authservice.services; + +import com.javatab.authservice.domain.Role; +import com.javatab.authservice.domain.User; +import com.javatab.authservice.repository.RoleRepository; +import com.javatab.authservice.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.Set; + +@Service +@RequiredArgsConstructor +public class UserService { + + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + private final RoleRepository roleRepository; + + public User createNewUser(User user) { + Role role = roleRepository.findById("USER").get(); + Set roles = new HashSet<>(); + roles.add(role); + user.setRoles(roles); + user.setPassword(getEncodedPassword(user.getPassword())); + return userRepository.save(user); + } + + private String getEncodedPassword(String password) { + return passwordEncoder.encode(password); + } +} diff --git a/auth-service/src/main/resources/application.yml b/auth-service/src/main/resources/application.yml index 3c92003..aa02778 100644 --- a/auth-service/src/main/resources/application.yml +++ b/auth-service/src/main/resources/application.yml @@ -7,4 +7,24 @@ spring: config: discovery: enabled: true - service-id: config-server \ No newline at end of file + service-id: config-server + + datasource: + url: jdbc:postgresql://localhost:5432/testdb + username: postgres + password: 123 + jpa: + properties: + hibernate: + dialect: org.hibernate.dialect.PostgreSQLDialect + + hibernate: + ddl-auto: update +eureka: + client: + service-url: + default-zone: http://discoveryservice:8761/eureka/ + +jwt: + secret: BvPHGM8C0ia4uOuxxqPD5DTbWC9F9TWvPStp3pb7ARo0oK2mJ3pd3YG4lxA9i8bj6OTbadwezxgeEByY + expiration: 86400 \ No newline at end of file diff --git a/course-service/src/main/resources/application.yml b/course-service/src/main/resources/application.yml index a8cd132..8b1fd38 100644 --- a/course-service/src/main/resources/application.yml +++ b/course-service/src/main/resources/application.yml @@ -7,4 +7,9 @@ spring: config: discovery: enabled: true - service-id: config-server \ No newline at end of file + service-id: config-server + +eureka: + client: + service-url: + default-zone: http://discoveryservice:8761/eureka/ \ No newline at end of file diff --git a/docker/database.env b/docker/database.env new file mode 100644 index 0000000..8dcbf1a --- /dev/null +++ b/docker/database.env @@ -0,0 +1,4 @@ +# database.env +POSTGRES_USER=postgres +POSTGRES_PASSWORD=123 +POSTGRES_DB=testdb \ No newline at end of file diff --git a/docker/docker-compose-keycloak.yml b/docker/docker-compose-keycloak.yml new file mode 100644 index 0000000..d7e8f3d --- /dev/null +++ b/docker/docker-compose-keycloak.yml @@ -0,0 +1,18 @@ +version: '3.8' # Migrate to version 3 +services: + keycloak: + image: jboss/keycloak + restart: always + environment: + KEYCLOAK_USER: admin + KEYCLOAK_PASSWORD: admin + ports: + - "8080:8080" + networks: + backend: + aliases: + - "keycloak" + +networks: + backend: + driver: bridge \ No newline at end of file diff --git a/docker/docker-compose-mongo.yml b/docker/docker-compose-mongo.yml new file mode 100644 index 0000000..aed2805 --- /dev/null +++ b/docker/docker-compose-mongo.yml @@ -0,0 +1,15 @@ +version: '3.8' +services: + mongodb_container: + image: mongo:latest + restart: always + #environment: + # MONGO_INITDB_ROOT_USERNAME: root + # MONGO_INITDB_ROOT_PASSWORD: rootpassword + ports: + - 27017:27017 + volumes: + - mongodb_data_container:/data/db + +volumes: + mongodb_data_container: \ No newline at end of file diff --git a/docker/docker-compose-postgres.yml b/docker/docker-compose-postgres.yml new file mode 100644 index 0000000..f26a8f8 --- /dev/null +++ b/docker/docker-compose-postgres.yml @@ -0,0 +1,13 @@ +version: '3.8' +services: + postgres_container1: + restart: always + image: "postgres:latest" # use latest official postgres version + env_file: + - database.env # configure postgres + ports: + - "5432:5432" + volumes: + - database-data:/var/lib/postgresql/data/ # persist data even if container shuts down +volumes: + database-data: # named volumes can be managed easier using docker-compose \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3f2b34c..6b04538 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2.1' +version: '3.8' # Migrate to version 3 services: discoveryservice: image: javatab/discovery-service:1.0.0 @@ -9,12 +9,14 @@ services: backend: aliases: - "discoveryservice" - healthcheck: - test: [ "CMD", "curl", "-f", "http://discoveryservice:8761/eureka/" ] - interval: 30s - timeout: 10s - retries: 5 + #healthcheck: + # test: [ "CMD", "curl", "-f", "http://discoveryservice:8761/eureka/" ] + # interval: 30s + # timeout: 10s + # retries: 5 configserver: + depends_on: + - discoveryservice image: javatab/config-server:1.0.0 container_name: configServer ports: @@ -23,10 +25,10 @@ services: backend: aliases: - "configserver" - depends_on: - discoveryservice: - condition: service_healthy auth-service: + depends_on: + - discoveryservice + - configserver image: javatab/auth-service:1.0.0 container_name: authService ports: @@ -35,22 +37,18 @@ services: backend: aliases: - "authservice" - depends_on: - discoveryservice: - condition: service_healthy - configserver: - condition: service_started apigatewayservice: + depends_on: + - discoveryservice + - configserver image: javatab/api-gateway-service:1.0.0 container_name: apiGatewayService ports: - "8762:8762" - depends_on: - discoveryservice: - condition: service_healthy - configserver: - condition: service_started courseservice: + depends_on: + - discoveryservice + - configserver image: javatab/course-service:1.0.0 container_name: courseService ports: @@ -59,11 +57,6 @@ services: backend: aliases: - "course-service" - depends_on: - discoveryservice: - condition: service_healthy - configserver: - condition: service_started networks: backend: diff --git a/pom.xml b/pom.xml index b40a905..3360226 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,4 @@ api-gateway-service auth-service - - \ No newline at end of file diff --git a/student-service/.gitignore b/student-service/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/student-service/.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/student-service/.mvn/wrapper/MavenWrapperDownloader.java b/student-service/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..e76d1f3 --- /dev/null +++ b/student-service/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/student-service/.mvn/wrapper/maven-wrapper.jar b/student-service/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/student-service/.mvn/wrapper/maven-wrapper.jar differ diff --git a/student-service/.mvn/wrapper/maven-wrapper.properties b/student-service/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..abd303b --- /dev/null +++ b/student-service/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/student-service/mvnw b/student-service/mvnw new file mode 100755 index 0000000..a16b543 --- /dev/null +++ b/student-service/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/student-service/mvnw.cmd b/student-service/mvnw.cmd new file mode 100644 index 0000000..c8d4337 --- /dev/null +++ b/student-service/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/student-service/pom.xml b/student-service/pom.xml new file mode 100644 index 0000000..f97bbca --- /dev/null +++ b/student-service/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.5.4 + + + com.javatab + student-service + 0.0.1-SNAPSHOT + student-service + Demo project for Spring Boot + + 11 + javatab + 2020.0.3 + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + org.springframework.cloud + spring-cloud-starter-config + + + org.projectlombok + lombok + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + com.spotify + dockerfile-maven-plugin + 1.4.13 + + ${docker.image.prefix}/${project.artifactId} + ${project.version} + + target/${project.build.finalName}.jar + + + + + default + install + + build + + + + + + + + diff --git a/student-service/src/main/java/com/javatab/student/Student.java b/student-service/src/main/java/com/javatab/student/Student.java new file mode 100644 index 0000000..b68bc1e --- /dev/null +++ b/student-service/src/main/java/com/javatab/student/Student.java @@ -0,0 +1,27 @@ +package com.javatab.student; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.bson.types.ObjectId; +import org.springframework.data.mongodb.core.mapping.Document; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +@Document(collection = "users") +public class Student { + + + @JsonSerialize(using = ToStringSerializer.class) + private ObjectId id; + private String username; + private String email; + private String password; + private String role; + +} diff --git a/student-service/src/main/java/com/javatab/student/StudentController.java b/student-service/src/main/java/com/javatab/student/StudentController.java new file mode 100644 index 0000000..267a666 --- /dev/null +++ b/student-service/src/main/java/com/javatab/student/StudentController.java @@ -0,0 +1,22 @@ +package com.javatab.student; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(value = "/students") +public class StudentController { + + @Autowired + StudentRepository studentRepository; + + @PostMapping + public Student save(@RequestBody Student student) { + return studentRepository.save(student); + } + + @GetMapping("{username}") + public Student getUsername(@PathVariable("username") String username) { + return studentRepository.findByUsername(username).get(); + } +} diff --git a/student-service/src/main/java/com/javatab/student/StudentRepository.java b/student-service/src/main/java/com/javatab/student/StudentRepository.java new file mode 100644 index 0000000..47dece1 --- /dev/null +++ b/student-service/src/main/java/com/javatab/student/StudentRepository.java @@ -0,0 +1,12 @@ +package com.javatab.student; + +import org.bson.types.ObjectId; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface StudentRepository extends MongoRepository { + Optional findByUsername(String username); +} diff --git a/student-service/src/main/java/com/javatab/student/StudentServiceApplication.java b/student-service/src/main/java/com/javatab/student/StudentServiceApplication.java new file mode 100644 index 0000000..e9b235a --- /dev/null +++ b/student-service/src/main/java/com/javatab/student/StudentServiceApplication.java @@ -0,0 +1,22 @@ +package com.javatab.student; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +@SpringBootApplication +public class StudentServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(StudentServiceApplication.class, args); + } + + @Bean + @LoadBalanced + public RestTemplate restTemplate() { + return new RestTemplate(); + } + +} diff --git a/student-service/src/main/resources/application.yml b/student-service/src/main/resources/application.yml new file mode 100644 index 0000000..2641e6d --- /dev/null +++ b/student-service/src/main/resources/application.yml @@ -0,0 +1,21 @@ +spring: + application: + name: student-service + config: + import: optional:configserver:config-server + cloud: + config: + discovery: + enabled: true + service-id: config-server + data: + mongodb: + host: localhost + port: 27017 + database: student_db + +eureka: + client: + service-url: + default-zone: http://discoveryservice:8761/eureka/ + diff --git a/student-service/src/test/java/com/javatab/student/StudentServiceApplicationTests.java b/student-service/src/test/java/com/javatab/student/StudentServiceApplicationTests.java new file mode 100644 index 0000000..011c35a --- /dev/null +++ b/student-service/src/test/java/com/javatab/student/StudentServiceApplicationTests.java @@ -0,0 +1,13 @@ +package com.javatab.student; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class StudentServiceApplicationTests { + + @Test + void contextLoads() { + } + +}