├── .gitignore ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── java │ └── pl │ │ └── nullpointerexception │ │ └── restapi │ │ ├── SpringBootTutorialYoutube.java │ │ ├── config │ │ ├── Config.java │ │ ├── JsonObjectAuthenticationFilter.java │ │ ├── JwtAuthorizationFilter.java │ │ ├── LoginCredentials.java │ │ ├── RestAuthenticationFailureHandler.java │ │ ├── RestAuthenticationSuccessHandler.java │ │ └── SecurityConfig.java │ │ ├── controller │ │ ├── LoginController.java │ │ ├── PostController.java │ │ ├── PostDtoMapper.java │ │ └── dto │ │ │ └── PostDto.java │ │ ├── model │ │ ├── Comment.java │ │ └── Post.java │ │ ├── repository │ │ ├── CommentRepository.java │ │ └── PostRepository.java │ │ └── service │ │ └── PostService.java └── resources │ ├── application.properties │ ├── database │ ├── 2020-09-29 │ │ ├── 01-create-post.sql │ │ ├── 02-create-comment.sql │ │ └── 03-post-comment-data.sql │ └── 2020-10-13 │ │ └── 01-create-user-authorities.sql │ ├── ehcache.xml │ └── liquibase-changeLog.xml └── test ├── java └── pl │ └── nullpointerexception │ └── restapi │ ├── controller │ ├── LoginControllerTest.java │ └── PostControllerTest.java │ └── service │ └── PostServiceTest.java └── resources └── application.properties /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/** 6 | !**/src/test/** 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | out/ 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpringBootTutorialYoutube 2 | 3 | To jest repozytorium do mojego kursu Spring Boota na Youtube'ie 4 | 5 | [Kurs Spring Boot](https://www.youtube.com/playlist?list=PLLIGVl2WVN6ugud2cc3OShwWoTt65jzSL) 6 | 7 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.2.6.RELEASE' 3 | id 'io.spring.dependency-management' version '1.0.9.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'pl.nullpointerexception' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | compileJava { 18 | options.compilerArgs << '-parameters' 19 | } 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | dependencies { 26 | implementation 'org.springframework.boot:spring-boot-starter-web' 27 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 28 | implementation 'org.springframework.boot:spring-boot-starter-security' 29 | implementation 'org.springframework.boot:spring-boot-starter-cache' 30 | implementation 'com.auth0:java-jwt:3.8.3' 31 | implementation 'org.liquibase:liquibase-core' 32 | 33 | implementation 'org.ehcache:ehcache:3.8.1' 34 | implementation 'javax.cache:cache-api:1.1.1' 35 | 36 | compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2' 37 | compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2' 38 | 39 | runtimeOnly 'mysql:mysql-connector-java' 40 | compileOnly 'org.projectlombok:lombok' 41 | 42 | annotationProcessor 'org.projectlombok:lombok' 43 | testImplementation('org.springframework.boot:spring-boot-starter-test') { 44 | exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' 45 | } 46 | testImplementation 'org.springframework.security:spring-security-test' 47 | } 48 | 49 | test { 50 | useJUnitPlatform() 51 | } 52 | 53 | task generatePostsAndComments() { 54 | doLast { 55 | File dataSql = file("src/main/resources/data.sql") 56 | dataSql.write("") 57 | //posty 58 | for (int i = 1; i <= 100; i++) { 59 | dataSql.append("insert into post(id, title, content, created) " + 60 | "values (${i}, 'Test post ${i}', 'Content ${i}', '"+ LocalDateTime.now().minusDays(100 - i) +"');\n") 61 | } 62 | //komentarze 63 | for (int i = 1; i <= 100; i++) { 64 | int postId = 1 + i / 10 65 | dataSql.append("insert into comment(id, post_id, content, created) " + 66 | "values (${i}, ${postId}, 'Comment ${i}', '"+ LocalDateTime.now().minusDays(100 - i) +"');\n") 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matdabrowski/SpringBootTutorialYoutube/ee0838e238affc0b6c521903daf05b3f3e6e368d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'SpringBootTutorialYoutube' 2 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/SpringBootTutorialYoutube.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootTutorialYoutube { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootTutorialYoutube.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/config/Config.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.config; 2 | 3 | import org.springframework.cache.annotation.EnableCaching; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.service.ApiKey; 9 | import springfox.documentation.service.AuthorizationScope; 10 | import springfox.documentation.service.SecurityReference; 11 | import springfox.documentation.service.SecurityScheme; 12 | import springfox.documentation.spi.DocumentationType; 13 | import springfox.documentation.spi.service.contexts.SecurityContext; 14 | import springfox.documentation.spring.web.plugins.Docket; 15 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 16 | 17 | import java.util.List; 18 | 19 | import static java.util.Collections.singletonList; 20 | 21 | @Configuration 22 | @EnableSwagger2 23 | @EnableCaching 24 | public class Config { 25 | 26 | @Bean 27 | public Docket swaggerApi() { 28 | return new Docket(DocumentationType. SWAGGER_2 ) 29 | .ignoredParameterTypes(UsernamePasswordAuthenticationToken.class) 30 | .select() 31 | .paths(PathSelectors. regex ( "^(?!/(error).*$).*$" )) 32 | .build() 33 | .securitySchemes(singletonList(createSchema())) 34 | .securityContexts(singletonList(createContext())); 35 | } 36 | 37 | private SecurityContext createContext() { 38 | return SecurityContext.builder() 39 | .securityReferences(createRef()) 40 | .forPaths(PathSelectors.any()) 41 | .build(); 42 | } 43 | 44 | private List createRef() { 45 | AuthorizationScope authorizationScope = new AuthorizationScope( 46 | "global", "accessEverything"); 47 | AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; 48 | authorizationScopes[0] = authorizationScope; 49 | return singletonList(new SecurityReference("apiKey", authorizationScopes)); 50 | } 51 | 52 | private SecurityScheme createSchema() { 53 | return new ApiKey("apiKey", "Authorization", "header"); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/config/JsonObjectAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.AuthenticationException; 7 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.BufferedReader; 12 | import java.io.IOException; 13 | 14 | public class JsonObjectAuthenticationFilter extends UsernamePasswordAuthenticationFilter { 15 | 16 | private final ObjectMapper objectMapper; 17 | 18 | public JsonObjectAuthenticationFilter(ObjectMapper objectMapper) { 19 | this.objectMapper = objectMapper; 20 | } 21 | 22 | @Override 23 | public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { 24 | try { 25 | BufferedReader reader = request.getReader(); 26 | StringBuilder sb = new StringBuilder(); 27 | String line; 28 | while ((line = reader.readLine()) != null) { 29 | sb.append(line); 30 | } 31 | LoginCredentials authRequest = objectMapper.readValue(sb.toString(), LoginCredentials.class); 32 | UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( 33 | authRequest.getUsername(), authRequest.getPassword() 34 | ); 35 | setDetails(request, token); 36 | return this.getAuthenticationManager().authenticate(token); 37 | } catch (IOException e) { 38 | throw new IllegalArgumentException(e.getMessage()); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/config/JwtAuthorizationFilter.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.config; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | import org.springframework.security.authentication.AuthenticationManager; 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import org.springframework.security.core.context.SecurityContextHolder; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; 11 | 12 | import javax.servlet.FilterChain; 13 | import javax.servlet.ServletException; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | 18 | public class JwtAuthorizationFilter extends BasicAuthenticationFilter { 19 | private static final String TOKEN_HEADER = "Authorization"; 20 | private static final String TOKEN_PREFIX = "Bearer "; 21 | private final UserDetailsService userDetailsService; 22 | private final String secret; 23 | 24 | public JwtAuthorizationFilter(AuthenticationManager authenticationManager, 25 | UserDetailsService userDetailsService, 26 | String secret) { 27 | super(authenticationManager); 28 | this.userDetailsService = userDetailsService; 29 | this.secret = secret; 30 | } 31 | @Override 32 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 33 | FilterChain filterChain) throws IOException, ServletException { 34 | UsernamePasswordAuthenticationToken authentication = getAuthentication(request); 35 | if (authentication == null) { 36 | filterChain.doFilter(request, response); 37 | return; 38 | } 39 | SecurityContextHolder.getContext().setAuthentication(authentication); 40 | filterChain.doFilter(request, response); 41 | } 42 | 43 | private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) { 44 | String token = request.getHeader(TOKEN_HEADER); 45 | if (token != null && token.startsWith(TOKEN_PREFIX)) { 46 | String userName = JWT.require(Algorithm.HMAC256(secret)) 47 | .build() 48 | .verify(token.replace(TOKEN_PREFIX, "")) 49 | .getSubject(); 50 | if (userName != null) { 51 | UserDetails userDetails = userDetailsService.loadUserByUsername(userName); 52 | return new UsernamePasswordAuthenticationToken(userDetails.getUsername(), null, userDetails.getAuthorities()); 53 | } 54 | } 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/config/LoginCredentials.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | public class LoginCredentials { 8 | private String username; 9 | private String password; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/config/RestAuthenticationFailureHandler.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.config; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | 12 | @Component 13 | public class RestAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { 14 | @Override 15 | public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, 16 | AuthenticationException exception) throws IOException, ServletException { 17 | super.onAuthenticationFailure(request, response, exception); 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/config/RestAuthenticationSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.config; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; 9 | import org.springframework.stereotype.Component; 10 | 11 | import javax.servlet.ServletException; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.io.IOException; 15 | import java.util.Date; 16 | 17 | @Component 18 | public class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { 19 | 20 | private final long expirationTime; 21 | private final String secret; 22 | 23 | public RestAuthenticationSuccessHandler( 24 | @Value("${jwt.expirationTime}") long expirationTime, 25 | @Value("${jwt.secret}") String secret) { 26 | this.expirationTime = expirationTime; 27 | this.secret = secret; 28 | } 29 | 30 | @Override 31 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, 32 | Authentication authentication) throws IOException, ServletException { 33 | UserDetails principal = (UserDetails) authentication.getPrincipal(); 34 | String token = JWT.create() 35 | .withSubject(principal.getUsername()) 36 | .withExpiresAt(new Date(System.currentTimeMillis() + expirationTime)) 37 | .sign(Algorithm.HMAC256(secret)); 38 | response.addHeader("Authorization", "Bearer " + token); 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 12 | import org.springframework.security.config.http.SessionCreationPolicy; 13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 14 | import org.springframework.security.provisioning.JdbcUserDetailsManager; 15 | import org.springframework.security.provisioning.UserDetailsManager; 16 | import org.springframework.security.web.authentication.HttpStatusEntryPoint; 17 | 18 | import javax.sql.DataSource; 19 | 20 | @Configuration 21 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 22 | 23 | private final DataSource dataSource; 24 | private final ObjectMapper objectMapper; 25 | private final RestAuthenticationSuccessHandler successHandler; 26 | private final RestAuthenticationFailureHandler failureHandler; 27 | private final String secret; 28 | 29 | public SecurityConfig(DataSource dataSource, ObjectMapper objectMapper, RestAuthenticationSuccessHandler successHandler, 30 | RestAuthenticationFailureHandler failureHandler, 31 | @Value("${jwt.secret}") String secret) { 32 | this.dataSource = dataSource; 33 | this.objectMapper = objectMapper; 34 | this.successHandler = successHandler; 35 | this.failureHandler = failureHandler; 36 | this.secret = secret; 37 | } 38 | 39 | @Override 40 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 41 | auth.jdbcAuthentication().dataSource(dataSource); 42 | } 43 | 44 | @Override 45 | protected void configure(HttpSecurity http) throws Exception { 46 | http.csrf().disable(); 47 | http 48 | .authorizeRequests() 49 | .antMatchers("/swagger-ui.html").permitAll() 50 | .antMatchers("/v2/api-docs").permitAll() 51 | .antMatchers("/webjars/**").permitAll() 52 | .antMatchers("/swagger-resources/**").permitAll() 53 | .antMatchers("/h2-console/**").permitAll() 54 | .anyRequest().authenticated() 55 | .and() 56 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 57 | .and() 58 | .addFilter(authenticationFilter()) 59 | .addFilter(new JwtAuthorizationFilter(authenticationManager(), userDetailsManager(), secret)) 60 | .exceptionHandling() 61 | .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) 62 | .and() 63 | .headers().frameOptions().disable(); 64 | } 65 | 66 | public JsonObjectAuthenticationFilter authenticationFilter() throws Exception { 67 | JsonObjectAuthenticationFilter authenticationFilter = new JsonObjectAuthenticationFilter(objectMapper); 68 | authenticationFilter.setAuthenticationSuccessHandler(successHandler); 69 | authenticationFilter.setAuthenticationFailureHandler(failureHandler); 70 | authenticationFilter.setAuthenticationManager(super.authenticationManager()); 71 | return authenticationFilter; 72 | } 73 | 74 | @Bean 75 | public UserDetailsManager userDetailsManager() { 76 | return new JdbcUserDetailsManager(dataSource); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.controller; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RequestBody; 6 | import org.springframework.web.bind.annotation.RestController; 7 | import pl.nullpointerexception.restapi.config.LoginCredentials; 8 | 9 | @RestController 10 | public class LoginController { 11 | 12 | @PostMapping("/login") 13 | public void login(@RequestBody LoginCredentials credentials) { 14 | } 15 | 16 | @GetMapping("/secured") 17 | public String secured() { 18 | return "secured"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/controller/PostController.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.data.domain.Sort; 5 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 6 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 7 | import org.springframework.web.bind.annotation.DeleteMapping; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.PutMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestParam; 14 | import org.springframework.web.bind.annotation.RestController; 15 | import pl.nullpointerexception.restapi.controller.dto.PostDto; 16 | import pl.nullpointerexception.restapi.model.Post; 17 | import pl.nullpointerexception.restapi.service.PostService; 18 | 19 | import java.util.List; 20 | 21 | @RestController 22 | @RequiredArgsConstructor 23 | public class PostController { 24 | 25 | private final PostService postService; 26 | 27 | @GetMapping("/posts") 28 | public List getPosts(@RequestParam(required = false) Integer page, Sort.Direction sort, 29 | @AuthenticationPrincipal UsernamePasswordAuthenticationToken user) { 30 | int pageNumber = page != null && page >= 0 ? page : 0; 31 | Sort.Direction sortDirection = sort != null ? sort : Sort.Direction.ASC; 32 | return PostDtoMapper.mapToPostDtos(postService.getPosts(pageNumber, sortDirection)); 33 | } 34 | 35 | @GetMapping("/posts/comments") 36 | public List getPostsWithComment(@RequestParam(required = false) Integer page, Sort.Direction sort) { 37 | int pageNumber = page != null && page >= 0 ? page : 0; 38 | Sort.Direction sortDirection = sort != null ? sort : Sort.Direction.ASC; 39 | return postService.getPostsWithComments(pageNumber, sortDirection); 40 | } 41 | 42 | @GetMapping("/posts/{id}") 43 | public Post getSinglePost(@PathVariable long id) { 44 | return postService.getSinglePost(id); 45 | } 46 | 47 | @PostMapping("/posts") 48 | public Post addPost(@RequestBody Post post) { 49 | return postService.addPost(post); 50 | } 51 | 52 | @PutMapping("/posts") 53 | public Post editPost(@RequestBody Post post) { 54 | return postService.editPost(post); 55 | } 56 | 57 | @DeleteMapping("/posts/{id}") 58 | public void deletePost(@PathVariable long id) { 59 | postService.deletePost(id); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/controller/PostDtoMapper.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.controller; 2 | 3 | import pl.nullpointerexception.restapi.controller.dto.PostDto; 4 | import pl.nullpointerexception.restapi.model.Post; 5 | 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | public class PostDtoMapper { 10 | 11 | private PostDtoMapper() { 12 | } 13 | 14 | public static List mapToPostDtos(List posts) { 15 | return posts.stream() 16 | .map(post -> mapToPostDto(post)) 17 | .collect(Collectors.toList()); 18 | } 19 | 20 | private static PostDto mapToPostDto(Post post) { 21 | return PostDto.builder() 22 | .id(post.getId()) 23 | .title(post.getTitle()) 24 | .content(post.getContent()) 25 | .created(post.getCreated()) 26 | .build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/controller/dto/PostDto.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.controller.dto; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Getter 9 | @Builder 10 | public class PostDto { 11 | private long id; 12 | private String title; 13 | private String content; 14 | private LocalDateTime created; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/model/Comment.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import java.time.LocalDateTime; 11 | 12 | @Entity 13 | @Getter 14 | @Setter 15 | public class Comment { 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | private long id; 19 | private long postId; 20 | private String content; 21 | private LocalDateTime created; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/model/Post.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import javax.persistence.CascadeType; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.GenerationType; 10 | import javax.persistence.Id; 11 | import javax.persistence.JoinColumn; 12 | import javax.persistence.OneToMany; 13 | import java.time.LocalDateTime; 14 | import java.util.List; 15 | 16 | @Entity 17 | @Getter 18 | @Setter 19 | public class Post { 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.IDENTITY) 22 | private long id; 23 | private String title; 24 | private String content; 25 | private LocalDateTime created; 26 | @OneToMany(cascade = CascadeType.REMOVE) 27 | @JoinColumn(name = "postId", updatable = false, insertable = false) 28 | private List comment; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/repository/CommentRepository.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import pl.nullpointerexception.restapi.model.Comment; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface CommentRepository extends JpaRepository { 11 | List findAllByPostIdIn(List ids); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/repository/PostRepository.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.repository; 2 | 3 | import org.springframework.data.domain.Pageable; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.stereotype.Repository; 7 | import pl.nullpointerexception.restapi.model.Post; 8 | 9 | import java.util.List; 10 | 11 | @Repository 12 | public interface PostRepository extends JpaRepository { 13 | 14 | @Query("Select p From Post p") 15 | List findAllPosts(Pageable page); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/pl/nullpointerexception/restapi/service/PostService.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.cache.annotation.CacheEvict; 5 | import org.springframework.cache.annotation.CachePut; 6 | import org.springframework.cache.annotation.Cacheable; 7 | import org.springframework.data.domain.PageRequest; 8 | import org.springframework.data.domain.Sort; 9 | import org.springframework.stereotype.Service; 10 | import pl.nullpointerexception.restapi.model.Comment; 11 | import pl.nullpointerexception.restapi.model.Post; 12 | import pl.nullpointerexception.restapi.repository.CommentRepository; 13 | import pl.nullpointerexception.restapi.repository.PostRepository; 14 | 15 | import javax.transaction.Transactional; 16 | import java.util.List; 17 | import java.util.stream.Collectors; 18 | 19 | @Service 20 | @RequiredArgsConstructor 21 | public class PostService { 22 | 23 | private static final int PAGE_SIZE = 20; 24 | private final PostRepository postRepository; 25 | private final CommentRepository commentRepository; 26 | 27 | public List getPosts(int page, Sort.Direction sort) { 28 | return postRepository.findAllPosts( 29 | PageRequest.of(page, PAGE_SIZE, 30 | Sort.by(sort, "id") 31 | ) 32 | ); 33 | } 34 | 35 | @Cacheable(cacheNames = "SinglePost", key = "#id") 36 | public Post getSinglePost(long id) { 37 | return postRepository.findById(id) 38 | .orElseThrow(); 39 | } 40 | 41 | @Cacheable(cacheNames = "PostsWithComments") 42 | public List getPostsWithComments(int page, Sort.Direction sort) { 43 | List allPosts = postRepository.findAllPosts(PageRequest.of(page, PAGE_SIZE, 44 | Sort.by(sort, "id") 45 | )); 46 | List ids = allPosts.stream() 47 | .map(Post::getId) 48 | .collect(Collectors.toList()); 49 | List comments = commentRepository.findAllByPostIdIn(ids); 50 | allPosts.forEach(post -> post.setComment(extractComments(comments, post.getId()))); 51 | return allPosts; 52 | } 53 | 54 | private List extractComments(List comments, long id) { 55 | return comments.stream() 56 | .filter(comment -> comment.getPostId() == id) 57 | .collect(Collectors.toList()); 58 | } 59 | 60 | public Post addPost(Post post) { 61 | return postRepository.save(post); 62 | } 63 | 64 | @Transactional 65 | @CachePut(cacheNames = "SinglePost", key = "#result.id") 66 | public Post editPost(Post post) { 67 | Post postEdited = postRepository.findById(post.getId()).orElseThrow(); 68 | postEdited.setTitle(post.getTitle()); 69 | postEdited.setContent(post.getContent()); 70 | return postEdited; 71 | } 72 | 73 | @CacheEvict(cacheNames = "SinglePost") 74 | public void deletePost(long id) { 75 | postRepository.deleteById(id); 76 | } 77 | 78 | 79 | @CacheEvict(cacheNames = "PostsWithComments") 80 | public void clearPostsWithComments() { 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.jpa.hibernate.ddl-auto=none 2 | spring.jpa.show-sql=true 3 | 4 | 5 | jwt.expirationTime=3600000 6 | jwt.secret=asdsalqrk1232115dfas@!#!@sds 7 | 8 | spring.cache.jcache.config = classpath:ehcache.xml 9 | 10 | spring.liquibase.change-log=classpath:liquibase-changeLog.xml 11 | 12 | spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/youtube?useUnicode=true&serverTimezone=UTC 13 | spring.datasource.username=youtube 14 | spring.datasource.password=youtube -------------------------------------------------------------------------------- /src/main/resources/database/2020-09-29/01-create-post.sql: -------------------------------------------------------------------------------- 1 | --liquibase formatted sql 2 | --changeset mdabrowski:1 3 | CREATE TABLE POST ( 4 | id BIGINT AUTO_INCREMENT PRIMARY KEY, 5 | title VARCHAR(400) NOT NULL, 6 | content VARCHAR(2000) NULL, 7 | created timestamp 8 | ); 9 | -------------------------------------------------------------------------------- /src/main/resources/database/2020-09-29/02-create-comment.sql: -------------------------------------------------------------------------------- 1 | --liquibase formatted sql 2 | --changeset mdabrowski:2 3 | CREATE TABLE COMMENT ( 4 | id BIGINT AUTO_INCREMENT PRIMARY KEY, 5 | post_id BIGINT NOT NULL, 6 | content VARCHAR(2000) NULL, 7 | created timestamp 8 | ); 9 | 10 | ALTER TABLE COMMENT 11 | ADD CONSTRAINT comment_post_id 12 | FOREIGN KEY (post_id) REFERENCES post(id) 13 | -------------------------------------------------------------------------------- /src/main/resources/database/2020-09-29/03-post-comment-data.sql: -------------------------------------------------------------------------------- 1 | --liquibase formatted sql 2 | --changeset mdabrowski:3 3 | insert into post(id, title, content, created) values (1, 'Test post 1', 'Content 1', '2020-01-23T13:14:05.562673800'); 4 | insert into post(id, title, content, created) values (2, 'Test post 2', 'Content 2', '2020-01-24T13:14:05.563649'); 5 | insert into post(id, title, content, created) values (3, 'Test post 3', 'Content 3', '2020-01-25T13:14:05.564625600'); 6 | insert into post(id, title, content, created) values (4, 'Test post 4', 'Content 4', '2020-01-26T13:14:05.565599900'); 7 | insert into post(id, title, content, created) values (5, 'Test post 5', 'Content 5', '2020-01-27T13:14:05.566579200'); 8 | insert into post(id, title, content, created) values (6, 'Test post 6', 'Content 6', '2020-01-28T13:14:05.567204200'); 9 | insert into post(id, title, content, created) values (7, 'Test post 7', 'Content 7', '2020-01-29T13:14:05.567551200'); 10 | insert into post(id, title, content, created) values (8, 'Test post 8', 'Content 8', '2020-01-30T13:14:05.568535200'); 11 | insert into post(id, title, content, created) values (9, 'Test post 9', 'Content 9', '2020-01-31T13:14:05.568535200'); 12 | insert into post(id, title, content, created) values (10, 'Test post 10', 'Content 10', '2020-02-01T13:14:05.569504'); 13 | insert into post(id, title, content, created) values (11, 'Test post 11', 'Content 11', '2020-02-02T13:14:05.570479800'); 14 | insert into post(id, title, content, created) values (12, 'Test post 12', 'Content 12', '2020-02-03T13:14:05.570479800'); 15 | insert into post(id, title, content, created) values (13, 'Test post 13', 'Content 13', '2020-02-04T13:14:05.571456800'); 16 | insert into post(id, title, content, created) values (14, 'Test post 14', 'Content 14', '2020-02-05T13:14:05.571456800'); 17 | insert into post(id, title, content, created) values (15, 'Test post 15', 'Content 15', '2020-02-06T13:14:05.572513700'); 18 | insert into post(id, title, content, created) values (16, 'Test post 16', 'Content 16', '2020-02-07T13:14:05.572513700'); 19 | insert into post(id, title, content, created) values (17, 'Test post 17', 'Content 17', '2020-02-08T13:14:05.573477700'); 20 | insert into post(id, title, content, created) values (18, 'Test post 18', 'Content 18', '2020-02-09T13:14:05.574458300'); 21 | insert into post(id, title, content, created) values (19, 'Test post 19', 'Content 19', '2020-02-10T13:14:05.576408600'); 22 | insert into post(id, title, content, created) values (20, 'Test post 20', 'Content 20', '2020-02-11T13:14:05.579334800'); 23 | insert into post(id, title, content, created) values (21, 'Test post 21', 'Content 21', '2020-02-12T13:14:05.580309500'); 24 | insert into post(id, title, content, created) values (22, 'Test post 22', 'Content 22', '2020-02-13T13:14:05.581288600'); 25 | insert into post(id, title, content, created) values (23, 'Test post 23', 'Content 23', '2020-02-14T13:14:05.582262500'); 26 | insert into post(id, title, content, created) values (24, 'Test post 24', 'Content 24', '2020-02-15T13:14:05.583237300'); 27 | insert into post(id, title, content, created) values (25, 'Test post 25', 'Content 25', '2020-02-16T13:14:05.584214700'); 28 | insert into post(id, title, content, created) values (26, 'Test post 26', 'Content 26', '2020-02-17T13:14:05.585192800'); 29 | insert into post(id, title, content, created) values (27, 'Test post 27', 'Content 27', '2020-02-18T13:14:05.586169100'); 30 | insert into post(id, title, content, created) values (28, 'Test post 28', 'Content 28', '2020-02-19T13:14:05.587142800'); 31 | insert into post(id, title, content, created) values (29, 'Test post 29', 'Content 29', '2020-02-20T13:14:05.588118400'); 32 | insert into post(id, title, content, created) values (30, 'Test post 30', 'Content 30', '2020-02-21T13:14:05.589096600'); 33 | insert into post(id, title, content, created) values (31, 'Test post 31', 'Content 31', '2020-02-22T13:14:05.590089600'); 34 | insert into post(id, title, content, created) values (32, 'Test post 32', 'Content 32', '2020-02-23T13:14:05.590089600'); 35 | insert into post(id, title, content, created) values (33, 'Test post 33', 'Content 33', '2020-02-24T13:14:05.591045100'); 36 | insert into post(id, title, content, created) values (34, 'Test post 34', 'Content 34', '2020-02-25T13:14:05.592023200'); 37 | insert into post(id, title, content, created) values (35, 'Test post 35', 'Content 35', '2020-02-26T13:14:05.592999'); 38 | insert into post(id, title, content, created) values (36, 'Test post 36', 'Content 36', '2020-02-27T13:14:05.592999'); 39 | insert into post(id, title, content, created) values (37, 'Test post 37', 'Content 37', '2020-02-28T13:14:05.593974100'); 40 | insert into post(id, title, content, created) values (38, 'Test post 38', 'Content 38', '2020-02-29T13:14:05.595588600'); 41 | insert into post(id, title, content, created) values (39, 'Test post 39', 'Content 39', '2020-03-01T13:14:05.595926400'); 42 | insert into post(id, title, content, created) values (40, 'Test post 40', 'Content 40', '2020-03-02T13:14:05.596905900'); 43 | insert into post(id, title, content, created) values (41, 'Test post 41', 'Content 41', '2020-03-03T13:14:05.597878300'); 44 | insert into post(id, title, content, created) values (42, 'Test post 42', 'Content 42', '2020-03-04T13:14:05.598854600'); 45 | insert into post(id, title, content, created) values (43, 'Test post 43', 'Content 43', '2020-03-05T13:14:05.598854600'); 46 | insert into post(id, title, content, created) values (44, 'Test post 44', 'Content 44', '2020-03-06T13:14:05.599830'); 47 | insert into post(id, title, content, created) values (45, 'Test post 45', 'Content 45', '2020-03-07T13:14:05.599830'); 48 | insert into post(id, title, content, created) values (46, 'Test post 46', 'Content 46', '2020-03-08T13:14:05.600810100'); 49 | insert into post(id, title, content, created) values (47, 'Test post 47', 'Content 47', '2020-03-09T13:14:05.600810100'); 50 | insert into post(id, title, content, created) values (48, 'Test post 48', 'Content 48', '2020-03-10T13:14:05.601783200'); 51 | insert into post(id, title, content, created) values (49, 'Test post 49', 'Content 49', '2020-03-11T13:14:05.601783200'); 52 | insert into post(id, title, content, created) values (50, 'Test post 50', 'Content 50', '2020-03-12T13:14:05.602757600'); 53 | insert into post(id, title, content, created) values (51, 'Test post 51', 'Content 51', '2020-03-13T13:14:05.602757600'); 54 | insert into post(id, title, content, created) values (52, 'Test post 52', 'Content 52', '2020-03-14T13:14:05.602757600'); 55 | insert into post(id, title, content, created) values (53, 'Test post 53', 'Content 53', '2020-03-15T13:14:05.603738900'); 56 | insert into post(id, title, content, created) values (54, 'Test post 54', 'Content 54', '2020-03-16T13:14:05.603738900'); 57 | insert into post(id, title, content, created) values (55, 'Test post 55', 'Content 55', '2020-03-17T13:14:05.604709200'); 58 | insert into post(id, title, content, created) values (56, 'Test post 56', 'Content 56', '2020-03-18T13:14:05.604709200'); 59 | insert into post(id, title, content, created) values (57, 'Test post 57', 'Content 57', '2020-03-19T13:14:05.604709200'); 60 | insert into post(id, title, content, created) values (58, 'Test post 58', 'Content 58', '2020-03-20T13:14:05.605685200'); 61 | insert into post(id, title, content, created) values (59, 'Test post 59', 'Content 59', '2020-03-21T13:14:05.605685200'); 62 | insert into post(id, title, content, created) values (60, 'Test post 60', 'Content 60', '2020-03-22T13:14:05.606661100'); 63 | insert into post(id, title, content, created) values (61, 'Test post 61', 'Content 61', '2020-03-23T13:14:05.607645500'); 64 | insert into post(id, title, content, created) values (62, 'Test post 62', 'Content 62', '2020-03-24T13:14:05.609597600'); 65 | insert into post(id, title, content, created) values (63, 'Test post 63', 'Content 63', '2020-03-25T13:14:05.610569600'); 66 | insert into post(id, title, content, created) values (64, 'Test post 64', 'Content 64', '2020-03-26T13:14:05.610569600'); 67 | insert into post(id, title, content, created) values (65, 'Test post 65', 'Content 65', '2020-03-27T13:14:05.611543500'); 68 | insert into post(id, title, content, created) values (66, 'Test post 66', 'Content 66', '2020-03-28T13:14:05.612522700'); 69 | insert into post(id, title, content, created) values (67, 'Test post 67', 'Content 67', '2020-03-29T13:14:05.612522700'); 70 | insert into post(id, title, content, created) values (68, 'Test post 68', 'Content 68', '2020-03-30T13:14:05.613494'); 71 | insert into post(id, title, content, created) values (69, 'Test post 69', 'Content 69', '2020-03-31T13:14:05.614470100'); 72 | insert into post(id, title, content, created) values (70, 'Test post 70', 'Content 70', '2020-04-01T13:14:05.614470100'); 73 | insert into post(id, title, content, created) values (71, 'Test post 71', 'Content 71', '2020-04-02T13:14:05.615448200'); 74 | insert into post(id, title, content, created) values (72, 'Test post 72', 'Content 72', '2020-04-03T13:14:05.616423900'); 75 | insert into post(id, title, content, created) values (73, 'Test post 73', 'Content 73', '2020-04-04T13:14:05.618379200'); 76 | insert into post(id, title, content, created) values (74, 'Test post 74', 'Content 74', '2020-04-05T13:14:05.619353500'); 77 | insert into post(id, title, content, created) values (75, 'Test post 75', 'Content 75', '2020-04-06T13:14:05.620328400'); 78 | insert into post(id, title, content, created) values (76, 'Test post 76', 'Content 76', '2020-04-07T13:14:05.621301100'); 79 | insert into post(id, title, content, created) values (77, 'Test post 77', 'Content 77', '2020-04-08T13:14:05.621301100'); 80 | insert into post(id, title, content, created) values (78, 'Test post 78', 'Content 78', '2020-04-09T13:14:05.622277500'); 81 | insert into post(id, title, content, created) values (79, 'Test post 79', 'Content 79', '2020-04-10T13:14:05.623254900'); 82 | insert into post(id, title, content, created) values (80, 'Test post 80', 'Content 80', '2020-04-11T13:14:05.624229300'); 83 | insert into post(id, title, content, created) values (81, 'Test post 81', 'Content 81', '2020-04-12T13:14:05.624229300'); 84 | insert into post(id, title, content, created) values (82, 'Test post 82', 'Content 82', '2020-04-13T13:14:05.625206800'); 85 | insert into post(id, title, content, created) values (83, 'Test post 83', 'Content 83', '2020-04-14T13:14:05.626183800'); 86 | insert into post(id, title, content, created) values (84, 'Test post 84', 'Content 84', '2020-04-15T13:14:05.627159'); 87 | insert into post(id, title, content, created) values (85, 'Test post 85', 'Content 85', '2020-04-16T13:14:05.627159'); 88 | insert into post(id, title, content, created) values (86, 'Test post 86', 'Content 86', '2020-04-17T13:14:05.628135100'); 89 | insert into post(id, title, content, created) values (87, 'Test post 87', 'Content 87', '2020-04-18T13:14:05.629110500'); 90 | insert into post(id, title, content, created) values (88, 'Test post 88', 'Content 88', '2020-04-19T13:14:05.630087200'); 91 | insert into post(id, title, content, created) values (89, 'Test post 89', 'Content 89', '2020-04-20T13:14:05.630087200'); 92 | insert into post(id, title, content, created) values (90, 'Test post 90', 'Content 90', '2020-04-21T13:14:05.631063100'); 93 | insert into post(id, title, content, created) values (91, 'Test post 91', 'Content 91', '2020-04-22T13:14:05.632038700'); 94 | insert into post(id, title, content, created) values (92, 'Test post 92', 'Content 92', '2020-04-23T13:14:05.632038700'); 95 | insert into post(id, title, content, created) values (93, 'Test post 93', 'Content 93', '2020-04-24T13:14:05.633013700'); 96 | insert into post(id, title, content, created) values (94, 'Test post 94', 'Content 94', '2020-04-25T13:14:05.633013700'); 97 | insert into post(id, title, content, created) values (95, 'Test post 95', 'Content 95', '2020-04-26T13:14:05.633989900'); 98 | insert into post(id, title, content, created) values (96, 'Test post 96', 'Content 96', '2020-04-27T13:14:05.634964500'); 99 | insert into post(id, title, content, created) values (97, 'Test post 97', 'Content 97', '2020-04-28T13:14:05.635599900'); 100 | insert into post(id, title, content, created) values (98, 'Test post 98', 'Content 98', '2020-04-29T13:14:05.635940700'); 101 | insert into post(id, title, content, created) values (99, 'Test post 99', 'Content 99', '2020-04-30T13:14:05.635940700'); 102 | insert into post(id, title, content, created) values (100, 'Test post 100', 'Content 100', '2020-05-01T13:14:05.636916800'); 103 | insert into comment(id, post_id, content, created) values (1, 1, 'Comment 1', '2020-01-23T13:14:05.637893700'); 104 | insert into comment(id, post_id, content, created) values (2, 1, 'Comment 2', '2020-01-24T13:14:05.637893700'); 105 | insert into comment(id, post_id, content, created) values (3, 1, 'Comment 3', '2020-01-25T13:14:05.638869'); 106 | insert into comment(id, post_id, content, created) values (4, 1, 'Comment 4', '2020-01-26T13:14:05.639844900'); 107 | insert into comment(id, post_id, content, created) values (5, 1, 'Comment 5', '2020-01-27T13:14:05.639844900'); 108 | insert into comment(id, post_id, content, created) values (6, 1, 'Comment 6', '2020-01-28T13:14:05.641799900'); 109 | insert into comment(id, post_id, content, created) values (7, 1, 'Comment 7', '2020-01-29T13:14:05.642779200'); 110 | insert into comment(id, post_id, content, created) values (8, 1, 'Comment 8', '2020-01-30T13:14:05.644727500'); 111 | insert into comment(id, post_id, content, created) values (9, 1, 'Comment 9', '2020-01-31T13:14:05.645709900'); 112 | insert into comment(id, post_id, content, created) values (10, 2, 'Comment 10', '2020-02-01T13:14:05.646680100'); 113 | insert into comment(id, post_id, content, created) values (11, 2, 'Comment 11', '2020-02-02T13:14:05.649067'); 114 | insert into comment(id, post_id, content, created) values (12, 2, 'Comment 12', '2020-02-03T13:14:05.650583100'); 115 | insert into comment(id, post_id, content, created) values (13, 2, 'Comment 13', '2020-02-04T13:14:05.651559900'); 116 | insert into comment(id, post_id, content, created) values (14, 2, 'Comment 14', '2020-02-05T13:14:05.652535600'); 117 | insert into comment(id, post_id, content, created) values (15, 2, 'Comment 15', '2020-02-06T13:14:05.654489'); 118 | insert into comment(id, post_id, content, created) values (16, 2, 'Comment 16', '2020-02-07T13:14:05.655024500'); 119 | insert into comment(id, post_id, content, created) values (17, 2, 'Comment 17', '2020-02-08T13:14:05.655987600'); 120 | insert into comment(id, post_id, content, created) values (18, 2, 'Comment 18', '2020-02-09T13:14:05.656961900'); 121 | insert into comment(id, post_id, content, created) values (19, 2, 'Comment 19', '2020-02-10T13:14:05.657940600'); 122 | insert into comment(id, post_id, content, created) values (20, 3, 'Comment 20', '2020-02-11T13:14:05.658916'); 123 | insert into comment(id, post_id, content, created) values (21, 3, 'Comment 21', '2020-02-12T13:14:05.658916'); 124 | insert into comment(id, post_id, content, created) values (22, 3, 'Comment 22', '2020-02-13T13:14:05.659891100'); 125 | insert into comment(id, post_id, content, created) values (23, 3, 'Comment 23', '2020-02-14T13:14:05.660868'); 126 | insert into comment(id, post_id, content, created) values (24, 3, 'Comment 24', '2020-02-15T13:14:05.661846700'); 127 | insert into comment(id, post_id, content, created) values (25, 3, 'Comment 25', '2020-02-16T13:14:05.661846700'); 128 | insert into comment(id, post_id, content, created) values (26, 3, 'Comment 26', '2020-02-17T13:14:05.662820300'); 129 | insert into comment(id, post_id, content, created) values (27, 3, 'Comment 27', '2020-02-18T13:14:05.663795900'); 130 | insert into comment(id, post_id, content, created) values (28, 3, 'Comment 28', '2020-02-19T13:14:05.664772'); 131 | insert into comment(id, post_id, content, created) values (29, 3, 'Comment 29', '2020-02-20T13:14:05.664772'); 132 | insert into comment(id, post_id, content, created) values (30, 4, 'Comment 30', '2020-02-21T13:14:05.665746700'); 133 | insert into comment(id, post_id, content, created) values (31, 4, 'Comment 31', '2020-02-22T13:14:05.666723300'); 134 | insert into comment(id, post_id, content, created) values (32, 4, 'Comment 32', '2020-02-23T13:14:05.666723300'); 135 | insert into comment(id, post_id, content, created) values (33, 4, 'Comment 33', '2020-02-24T13:14:05.667699600'); 136 | insert into comment(id, post_id, content, created) values (34, 4, 'Comment 34', '2020-02-25T13:14:05.668674100'); 137 | insert into comment(id, post_id, content, created) values (35, 4, 'Comment 35', '2020-02-26T13:14:05.668674100'); 138 | insert into comment(id, post_id, content, created) values (36, 4, 'Comment 36', '2020-02-27T13:14:05.669652100'); 139 | insert into comment(id, post_id, content, created) values (37, 4, 'Comment 37', '2020-02-28T13:14:05.670625900'); 140 | insert into comment(id, post_id, content, created) values (38, 4, 'Comment 38', '2020-02-29T13:14:05.670625900'); 141 | insert into comment(id, post_id, content, created) values (39, 4, 'Comment 39', '2020-03-01T13:14:05.671602300'); 142 | insert into comment(id, post_id, content, created) values (40, 5, 'Comment 40', '2020-03-02T13:14:05.672578500'); 143 | insert into comment(id, post_id, content, created) values (41, 5, 'Comment 41', '2020-03-03T13:14:05.677459700'); 144 | insert into comment(id, post_id, content, created) values (42, 5, 'Comment 42', '2020-03-04T13:14:05.679414400'); 145 | insert into comment(id, post_id, content, created) values (43, 5, 'Comment 43', '2020-03-05T13:14:05.680386800'); 146 | insert into comment(id, post_id, content, created) values (44, 5, 'Comment 44', '2020-03-06T13:14:05.681364400'); 147 | insert into comment(id, post_id, content, created) values (45, 5, 'Comment 45', '2020-03-07T13:14:05.682341800'); 148 | insert into comment(id, post_id, content, created) values (46, 5, 'Comment 46', '2020-03-08T13:14:05.684291300'); 149 | insert into comment(id, post_id, content, created) values (47, 5, 'Comment 47', '2020-03-09T13:14:05.684291300'); 150 | insert into comment(id, post_id, content, created) values (48, 5, 'Comment 48', '2020-03-10T13:14:05.685268900'); 151 | insert into comment(id, post_id, content, created) values (49, 5, 'Comment 49', '2020-03-11T13:14:05.687219500'); 152 | insert into comment(id, post_id, content, created) values (50, 6, 'Comment 50', '2020-03-12T13:14:05.687219500'); 153 | insert into comment(id, post_id, content, created) values (51, 6, 'Comment 51', '2020-03-13T13:14:05.688197600'); 154 | insert into comment(id, post_id, content, created) values (52, 6, 'Comment 52', '2020-03-14T13:14:05.689169600'); 155 | insert into comment(id, post_id, content, created) values (53, 6, 'Comment 53', '2020-03-15T13:14:05.690146800'); 156 | insert into comment(id, post_id, content, created) values (54, 6, 'Comment 54', '2020-03-16T13:14:05.691122800'); 157 | insert into comment(id, post_id, content, created) values (55, 6, 'Comment 55', '2020-03-17T13:14:05.692100100'); 158 | insert into comment(id, post_id, content, created) values (56, 6, 'Comment 56', '2020-03-18T13:14:05.692100100'); 159 | insert into comment(id, post_id, content, created) values (57, 6, 'Comment 57', '2020-03-19T13:14:05.694049600'); 160 | insert into comment(id, post_id, content, created) values (58, 6, 'Comment 58', '2020-03-20T13:14:05.695028500'); 161 | insert into comment(id, post_id, content, created) values (59, 6, 'Comment 59', '2020-03-21T13:14:05.696003'); 162 | insert into comment(id, post_id, content, created) values (60, 7, 'Comment 60', '2020-03-22T13:14:05.696979700'); 163 | insert into comment(id, post_id, content, created) values (61, 7, 'Comment 61', '2020-03-23T13:14:05.697956400'); 164 | insert into comment(id, post_id, content, created) values (62, 7, 'Comment 62', '2020-03-24T13:14:05.698930100'); 165 | insert into comment(id, post_id, content, created) values (63, 7, 'Comment 63', '2020-03-25T13:14:05.699906600'); 166 | insert into comment(id, post_id, content, created) values (64, 7, 'Comment 64', '2020-03-26T13:14:05.699906600'); 167 | insert into comment(id, post_id, content, created) values (65, 7, 'Comment 65', '2020-03-27T13:14:05.700884400'); 168 | insert into comment(id, post_id, content, created) values (66, 7, 'Comment 66', '2020-03-28T13:14:05.700884400'); 169 | insert into comment(id, post_id, content, created) values (67, 7, 'Comment 67', '2020-03-29T13:14:05.701857900'); 170 | insert into comment(id, post_id, content, created) values (68, 7, 'Comment 68', '2020-03-30T13:14:05.702833900'); 171 | insert into comment(id, post_id, content, created) values (69, 7, 'Comment 69', '2020-03-31T13:14:05.702833900'); 172 | insert into comment(id, post_id, content, created) values (70, 8, 'Comment 70', '2020-04-01T13:14:05.703810300'); 173 | insert into comment(id, post_id, content, created) values (71, 8, 'Comment 71', '2020-04-02T13:14:05.703810300'); 174 | insert into comment(id, post_id, content, created) values (72, 8, 'Comment 72', '2020-04-03T13:14:05.704785800'); 175 | insert into comment(id, post_id, content, created) values (73, 8, 'Comment 73', '2020-04-04T13:14:05.704785800'); 176 | insert into comment(id, post_id, content, created) values (74, 8, 'Comment 74', '2020-04-05T13:14:05.705761500'); 177 | insert into comment(id, post_id, content, created) values (75, 8, 'Comment 75', '2020-04-06T13:14:05.705761500'); 178 | insert into comment(id, post_id, content, created) values (76, 8, 'Comment 76', '2020-04-07T13:14:05.706738800'); 179 | insert into comment(id, post_id, content, created) values (77, 8, 'Comment 77', '2020-04-08T13:14:05.708702200'); 180 | insert into comment(id, post_id, content, created) values (78, 8, 'Comment 78', '2020-04-09T13:14:05.710643600'); 181 | insert into comment(id, post_id, content, created) values (79, 8, 'Comment 79', '2020-04-10T13:14:05.711619100'); 182 | insert into comment(id, post_id, content, created) values (80, 9, 'Comment 80', '2020-04-11T13:14:05.713570400'); 183 | insert into comment(id, post_id, content, created) values (81, 9, 'Comment 81', '2020-04-12T13:14:05.715525'); 184 | insert into comment(id, post_id, content, created) values (82, 9, 'Comment 82', '2020-04-13T13:14:05.716500200'); 185 | insert into comment(id, post_id, content, created) values (83, 9, 'Comment 83', '2020-04-14T13:14:05.717476'); 186 | insert into comment(id, post_id, content, created) values (84, 9, 'Comment 84', '2020-04-15T13:14:05.718452300'); 187 | insert into comment(id, post_id, content, created) values (85, 9, 'Comment 85', '2020-04-16T13:14:05.719508900'); 188 | insert into comment(id, post_id, content, created) values (86, 9, 'Comment 86', '2020-04-17T13:14:05.720464200'); 189 | insert into comment(id, post_id, content, created) values (87, 9, 'Comment 87', '2020-04-18T13:14:05.722411500'); 190 | insert into comment(id, post_id, content, created) values (88, 9, 'Comment 88', '2020-04-19T13:14:05.723907200'); 191 | insert into comment(id, post_id, content, created) values (89, 9, 'Comment 89', '2020-04-20T13:14:05.724885500'); 192 | insert into comment(id, post_id, content, created) values (90, 10, 'Comment 90', '2020-04-21T13:14:05.725425800'); 193 | insert into comment(id, post_id, content, created) values (91, 10, 'Comment 91', '2020-04-22T13:14:05.726387300'); 194 | insert into comment(id, post_id, content, created) values (92, 10, 'Comment 92', '2020-04-23T13:14:05.727364900'); 195 | insert into comment(id, post_id, content, created) values (93, 10, 'Comment 93', '2020-04-24T13:14:05.727364900'); 196 | insert into comment(id, post_id, content, created) values (94, 10, 'Comment 94', '2020-04-25T13:14:05.728342700'); 197 | insert into comment(id, post_id, content, created) values (95, 10, 'Comment 95', '2020-04-26T13:14:05.729317600'); 198 | insert into comment(id, post_id, content, created) values (96, 10, 'Comment 96', '2020-04-27T13:14:05.729317600'); 199 | insert into comment(id, post_id, content, created) values (97, 10, 'Comment 97', '2020-04-28T13:14:05.730292900'); 200 | insert into comment(id, post_id, content, created) values (98, 10, 'Comment 98', '2020-04-29T13:14:05.731267700'); 201 | insert into comment(id, post_id, content, created) values (99, 10, 'Comment 99', '2020-04-30T13:14:05.732244800'); 202 | insert into comment(id, post_id, content, created) values (100, 11, 'Comment 100', '2020-05-01T13:14:05.732244800'); 203 | -------------------------------------------------------------------------------- /src/main/resources/database/2020-10-13/01-create-user-authorities.sql: -------------------------------------------------------------------------------- 1 | --liquibase formatted sql 2 | --changeset mdabrowski:4 3 | create table users( 4 | id BIGINT AUTO_INCREMENT PRIMARY KEY, 5 | username varchar ( 50 ) not null UNIQUE, 6 | password varchar ( 100 ) not null, 7 | enabled boolean not null 8 | ); 9 | --changeset mdabrowski:5 10 | create table authorities ( 11 | username varchar ( 50 ) not null, 12 | authority varchar ( 50 ) not null, 13 | constraint fk_authorities_users foreign key (username) references 14 | users(username) , 15 | UNIQUE KEY username_authority (username, authority) 16 | ); 17 | --changeset mdabrowski:6 18 | insert into users (id, username, password, enabled) 19 | values (1, 'test', '{bcrypt}$2a$10$upzXFsFUOClFRR69OMKF8eajGMRs0vhcSHqvNDKy9yfW45w7o9z6O', true); 20 | insert into authorities (username, authority) values ('test','USER'); -------------------------------------------------------------------------------- /src/main/resources/ehcache.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 1 10 | 11 | 1000 12 | 13 | 14 | 15 | 16 | 17 | 4 18 | 19 | 500 20 | 21 | -------------------------------------------------------------------------------- /src/main/resources/liquibase-changeLog.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/test/java/pl/nullpointerexception/restapi/controller/LoginControllerTest.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.controller; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.web.servlet.MockMvc; 8 | import org.springframework.test.web.servlet.MvcResult; 9 | 10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 11 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 12 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 13 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 14 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 15 | 16 | @SpringBootTest 17 | @AutoConfigureMockMvc 18 | class LoginControllerTest { 19 | 20 | @Autowired 21 | private MockMvc mockMvc; 22 | 23 | @Test 24 | void shouldLoginAndGetContent() throws Exception { 25 | MvcResult login = mockMvc.perform(post("/login") 26 | .content("{\"username\": \"test\", \"password\": \"test\"}") 27 | ) 28 | .andDo(print()) 29 | .andExpect(status().is(200)) 30 | .andReturn(); 31 | String token = login.getResponse().getHeader("Authorization"); 32 | 33 | mockMvc.perform(get("/secured") 34 | .header("Authorization", token) 35 | ) 36 | .andDo(print()) 37 | .andExpect(status().is(200)) 38 | .andExpect(content().string("secured")); 39 | 40 | mockMvc.perform(get("/secured")) 41 | .andDo(print()) 42 | .andExpect(status().is(401)); 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /src/test/java/pl/nullpointerexception/restapi/controller/PostControllerTest.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.controller; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.security.test.context.support.WithMockUser; 9 | import org.springframework.test.web.servlet.MockMvc; 10 | import org.springframework.test.web.servlet.MvcResult; 11 | import pl.nullpointerexception.restapi.model.Post; 12 | import pl.nullpointerexception.restapi.repository.PostRepository; 13 | 14 | import javax.transaction.Transactional; 15 | import java.time.LocalDateTime; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 19 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 21 | 22 | @SpringBootTest 23 | @AutoConfigureMockMvc 24 | @WithMockUser 25 | class PostControllerTest { 26 | 27 | @Autowired 28 | private MockMvc mockMvc; 29 | @Autowired 30 | private ObjectMapper objectMapper; 31 | @Autowired 32 | private PostRepository postRepository; 33 | 34 | 35 | @Test 36 | @Transactional 37 | void shouldGetSinglePost() throws Exception { 38 | // given 39 | Post newPost = new Post(); 40 | newPost.setTitle("Test"); 41 | newPost.setContent("Test content"); 42 | newPost.setCreated(LocalDateTime.now()); 43 | postRepository.save(newPost); 44 | // when 45 | MvcResult mvcResult = mockMvc.perform(get("/posts/" + newPost.getId())) 46 | .andDo(print()) 47 | .andExpect(status().is(200)) 48 | .andReturn(); 49 | // then 50 | Post post = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), Post.class); 51 | assertThat(post).isNotNull(); 52 | assertThat(post.getId()).isEqualTo(newPost.getId()); 53 | assertThat(post.getTitle()).isEqualTo("Test"); 54 | assertThat(post.getContent()).isEqualTo("Test content"); 55 | 56 | } 57 | } -------------------------------------------------------------------------------- /src/test/java/pl/nullpointerexception/restapi/service/PostServiceTest.java: -------------------------------------------------------------------------------- 1 | package pl.nullpointerexception.restapi.service; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.security.test.context.support.WithMockUser; 7 | import pl.nullpointerexception.restapi.model.Post; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @SpringBootTest 12 | @WithMockUser 13 | class PostServiceTest { 14 | 15 | @Autowired 16 | private PostService postService; 17 | 18 | @Test 19 | void shouldGetSinglePost() { 20 | // given 21 | // when 22 | Post singlePost = postService.getSinglePost(1L); 23 | // then 24 | assertThat(singlePost).isNotNull(); 25 | assertThat(singlePost.getId()).isEqualTo(1L); 26 | } 27 | } -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.jpa.hibernate.ddl-auto=none 2 | spring.jpa.show-sql=true 3 | 4 | 5 | jwt.expirationTime=3600000 6 | jwt.secret=asdsalqrk1232115dfas@!#!@sds 7 | 8 | spring.cache.jcache.config = classpath:ehcache.xml 9 | 10 | spring.liquibase.change-log=classpath:liquibase-changeLog.xml 11 | 12 | spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/youtubetest?useUnicode=true&serverTimezone=UTC 13 | spring.datasource.username=youtubetest 14 | spring.datasource.password=youtubetest --------------------------------------------------------------------------------