├── .gitignore ├── 00-00-Final.md ├── 00-02-update-2023-02.md ├── 00-03-update-2023-02-course-changes.md ├── 00-AngularHardcoded.zip ├── 00-spring-boot-3-updates.md ├── 01-AngularHttpWithoutAuthentication.zip ├── 01-SpringBootWithoutAuthentication.zip ├── 02-AngularHttpBasicAuthentication.zip ├── 02-SpringBootBasicAuthentication.zip ├── 03-AngularHttpJwtAuthentication.zip ├── 03-SpringBootWithJwtAuthentication.zip ├── 71-spring-security ├── .DS_Store ├── .gitignore ├── HELP.md ├── Step06.md ├── Step06.zip ├── Step07.md ├── Step07.zip ├── Step08.md ├── Step08.zip ├── Step10.md ├── Step10.zip ├── Step12.md ├── Step12.zip ├── Step13.md ├── Step13.zip ├── Step15.md ├── Step15.zip ├── Step18.md ├── Step18.zip ├── Step19.md ├── Step19.zip ├── Step20.md ├── Step20.zip ├── Step22.md ├── Step22.zip ├── build.gradle ├── gradle │ ├── .DS_Store │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src │ ├── .DS_Store │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── in28minutes │ │ │ │ └── learnspringsecurity │ │ │ │ ├── LearnSpringSecurityApplication.java │ │ │ │ ├── basic │ │ │ │ └── BasicAuthSecurityConfiguration.java │ │ │ │ ├── jwt │ │ │ │ ├── JwtAuthenticationResource.java │ │ │ │ └── JwtSecurityConfiguration.java │ │ │ │ └── resources │ │ │ │ ├── HelloWorldResource.java │ │ │ │ ├── SpringSecurityPlayResource.java │ │ │ │ └── TodoResource.java │ │ └── resources │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── in28minutes │ │ └── learnspringsecurity │ │ └── LearnSpringSecurityApplicationTests.java └── step-by-step-changes │ ├── .DS_Store │ └── step-by-step-changes.md ├── 72-oauth ├── .DS_Store ├── .gitignore ├── build.gradle ├── final.md ├── final.zip ├── gradle │ ├── .DS_Store │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── .DS_Store │ ├── main │ ├── .DS_Store │ ├── java │ │ └── com │ │ │ └── in28minutes │ │ │ └── learnoauth │ │ │ ├── HelloWorldResource.java │ │ │ ├── LearnOauthApplication.java │ │ │ └── OauthSecurityConfiguration.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── in28minutes │ └── learnoauth │ └── LearnOauthApplicationTests.java ├── 99-reuse ├── .DS_Store ├── 01-spring-security.md └── restful-web-services-v2.zip ├── Backup-01-Generated-From-Angular-Cli.md ├── Backup-01-Generated-From-Angular-Cli.zip ├── Backup-02-After-Creating-Welcome-Component.md ├── Backup-02-After-Creating-Welcome-Component.zip ├── Backup-03-Basic-Hardcoded-Validation-Introduced-ngIf.md ├── Backup-03-Basic-Hardcoded-Validation-Introduced-ngIf.zip ├── Backup-04-Basic-Todo-List-Component.md ├── Backup-04-Basic-Todo-List-Component.zip ├── Backup-05-FIRST-BREAK-AND-REVIEW.md ├── Backup-05-FIRST-BREAK-AND-REVIEW.zip ├── Backup-06-FORMATTING-COMPLETE.md ├── Backup-06-FORMATTING-COMPLETE.zip ├── Backup-07-ANGULAR-FIRST-COMPLETE-VERSION.md ├── Backup-07-ANGULAR-FIRST-COMPLETE-VERSION.zip ├── Backup-08-CREATED-HELLO-WORLD-REST-SERVICES.md ├── Backup-08-CREATED-HELLO-WORLD-REST-SERVICES.zip ├── Backup-09-Hello-World-Message-Call-Basic-Implementation-In-Angular.md ├── Backup-09-Hello-World-Message-Call-Basic-Implementation-In-Angular.zip ├── Backup-10-Hello-World-Message-Call-With-Basic-Error-Handling.md ├── Backup-10-Hello-World-Message-Call-With-Basic-Error-Handling.zip ├── Backup-11-Hello-World-With-Parameter.md ├── Backup-11-Hello-World-With-Parameter.zip ├── Backup-12-Update-Todo-Display.md ├── Backup-12-Update-Todo-Display.zip ├── Backup-13-Update-Todo-Display-Target-Data-2-Way-Binding.md ├── Backup-13-Update-Todo-Display-Target-Data-2-Way-Binding.zip ├── Backup-14-Create-Todo-Functionality-Done.md ├── Backup-14-Create-Todo-Functionality-Done.zip ├── Backup-15-Validation-Done-Section-Complete.md ├── Backup-15-Validation-Done-Section-Complete.zip ├── Backup-16-Front-end-using-http-basic-for-executing-http-requests.md ├── Backup-16-Front-end-using-http-basic-for-executing-http-requests.zip ├── Backup-17-Login-Working-After-disabling-http-interceptor.md ├── Backup-17-Login-Working-After-disabling-http-interceptor.zip ├── Backup-18-Working-App-With-Basic-Authentication-After-Enabling-HttpInterceptor.md ├── Backup-18-Working-App-With-Basic-Authentication-After-Enabling-HttpInterceptor.zip ├── Backup-19-Refactoring-To-Constants.md ├── Backup-19-Refactoring-To-Constants.zip ├── Backup-20-Working-With-JWT-Authentication.md ├── Backup-20-Working-With-JWT-Authentication.zip ├── Backup-21-JWT-Auth-Connected-To-Jpa.md ├── Backup-21-JWT-Auth-Connected-To-Jpa.zip ├── LICENSE ├── MigrateToAngularLatest.md ├── MigrateToAngularStandalone.md ├── README.md ├── StepsForMigratingToAngular18OrLatest.md ├── frontend ├── .DS_Store ├── todo-2023-11-angular15.zip └── todo │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── final.md │ ├── final.zip │ ├── notes-2023-update.md │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.config.ts │ │ ├── app.constants.ts │ │ ├── app.routes.ts │ │ ├── error │ │ │ ├── error.component.css │ │ │ ├── error.component.html │ │ │ ├── error.component.spec.ts │ │ │ └── error.component.ts │ │ ├── footer │ │ │ ├── footer.component.css │ │ │ ├── footer.component.html │ │ │ ├── footer.component.spec.ts │ │ │ └── footer.component.ts │ │ ├── list-todos │ │ │ ├── list-todos.component.css │ │ │ ├── list-todos.component.html │ │ │ ├── list-todos.component.spec.ts │ │ │ └── list-todos.component.ts │ │ ├── login │ │ │ ├── login.component.css │ │ │ ├── login.component.html │ │ │ ├── login.component.spec.ts │ │ │ └── login.component.ts │ │ ├── logout │ │ │ ├── logout.component.css │ │ │ ├── logout.component.html │ │ │ ├── logout.component.spec.ts │ │ │ └── logout.component.ts │ │ ├── menu │ │ │ ├── menu.component.css │ │ │ ├── menu.component.html │ │ │ ├── menu.component.spec.ts │ │ │ └── menu.component.ts │ │ ├── service │ │ │ ├── basic-authentication.service.ts │ │ │ ├── data │ │ │ │ ├── todo-data.service.spec.ts │ │ │ │ ├── todo-data.service.ts │ │ │ │ ├── welcome-data.service.spec.ts │ │ │ │ └── welcome-data.service.ts │ │ │ ├── hardcoded-authentication.service.spec.ts │ │ │ ├── hardcoded-authentication.service.ts │ │ │ ├── http │ │ │ │ ├── http-intercepter-basic-auth.service.spec.ts │ │ │ │ └── http-intercepter-basic-auth.service.ts │ │ │ ├── route-guard.service.spec.ts │ │ │ └── route-guard.service.ts │ │ ├── todo │ │ │ ├── todo.component.css │ │ │ ├── todo.component.html │ │ │ ├── todo.component.spec.ts │ │ │ └── todo.component.ts │ │ └── welcome │ │ │ ├── welcome.component.css │ │ │ ├── welcome.component.html │ │ │ ├── welcome.component.spec.ts │ │ │ └── welcome.component.ts │ ├── assets │ │ └── .gitkeep │ ├── browserslist │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── karma.conf.js │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ ├── todos.txt │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── tslint.json │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── npm ├── package-lock.json └── package.json ├── restful-web-services ├── .classpath ├── .gitignore ├── .project ├── .settings │ ├── org.eclipse.core.resources.prefs │ ├── org.eclipse.jdt.core.prefs │ └── org.eclipse.m2e.core.prefs ├── .springBeans ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── in28minutes │ │ │ └── rest │ │ │ ├── basic │ │ │ └── auth │ │ │ │ ├── AuthenticationBean.java │ │ │ │ ├── BasicAuthenticationController.java │ │ │ │ └── SpringSecurityConfigurationBasicAuth.java │ │ │ └── webservices │ │ │ └── restfulwebservices │ │ │ ├── BcryptEncoderTest.java │ │ │ ├── RestfulWebServicesApplication.java │ │ │ ├── helloworld │ │ │ ├── HelloWorldBean.java │ │ │ └── HelloWorldController.java │ │ │ ├── jwt │ │ │ ├── JWTWebSecurityConfig.java │ │ │ ├── JwtAuthenticationRestController.java │ │ │ ├── JwtTokenRequest.java │ │ │ ├── JwtTokenResponse.java │ │ │ └── JwtTokenService.java │ │ │ └── todo │ │ │ ├── Todo.java │ │ │ ├── TodoHardcodedService.java │ │ │ ├── TodoJpaRepository.java │ │ │ ├── TodoJpaResource.java │ │ │ └── TodoResource.java │ └── resources │ │ ├── application.properties │ │ └── data.sql │ └── test │ └── java │ └── com │ └── in28minutes │ └── rest │ └── webservices │ └── restfulwebservices │ └── RestfulWebServicesApplicationTests.java ├── step-by-step-changes-to-standalone.md └── zz-spring-boot-in-10-steps ├── .DS_Store ├── .gitignore ├── HELP.md ├── final.md ├── notes.txt ├── pom.xml ├── readme.md └── src ├── .DS_Store ├── main ├── java │ └── com │ │ └── in28minutes │ │ └── springboot │ │ └── learnspringboot │ │ ├── Course.java │ │ ├── CourseController.java │ │ ├── CurrencyConfigurationController.java │ │ ├── CurrencyServiceConfiguration.java │ │ └── LearnSpringBootApplication.java └── resources │ ├── application-dev.properties │ ├── application-prod.properties │ └── application.properties └── test └── java └── com └── in28minutes └── springboot └── learnspringboot └── LearnSpringBootApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | restful-web-services/restful-web-services.iml 3 | frontend/.DS_Store 4 | -------------------------------------------------------------------------------- /00-02-update-2023-02.md: -------------------------------------------------------------------------------- 1 | ## Code Changes 2 | 3 | ### /frontend/todo/src/app/list-todos/list-todos.component.ts 4 | 5 | ``` 6 | export class ListTodosComponent implements OnInit { 7 | 8 | - todos: Todo[] 9 | + todos: Todo[] = []; 10 | 11 | - message: string 12 | + message: string = ''; 13 | 14 | 15 | - deleteTodo(id) { 16 | + deleteTodo(id: number) { 17 | 18 | - updateTodo(id) { 19 | + updateTodo(id: number) { 20 | ``` 21 | 22 | ### /frontend/todo/src/app/menu/menu.component.ts 23 | 24 | ``` 25 | export class MenuComponent implements OnInit { 26 | 27 | - constructor(private hardcodedAuthenticationService: HardcodedAuthenticationService) { } 28 | + constructor(public hardcodedAuthenticationService : HardcodedAuthenticationService) { } 29 | 30 | ``` 31 | 32 | ### /frontend/todo/src/app/service/basic-authentication.service.ts 33 | 34 | ``` 35 | - executeJWTAuthenticationService(username, password) { 36 | + executeJWTAuthenticationService(username: string, password: string) { 37 | 38 | 39 | - executeAuthenticationService(username, password) { 40 | + executeAuthenticationService(username: string, password: string) { 41 | 42 | getAuthenticatedToken() { 43 | if (this.getAuthenticatedUser()) 44 | return sessionStorage.getItem(TOKEN) 45 | + return null 46 | } 47 | ``` 48 | 49 | ### /frontend/todo/src/app/service/data/todo-data.service.ts 50 | 51 | ``` 52 | - retrieveAllTodos(username) { 53 | + retrieveAllTodos(username: string) { 54 | 55 | - deleteTodo(username, id){ 56 | + deleteTodo(username: string, id: number) { 57 | 58 | - retrieveTodo(username, id){ 59 | + retrieveTodo(username: string, id: number) { 60 | 61 | - updateTodo(username, id, todo){ 62 | + updateTodo(username: string, id: number, todo: Todo) { 63 | 64 | - createTodo(username, todo){ 65 | + createTodo(username: string, todo: Todo) { 66 | ``` 67 | 68 | ### /frontend/todo/src/app/service/data/welcome-data.service.ts 69 | 70 | ``` 71 | - executeHelloWorldServiceWithPathVariable(name) { 72 | + executeHelloWorldServiceWithPathVariable(name: string) { 73 | ``` 74 | 75 | ### /frontend/todo/src/app/service/hardcoded-authentication.service.ts 76 | 77 | ``` 78 | - authenticate(username, password) { 79 | + authenticate(username: string, password: string) { 80 | ``` 81 | 82 | ### /frontend/todo/src/app/todo/todo.component.ts 83 | 84 | ``` 85 | export class TodoComponent implements OnInit { 86 | 87 | - id:number 88 | - todo: Todo 89 | + id: number = 0; 90 | + todo: Todo = new Todo(this.id, '', false, new Date()); 91 | ``` 92 | 93 | ### /frontend/todo/src/app/welcome/welcome.component.ts 94 | 95 | ``` 96 | export class WelcomeComponent implements OnInit { 97 | 98 | - welcomeMessageFromService:string 99 | + welcomeMessageFromService: string = '' 100 | 101 | - handleSuccessfulResponse(response){ 102 | + handleSuccessfulResponse(response: any) { 103 | 104 | - handleErrorResponse(error) { 105 | + handleErrorResponse(error: any) { 106 | ``` 107 | -------------------------------------------------------------------------------- /00-03-update-2023-02-course-changes.md: -------------------------------------------------------------------------------- 1 | ## Updated Video Lectures 2 | ``` 3 | Step 75 - Configure Spring Security to disable CSRF and enable OPTION Requests 4 | Step 86 - Importing JWT Framework into Eclipse 5 | ``` 6 | 7 | ## New Video Lectures 8 | 9 | ``` 10 | SS Step 00 - Getting started with Spring Security 11 | SS Step 01 - Understanding Security Fundamentals 12 | SS Step 02 - Understanding Security Principles 13 | SS Step 03 - Getting Started with Spring Security 14 | SS Step 04 - Exploring Default Spring Security Configuration 15 | SS Step 05 - Creating Spring Boot Project for Spring Security 16 | SS Step 06 - Exploring Spring Security - Form Authentication 17 | SS Step 07 - Exploring Spring Security - Basic Authentication 18 | SS Step 08 - Exploring Spring Security - Cross Site Request Forgery - CSRF 19 | SS Step 09 - Exploring Spring Security - CSRF for REST API 20 | SS Step 10 - Creating Spring Security Configuration to Disable CSRF 21 | SS Step 11 - Exploring Spring Security - Getting Started with CORS 22 | SS Step 12 - Exploring Spring Security - Storing User Credentials in memory 23 | SS Step 13 - Exploring Spring Security - Storing User Credentials using JDBC 24 | SS Step 14 - Understanding Encoding vs Hashing vs Encryption 25 | SS Step 15 - Exploring Spring Security - Storing Bcrypt Encoded Passwords 26 | SS Step 16 - Getting Started with JWT Authentication 27 | SS Step 17 - Setting up JWT Auth with Spring Security and Spring Boot - 1 28 | SS Step 18 - Setting up JWT Auth with Spring Security and Spring Boot - 2 29 | SS Step 19 - Setting up JWT Resource with Spring Security and Spring Boot - 1 30 | SS Step 20 - Setting up JWT Resource with Spring Security and Spring Boot - 2 31 | SS Step 21 - Understanding Spring Security Authentication 32 | SS Step 22 - Exploring Spring Security Authorization 33 | SS Step 23 - Creating a Spring Boot Project for OAuth with Spring Security 34 | SS Step 24 - Getting Started with Spring Boot and OAuth2 - Login with Google 35 | SS Step 25 - Quick Review - Securing Spring Boot Apps with Spring Security 36 | ``` 37 | 38 | ## Course Compatible with Spring Boot 3 & Latest Angular Version 39 | 40 | Thank you so much for enrolling, I'm so excited for you to start your learning journey! 41 | 42 | #### 👉 DO YOU KNOW? 43 | 44 | All code in the course is updated to Spring Boot 3 and the latest version of Angular. 45 | 46 | #### 👉 WHAT SHOULD YOU DO? 47 | 48 | Bookmark the Github Repo of the course - https://github.com/in28minutes/full-stack-with-angular-and-spring-boot 49 | 50 | I'll see you at the next lecture! 51 | 52 | Happy Learning 53 | 54 | Ranga 55 | 56 | 57 | ## Course Update - Use Latest Angular Version 58 | 59 | Congratulations on making a great choice! 60 | 61 | #### 👉 DO YOU KNOW? 62 | 63 | All code in the course is updated to Spring Boot 3 and the latest version of Angular. 64 | 65 | #### 👉 WHAT SHOULD YOU DO? 66 | 67 | In the next step, you can install latest Angular CLI by using this command in the next lecture 68 | 69 | `npm install -g @angular/cli` 70 | 71 | 72 | I'll see you at the next lecture! 73 | 74 | Happy Learning 75 | 76 | Ranga 77 | 78 | 79 | ## Course Update - ng lint & ng e2e 80 | 81 | I'm delighted to have the privilege of being your instructor. 82 | 83 | #### 👉 DO YOU KNOW? 84 | 85 | In the next step, ng lint & ng e2e commands are no longer available 86 | 87 | #### 👉 WHAT SHOULD YOU DO? 88 | 89 | Ignore any failures related to ng lint & ng e2e commands 90 | 91 | I'll see you at the next lecture! 92 | 93 | Happy Learning 94 | 95 | Ranga 96 | 97 | 98 | ## Course Update - Strict Typing of Parameters and Variables 99 | 100 | #### 👉 DO YOU KNOW? 101 | 102 | In recent versions of Angular, strict typing and mandatory initialization is enforced for parameters and variables. 103 | 104 | #### 👉 WHAT SHOULD YOU DO? 105 | 106 | We request you to understand the changes and follow them in the next lectures! 107 | 108 | All changes listed here - https://github.com/in28minutes/full-stack-with-angular-and-spring-boot/blob/master/00-02-update-2023-02.md 109 | 110 | Here is an example diff between previous and current Angular versions: 111 | 112 | ### /frontend/todo/src/app/service/hardcoded-authentication.service.ts 113 | 114 | ``` 115 | - authenticate(username, password) { 116 | + authenticate(username: string, password: string) { 117 | ``` 118 | 119 | I'll see you at the next lecture! 120 | 121 | Happy Learning 122 | 123 | Ranga 124 | 125 | 126 | ## Reminder - Course Update - Strict Typing of Parameters and Variables 127 | 128 | - Step 31 - Creating an Independent Authentication Service Component 129 | 130 | #### 👉 DO YOU KNOW? 131 | 132 | In recent versions of Angular, strict typing and mandatory initialization is enforced for parameters and variables. 133 | 134 | #### 👉 WHAT SHOULD YOU DO? 135 | 136 | We request you to understand the changes and follow them in the next lectures! 137 | 138 | All changes listed here - https://github.com/in28minutes/full-stack-with-angular-and-spring-boot/blob/master/00-02-update-2023-02.md 139 | 140 | Here are a couple of diff examples between previous and current Angular versions: 141 | 142 | 143 | ### /frontend/todo/src/app/welcome/welcome.component.ts 144 | 145 | ``` 146 | export class WelcomeComponent implements OnInit { 147 | 148 | - welcomeMessageFromService:string 149 | + welcomeMessageFromService: string = '' 150 | 151 | - handleSuccessfulResponse(response){ 152 | + handleSuccessfulResponse(response: any) { 153 | 154 | - handleErrorResponse(error) { 155 | + handleErrorResponse(error: any) { 156 | ``` 157 | 158 | ### /frontend/todo/src/app/welcome/welcome.component.ts 159 | 160 | ``` 161 | export class WelcomeComponent implements OnInit { 162 | 163 | - welcomeMessageFromService:string 164 | + welcomeMessageFromService: string = '' 165 | 166 | - handleSuccessfulResponse(response){ 167 | + handleSuccessfulResponse(response: any) { 168 | 169 | - handleErrorResponse(error) { 170 | + handleErrorResponse(error: any) { 171 | ``` 172 | 173 | ### /frontend/todo/src/app/service/data/welcome-data.service.ts 174 | 175 | ``` 176 | - executeHelloWorldServiceWithPathVariable(name) { 177 | + executeHelloWorldServiceWithPathVariable(name: string) { 178 | ``` 179 | 180 | I'll see you at the next lecture! 181 | 182 | Happy Learning 183 | 184 | Ranga 185 | 186 | 187 | ## Next Lecture - Configure Basic Authentication 188 | 189 | I'm delighted to have the privilege of being your instructor. 190 | 191 | 192 | #### 👉 DO YOU KNOW? 193 | 194 | In the next lecture, we will configure basic authentication for our REST API. 195 | 196 | #### 👉 WHAT SHOULD YOU DO? 197 | 198 | Request you to bookmark this page. We will make use of it in the next lecture - https://github.com/in28minutes/full-stack-with-angular-and-spring-boot/blob/master/99-reuse/01-spring-security.md 199 | 200 | 201 | I'll see you at the next lecture! 202 | 203 | Happy Learning 204 | 205 | Ranga 206 | 207 | 208 | ## Next Lecture - Configure JWT Authentication 209 | 210 | I'm delighted to have the privilege of being your instructor. 211 | 212 | 213 | #### 👉 DO YOU KNOW? 214 | 215 | In the next lecture, we will configure JWT authentication for our REST API. 216 | 217 | #### 👉 WHAT SHOULD YOU DO? 218 | 219 | Request you to bookmark this page. We will make use of it in the next lecture - https://github.com/in28minutes/full-stack-with-angular-and-spring-boot/blob/master/99-reuse/01-spring-security.md 220 | 221 | 222 | I'll see you at the next lecture! 223 | 224 | Happy Learning 225 | 226 | Ranga 227 | 228 | -------------------------------------------------------------------------------- /00-AngularHardcoded.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/00-AngularHardcoded.zip -------------------------------------------------------------------------------- /00-spring-boot-3-updates.md: -------------------------------------------------------------------------------- 1 | ## Course Updates 2 | 3 | Really excited to announce that the entire course is now upgraded to Spring Boot 3 4 | 5 | ### Complete list of Changes 6 | 7 | Here's the complete list of changes for Spring Boot 3 Upgrade - https://github.com/in28minutes/full-stack-with-angular-and-spring-boot/commit/330352423d1f41946c15fe07acf589331b02b7db 8 | 9 | ### Basic Authentication 10 | 11 | 12 | 1) WebSecurityConfigurerAdapter is deprecated 13 | 2) antMatchers is now deprecated. Please use requestMatchers instead. 14 | 15 | 16 | Please find the complete class below 17 | 18 | ``` 19 | @Configuration 20 | @EnableWebSecurity 21 | @EnableMethodSecurity 22 | public class SpringSecurityConfigurationBasicAuth { 23 | 24 | @Bean 25 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 26 | http.authorizeHttpRequests(auth -> auth.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()); 27 | 28 | http.csrf(csrf -> csrf.disable()); 29 | 30 | http.httpBasic(withDefaults()); 31 | 32 | return http.build(); 33 | } 34 | 35 | } 36 | ``` 37 | 38 | 39 | ### JWT 40 | 41 | Dependency Changes 42 | 43 | ```xml 44 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-oauth2-resource-server 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-configuration-processor 59 | 60 | ``` 61 | 62 | Code Changes - https://github.com/in28minutes/full-stack-with-angular-and-spring-boot#core-jwt-components---spring-boot-3 -------------------------------------------------------------------------------- /01-AngularHttpWithoutAuthentication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/01-AngularHttpWithoutAuthentication.zip -------------------------------------------------------------------------------- /01-SpringBootWithoutAuthentication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/01-SpringBootWithoutAuthentication.zip -------------------------------------------------------------------------------- /02-AngularHttpBasicAuthentication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/02-AngularHttpBasicAuthentication.zip -------------------------------------------------------------------------------- /02-SpringBootBasicAuthentication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/02-SpringBootBasicAuthentication.zip -------------------------------------------------------------------------------- /03-AngularHttpJwtAuthentication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/03-AngularHttpJwtAuthentication.zip -------------------------------------------------------------------------------- /03-SpringBootWithJwtAuthentication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/03-SpringBootWithJwtAuthentication.zip -------------------------------------------------------------------------------- /71-spring-security/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/.DS_Store -------------------------------------------------------------------------------- /71-spring-security/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | *.idea 3 | build/** 4 | -------------------------------------------------------------------------------- /71-spring-security/HELP.md: -------------------------------------------------------------------------------- 1 | # Read Me First 2 | The following was discovered as part of building this project: 3 | 4 | * The original package name 'com.in28minutes.learn-spring-security' is invalid and this project uses 'com.in28minutes.learnspringsecurity' instead. 5 | 6 | # Getting Started 7 | 8 | ### Reference Documentation 9 | For further reference, please consider the following sections: 10 | 11 | * [Official Gradle documentation](https://docs.gradle.org) 12 | * [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.0.0-RC2/gradle-plugin/reference/html/) 13 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.0.0-RC2/gradle-plugin/reference/html/#build-image) 14 | * [Spring Web](https://docs.spring.io/spring-boot/docs/3.0.0-RC2/reference/htmlsingle/#web) 15 | * [Spring Security](https://docs.spring.io/spring-boot/docs/3.0.0-RC2/reference/htmlsingle/#web.security) 16 | 17 | ### Guides 18 | The following guides illustrate how to use some features concretely: 19 | 20 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) 21 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) 22 | * [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) 23 | * [Securing a Web Application](https://spring.io/guides/gs/securing-web/) 24 | * [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/) 25 | * [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/) 26 | 27 | ### Additional Links 28 | These additional references should also help you: 29 | 30 | * [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle) 31 | 32 | -------------------------------------------------------------------------------- /71-spring-security/Step06.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Complete Code Example 6 | 7 | 8 | ### /src/main/java/com/in28minutes/learnspringsecurity/HelloWorldResource.java 9 | 10 | ```java 11 | package com.in28minutes.learnspringsecurity; 12 | 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | @RestController 17 | public class HelloWorldResource { 18 | 19 | @GetMapping("/hello-world") 20 | public String helloWorld() { 21 | return "Hello World"; 22 | } 23 | 24 | } 25 | ``` 26 | --- 27 | 28 | ### /src/main/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplication.java 29 | 30 | ```java 31 | package com.in28minutes.learnspringsecurity; 32 | 33 | import org.springframework.boot.SpringApplication; 34 | import org.springframework.boot.autoconfigure.SpringBootApplication; 35 | 36 | @SpringBootApplication 37 | public class LearnSpringSecurityApplication { 38 | 39 | public static void main(String[] args) { 40 | SpringApplication.run(LearnSpringSecurityApplication.class, args); 41 | } 42 | 43 | } 44 | ``` 45 | --- 46 | 47 | ### /src/main/resources/application.properties 48 | 49 | ```properties 50 | spring.main.banner-mode=off 51 | logging.pattern.console= %d{MM-dd HH:mm:ss} - %logger{36} - %msg%n 52 | 53 | ``` 54 | --- 55 | 56 | ### /src/test/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplicationTests.java 57 | 58 | ```java 59 | package com.in28minutes.learnspringsecurity; 60 | 61 | import org.junit.jupiter.api.Test; 62 | import org.springframework.boot.test.context.SpringBootTest; 63 | 64 | @SpringBootTest 65 | class LearnSpringSecurityApplicationTests { 66 | 67 | @Test 68 | void contextLoads() { 69 | } 70 | 71 | } 72 | ``` 73 | --- 74 | -------------------------------------------------------------------------------- /71-spring-security/Step06.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step06.zip -------------------------------------------------------------------------------- /71-spring-security/Step07.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Complete Code Example 6 | 7 | 8 | ### /src/main/java/com/in28minutes/learnspringsecurity/HelloWorldResource.java 9 | 10 | ```java 11 | package com.in28minutes.learnspringsecurity; 12 | 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | @RestController 17 | public class HelloWorldResource { 18 | 19 | @GetMapping("/hello-world") 20 | public String helloWorld() { 21 | return "Hello World v1"; 22 | } 23 | 24 | } 25 | ``` 26 | --- 27 | 28 | ### /src/main/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplication.java 29 | 30 | ```java 31 | package com.in28minutes.learnspringsecurity; 32 | 33 | import org.springframework.boot.SpringApplication; 34 | import org.springframework.boot.autoconfigure.SpringBootApplication; 35 | 36 | @SpringBootApplication 37 | public class LearnSpringSecurityApplication { 38 | 39 | public static void main(String[] args) { 40 | SpringApplication.run(LearnSpringSecurityApplication.class, args); 41 | } 42 | 43 | } 44 | ``` 45 | --- 46 | 47 | ### /src/main/resources/application.properties 48 | 49 | ```properties 50 | spring.main.banner-mode=off 51 | logging.pattern.console= %d{MM-dd HH:mm:ss} - %logger{36} - %msg%n 52 | 53 | spring.security.user.name=in28minutes 54 | spring.security.user.password=dummy 55 | ``` 56 | --- 57 | 58 | ### /src/test/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplicationTests.java 59 | 60 | ```java 61 | package com.in28minutes.learnspringsecurity; 62 | 63 | import org.junit.jupiter.api.Test; 64 | import org.springframework.boot.test.context.SpringBootTest; 65 | 66 | @SpringBootTest 67 | class LearnSpringSecurityApplicationTests { 68 | 69 | @Test 70 | void contextLoads() { 71 | } 72 | 73 | } 74 | ``` 75 | --- 76 | -------------------------------------------------------------------------------- /71-spring-security/Step07.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step07.zip -------------------------------------------------------------------------------- /71-spring-security/Step08.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Complete Code Example 6 | 7 | 8 | ### /src/main/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplication.java 9 | 10 | ```java 11 | package com.in28minutes.learnspringsecurity; 12 | 13 | import org.springframework.boot.SpringApplication; 14 | import org.springframework.boot.autoconfigure.SpringBootApplication; 15 | 16 | @SpringBootApplication 17 | public class LearnSpringSecurityApplication { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(LearnSpringSecurityApplication.class, args); 21 | } 22 | 23 | } 24 | ``` 25 | --- 26 | 27 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/HelloWorldResource.java 28 | 29 | ```java 30 | package com.in28minutes.learnspringsecurity.resources; 31 | 32 | import org.springframework.web.bind.annotation.GetMapping; 33 | import org.springframework.web.bind.annotation.RestController; 34 | 35 | @RestController 36 | public class HelloWorldResource { 37 | 38 | @GetMapping("/hello-world") 39 | public String helloWorld() { 40 | return "Hello World v1"; 41 | } 42 | 43 | } 44 | ``` 45 | --- 46 | 47 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/TodoResource.java 48 | 49 | ```java 50 | package com.in28minutes.learnspringsecurity.resources; 51 | 52 | import java.util.List; 53 | 54 | import org.slf4j.Logger; 55 | import org.slf4j.LoggerFactory; 56 | import org.springframework.web.bind.annotation.GetMapping; 57 | import org.springframework.web.bind.annotation.PathVariable; 58 | import org.springframework.web.bind.annotation.PostMapping; 59 | import org.springframework.web.bind.annotation.RequestBody; 60 | import org.springframework.web.bind.annotation.RestController; 61 | 62 | @RestController 63 | public class TodoResource { 64 | 65 | private Logger logger = LoggerFactory.getLogger(getClass()); 66 | 67 | private static final List TODOS_LIST = 68 | List.of(new Todo("in28minutes", "Learn AWS"), 69 | new Todo("in28minutes", "Get AWS Certified")); 70 | 71 | @GetMapping("/todos") 72 | public List retrieveAllTodos() { 73 | return TODOS_LIST; 74 | } 75 | 76 | @GetMapping("/users/{username}/todos") 77 | public Todo retrieveTodosForSpecificUser(@PathVariable String username) { 78 | return TODOS_LIST.get(0); 79 | } 80 | 81 | @PostMapping("/users/{username}/todos") 82 | public void createTodoForSpecificUser(@PathVariable String username 83 | , @RequestBody Todo todo) { 84 | logger.info("Create {} for {}", todo, username); 85 | } 86 | 87 | } 88 | 89 | record Todo (String username, String description) {} 90 | ``` 91 | --- 92 | 93 | ### /src/main/resources/application.properties 94 | 95 | ```properties 96 | spring.main.banner-mode=off 97 | logging.pattern.console= %d{MM-dd HH:mm:ss} - %logger{36} - %msg%n 98 | 99 | spring.security.user.name=in28minutes 100 | spring.security.user.password=dummy 101 | ``` 102 | --- 103 | 104 | ### /src/test/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplicationTests.java 105 | 106 | ```java 107 | package com.in28minutes.learnspringsecurity; 108 | 109 | import org.junit.jupiter.api.Test; 110 | import org.springframework.boot.test.context.SpringBootTest; 111 | 112 | @SpringBootTest 113 | class LearnSpringSecurityApplicationTests { 114 | 115 | @Test 116 | void contextLoads() { 117 | } 118 | 119 | } 120 | ``` 121 | --- 122 | -------------------------------------------------------------------------------- /71-spring-security/Step08.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step08.zip -------------------------------------------------------------------------------- /71-spring-security/Step10.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Complete Code Example 6 | 7 | 8 | ### /src/main/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplication.java 9 | 10 | ```java 11 | package com.in28minutes.learnspringsecurity; 12 | 13 | import org.springframework.boot.SpringApplication; 14 | import org.springframework.boot.autoconfigure.SpringBootApplication; 15 | 16 | @SpringBootApplication 17 | public class LearnSpringSecurityApplication { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(LearnSpringSecurityApplication.class, args); 21 | } 22 | 23 | } 24 | ``` 25 | --- 26 | 27 | ### /src/main/java/com/in28minutes/learnspringsecurity/basic/BasicAuthSecurityConfiguration.java 28 | 29 | ```java 30 | package com.in28minutes.learnspringsecurity.basic; 31 | 32 | import org.springframework.context.annotation.Bean; 33 | import org.springframework.context.annotation.Configuration; 34 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 35 | import org.springframework.security.config.http.SessionCreationPolicy; 36 | import org.springframework.security.web.SecurityFilterChain; 37 | 38 | 39 | @Configuration 40 | public class BasicAuthSecurityConfiguration { 41 | 42 | @Bean 43 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 44 | 45 | http.authorizeHttpRequests( 46 | auth -> { 47 | auth.anyRequest().authenticated(); 48 | }); 49 | 50 | http.sessionManagement( 51 | session -> 52 | session.sessionCreationPolicy( 53 | SessionCreationPolicy.STATELESS) 54 | ); 55 | 56 | //http.formLogin(); 57 | http.httpBasic(withDefaults()); 58 | 59 | http.csrf(csrf -> csrf.disable()); 60 | 61 | return http.build(); 62 | } 63 | 64 | 65 | } 66 | ``` 67 | --- 68 | 69 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/HelloWorldResource.java 70 | 71 | ```java 72 | package com.in28minutes.learnspringsecurity.resources; 73 | 74 | import org.springframework.web.bind.annotation.GetMapping; 75 | import org.springframework.web.bind.annotation.RestController; 76 | 77 | @RestController 78 | public class HelloWorldResource { 79 | 80 | @GetMapping("/hello-world") 81 | public String helloWorld() { 82 | return "Hello World v1"; 83 | } 84 | 85 | } 86 | ``` 87 | --- 88 | 89 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/SpringSecurityPlayResource.java 90 | 91 | ```java 92 | package com.in28minutes.learnspringsecurity.resources; 93 | 94 | import org.springframework.security.web.csrf.CsrfToken; 95 | import org.springframework.web.bind.annotation.GetMapping; 96 | import org.springframework.web.bind.annotation.RestController; 97 | 98 | import jakarta.servlet.http.HttpServletRequest; 99 | 100 | @RestController 101 | public class SpringSecurityPlayResource { 102 | 103 | @GetMapping("/csrf-token") 104 | public CsrfToken retrieveCsrfToken(HttpServletRequest request) { 105 | return (CsrfToken) request.getAttribute("_csrf"); 106 | } 107 | 108 | } 109 | ``` 110 | --- 111 | 112 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/TodoResource.java 113 | 114 | ```java 115 | package com.in28minutes.learnspringsecurity.resources; 116 | 117 | import java.util.List; 118 | 119 | import org.slf4j.Logger; 120 | import org.slf4j.LoggerFactory; 121 | import org.springframework.web.bind.annotation.GetMapping; 122 | import org.springframework.web.bind.annotation.PathVariable; 123 | import org.springframework.web.bind.annotation.PostMapping; 124 | import org.springframework.web.bind.annotation.RequestBody; 125 | import org.springframework.web.bind.annotation.RestController; 126 | 127 | @RestController 128 | public class TodoResource { 129 | 130 | private Logger logger = LoggerFactory.getLogger(getClass()); 131 | 132 | private static final List TODOS_LIST = 133 | List.of(new Todo("in28minutes", "Learn AWS"), 134 | new Todo("in28minutes", "Get AWS Certified")); 135 | 136 | @GetMapping("/todos") 137 | public List retrieveAllTodos() { 138 | return TODOS_LIST; 139 | } 140 | 141 | @GetMapping("/users/{username}/todos") 142 | public Todo retrieveTodosForSpecificUser(@PathVariable String username) { 143 | return TODOS_LIST.get(0); 144 | } 145 | 146 | @PostMapping("/users/{username}/todos") 147 | public void createTodoForSpecificUser(@PathVariable String username 148 | , @RequestBody Todo todo) { 149 | logger.info("Create {} for {}", todo, username); 150 | } 151 | 152 | } 153 | 154 | record Todo (String username, String description) {} 155 | ``` 156 | --- 157 | 158 | ### /src/main/resources/application.properties 159 | 160 | ```properties 161 | spring.main.banner-mode=off 162 | logging.pattern.console= %d{MM-dd HH:mm:ss} - %logger{36} - %msg%n 163 | 164 | spring.security.user.name=in28minutes 165 | spring.security.user.password=dummy 166 | ``` 167 | --- 168 | 169 | ### /src/test/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplicationTests.java 170 | 171 | ```java 172 | package com.in28minutes.learnspringsecurity; 173 | 174 | import org.junit.jupiter.api.Test; 175 | import org.springframework.boot.test.context.SpringBootTest; 176 | 177 | @SpringBootTest 178 | class LearnSpringSecurityApplicationTests { 179 | 180 | @Test 181 | void contextLoads() { 182 | } 183 | 184 | } 185 | ``` 186 | --- 187 | -------------------------------------------------------------------------------- /71-spring-security/Step10.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step10.zip -------------------------------------------------------------------------------- /71-spring-security/Step12.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Complete Code Example 6 | 7 | 8 | ### /src/main/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplication.java 9 | 10 | ```java 11 | package com.in28minutes.learnspringsecurity; 12 | 13 | import org.springframework.boot.SpringApplication; 14 | import org.springframework.boot.autoconfigure.SpringBootApplication; 15 | 16 | @SpringBootApplication 17 | public class LearnSpringSecurityApplication { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(LearnSpringSecurityApplication.class, args); 21 | } 22 | 23 | } 24 | ``` 25 | --- 26 | 27 | ### /src/main/java/com/in28minutes/learnspringsecurity/basic/BasicAuthSecurityConfiguration.java 28 | 29 | ```java 30 | package com.in28minutes.learnspringsecurity.basic; 31 | 32 | import org.springframework.context.annotation.Bean; 33 | import org.springframework.context.annotation.Configuration; 34 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 35 | import org.springframework.security.config.http.SessionCreationPolicy; 36 | import org.springframework.security.core.userdetails.User; 37 | import org.springframework.security.core.userdetails.UserDetails; 38 | import org.springframework.security.core.userdetails.UserDetailsService; 39 | import org.springframework.security.provisioning.InMemoryUserDetailsManager; 40 | import org.springframework.security.web.SecurityFilterChain; 41 | 42 | 43 | @Configuration 44 | public class BasicAuthSecurityConfiguration { 45 | 46 | @Bean 47 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 48 | 49 | http.authorizeHttpRequests( 50 | auth -> { 51 | auth.anyRequest().authenticated(); 52 | }); 53 | 54 | http.sessionManagement( 55 | session -> 56 | session.sessionCreationPolicy( 57 | SessionCreationPolicy.STATELESS) 58 | ); 59 | 60 | //http.formLogin(); 61 | http.httpBasic(withDefaults()); 62 | 63 | http.csrf(csrf -> csrf.disable()); 64 | 65 | return http.build(); 66 | } 67 | 68 | @Bean 69 | public UserDetailsService userDetailService() { 70 | 71 | var user = User.withUsername("in28minutes") 72 | .password("{noop}dummy") 73 | .roles("USER") 74 | .build(); 75 | 76 | 77 | var admin = User.withUsername("admin") 78 | .password("{noop}dummy") 79 | .roles("ADMIN") 80 | .build(); 81 | 82 | return new InMemoryUserDetailsManager(user, admin); 83 | } 84 | 85 | 86 | } 87 | ``` 88 | --- 89 | 90 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/HelloWorldResource.java 91 | 92 | ```java 93 | package com.in28minutes.learnspringsecurity.resources; 94 | 95 | import org.springframework.web.bind.annotation.GetMapping; 96 | import org.springframework.web.bind.annotation.RestController; 97 | 98 | @RestController 99 | public class HelloWorldResource { 100 | 101 | @GetMapping("/hello-world") 102 | public String helloWorld() { 103 | return "Hello World v1"; 104 | } 105 | 106 | } 107 | ``` 108 | --- 109 | 110 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/SpringSecurityPlayResource.java 111 | 112 | ```java 113 | package com.in28minutes.learnspringsecurity.resources; 114 | 115 | import org.springframework.security.web.csrf.CsrfToken; 116 | import org.springframework.web.bind.annotation.GetMapping; 117 | import org.springframework.web.bind.annotation.RestController; 118 | 119 | import jakarta.servlet.http.HttpServletRequest; 120 | 121 | @RestController 122 | public class SpringSecurityPlayResource { 123 | 124 | @GetMapping("/csrf-token") 125 | public CsrfToken retrieveCsrfToken(HttpServletRequest request) { 126 | return (CsrfToken) request.getAttribute("_csrf"); 127 | } 128 | 129 | } 130 | ``` 131 | --- 132 | 133 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/TodoResource.java 134 | 135 | ```java 136 | package com.in28minutes.learnspringsecurity.resources; 137 | 138 | import java.util.List; 139 | 140 | import org.slf4j.Logger; 141 | import org.slf4j.LoggerFactory; 142 | import org.springframework.web.bind.annotation.GetMapping; 143 | import org.springframework.web.bind.annotation.PathVariable; 144 | import org.springframework.web.bind.annotation.PostMapping; 145 | import org.springframework.web.bind.annotation.RequestBody; 146 | import org.springframework.web.bind.annotation.RestController; 147 | 148 | @RestController 149 | public class TodoResource { 150 | 151 | private Logger logger = LoggerFactory.getLogger(getClass()); 152 | 153 | private static final List TODOS_LIST = 154 | List.of(new Todo("in28minutes", "Learn AWS"), 155 | new Todo("in28minutes", "Get AWS Certified")); 156 | 157 | @GetMapping("/todos") 158 | public List retrieveAllTodos() { 159 | return TODOS_LIST; 160 | } 161 | 162 | @GetMapping("/users/{username}/todos") 163 | public Todo retrieveTodosForSpecificUser(@PathVariable String username) { 164 | return TODOS_LIST.get(0); 165 | } 166 | 167 | @PostMapping("/users/{username}/todos") 168 | public void createTodoForSpecificUser(@PathVariable String username 169 | , @RequestBody Todo todo) { 170 | logger.info("Create {} for {}", todo, username); 171 | } 172 | 173 | } 174 | 175 | record Todo (String username, String description) {} 176 | ``` 177 | --- 178 | 179 | ### /src/main/resources/application.properties 180 | 181 | ```properties 182 | spring.main.banner-mode=off 183 | logging.pattern.console= %d{MM-dd HH:mm:ss} - %logger{36} - %msg%n 184 | 185 | #spring.security.user.name=in28minutes 186 | #spring.security.user.password=dummy 187 | ``` 188 | --- 189 | 190 | ### /src/test/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplicationTests.java 191 | 192 | ```java 193 | package com.in28minutes.learnspringsecurity; 194 | 195 | import org.junit.jupiter.api.Test; 196 | import org.springframework.boot.test.context.SpringBootTest; 197 | 198 | @SpringBootTest 199 | class LearnSpringSecurityApplicationTests { 200 | 201 | @Test 202 | void contextLoads() { 203 | } 204 | 205 | } 206 | ``` 207 | --- 208 | -------------------------------------------------------------------------------- /71-spring-security/Step12.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step12.zip -------------------------------------------------------------------------------- /71-spring-security/Step13.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Complete Code Example 6 | 7 | 8 | ### /src/main/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplication.java 9 | 10 | ```java 11 | package com.in28minutes.learnspringsecurity; 12 | 13 | import org.springframework.boot.SpringApplication; 14 | import org.springframework.boot.autoconfigure.SpringBootApplication; 15 | 16 | @SpringBootApplication 17 | public class LearnSpringSecurityApplication { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(LearnSpringSecurityApplication.class, args); 21 | } 22 | 23 | } 24 | ``` 25 | --- 26 | 27 | ### /src/main/java/com/in28minutes/learnspringsecurity/basic/BasicAuthSecurityConfiguration.java 28 | 29 | ```java 30 | package com.in28minutes.learnspringsecurity.basic; 31 | 32 | import javax.sql.DataSource; 33 | 34 | import org.springframework.context.annotation.Bean; 35 | import org.springframework.context.annotation.Configuration; 36 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 37 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 38 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 39 | import org.springframework.security.config.http.SessionCreationPolicy; 40 | import org.springframework.security.core.userdetails.User; 41 | import org.springframework.security.core.userdetails.UserDetailsService; 42 | import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl; 43 | import org.springframework.security.provisioning.InMemoryUserDetailsManager; 44 | import org.springframework.security.provisioning.JdbcUserDetailsManager; 45 | import org.springframework.security.web.SecurityFilterChain; 46 | 47 | 48 | @Configuration 49 | public class BasicAuthSecurityConfiguration { 50 | 51 | @Bean 52 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 53 | 54 | http.authorizeHttpRequests( 55 | auth -> { 56 | auth.anyRequest().authenticated(); 57 | }); 58 | 59 | http.sessionManagement( 60 | session -> 61 | session.sessionCreationPolicy( 62 | SessionCreationPolicy.STATELESS) 63 | ); 64 | 65 | //http.formLogin(); 66 | http.httpBasic(withDefaults()); 67 | 68 | http.csrf(csrf -> csrf.disable()); 69 | 70 | http.headers(headersConfigurer -> headersConfigurer.frameOptions(frameOptionsConfig -> frameOptionsConfig.sameOrigin())); 71 | 72 | return http.build(); 73 | } 74 | 75 | // @Bean 76 | // public UserDetailsService userDetailService() { 77 | // 78 | // var user = User.withUsername("in28minutes") 79 | // .password("{noop}dummy") 80 | // .roles("USER") 81 | // .build(); 82 | // 83 | // 84 | // var admin = User.withUsername("admin") 85 | // .password("{noop}dummy") 86 | // .roles("ADMIN") 87 | // .build(); 88 | // 89 | // return new InMemoryUserDetailsManager(user, admin); 90 | // } 91 | 92 | @Bean 93 | public DataSource dataSource() { 94 | return new EmbeddedDatabaseBuilder() 95 | .setType(EmbeddedDatabaseType.H2) 96 | .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION) 97 | .build(); 98 | } 99 | 100 | @Bean 101 | public UserDetailsService userDetailService(DataSource dataSource) { 102 | 103 | var user = User.withUsername("in28minutes") 104 | .password("{noop}dummy") 105 | .roles("USER") 106 | .build(); 107 | 108 | var admin = User.withUsername("admin") 109 | .password("{noop}dummy") 110 | .roles("ADMIN", "USER") 111 | .build(); 112 | 113 | var jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource); 114 | jdbcUserDetailsManager.createUser(user); 115 | jdbcUserDetailsManager.createUser(admin); 116 | 117 | return jdbcUserDetailsManager; 118 | } 119 | 120 | 121 | } 122 | ``` 123 | --- 124 | 125 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/HelloWorldResource.java 126 | 127 | ```java 128 | package com.in28minutes.learnspringsecurity.resources; 129 | 130 | import org.springframework.web.bind.annotation.GetMapping; 131 | import org.springframework.web.bind.annotation.RestController; 132 | 133 | @RestController 134 | public class HelloWorldResource { 135 | 136 | @GetMapping("/hello-world") 137 | public String helloWorld() { 138 | return "Hello World v1"; 139 | } 140 | 141 | } 142 | ``` 143 | --- 144 | 145 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/SpringSecurityPlayResource.java 146 | 147 | ```java 148 | package com.in28minutes.learnspringsecurity.resources; 149 | 150 | import org.springframework.security.web.csrf.CsrfToken; 151 | import org.springframework.web.bind.annotation.GetMapping; 152 | import org.springframework.web.bind.annotation.RestController; 153 | 154 | import jakarta.servlet.http.HttpServletRequest; 155 | 156 | @RestController 157 | public class SpringSecurityPlayResource { 158 | 159 | @GetMapping("/csrf-token") 160 | public CsrfToken retrieveCsrfToken(HttpServletRequest request) { 161 | return (CsrfToken) request.getAttribute("_csrf"); 162 | } 163 | 164 | } 165 | ``` 166 | --- 167 | 168 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/TodoResource.java 169 | 170 | ```java 171 | package com.in28minutes.learnspringsecurity.resources; 172 | 173 | import java.util.List; 174 | 175 | import org.slf4j.Logger; 176 | import org.slf4j.LoggerFactory; 177 | import org.springframework.web.bind.annotation.GetMapping; 178 | import org.springframework.web.bind.annotation.PathVariable; 179 | import org.springframework.web.bind.annotation.PostMapping; 180 | import org.springframework.web.bind.annotation.RequestBody; 181 | import org.springframework.web.bind.annotation.RestController; 182 | 183 | @RestController 184 | public class TodoResource { 185 | 186 | private Logger logger = LoggerFactory.getLogger(getClass()); 187 | 188 | private static final List TODOS_LIST = 189 | List.of(new Todo("in28minutes", "Learn AWS"), 190 | new Todo("in28minutes", "Get AWS Certified")); 191 | 192 | @GetMapping("/todos") 193 | public List retrieveAllTodos() { 194 | return TODOS_LIST; 195 | } 196 | 197 | @GetMapping("/users/{username}/todos") 198 | public Todo retrieveTodosForSpecificUser(@PathVariable String username) { 199 | return TODOS_LIST.get(0); 200 | } 201 | 202 | @PostMapping("/users/{username}/todos") 203 | public void createTodoForSpecificUser(@PathVariable String username 204 | , @RequestBody Todo todo) { 205 | logger.info("Create {} for {}", todo, username); 206 | } 207 | 208 | } 209 | 210 | record Todo (String username, String description) {} 211 | ``` 212 | --- 213 | 214 | ### /src/main/resources/application.properties 215 | 216 | ```properties 217 | spring.main.banner-mode=off 218 | logging.pattern.console= %d{MM-dd HH:mm:ss} - %logger{36} - %msg%n 219 | 220 | #spring.security.user.name=in28minutes 221 | #spring.security.user.password=dummy 222 | 223 | spring.datasource.url=jdbc:h2:mem:testdb 224 | ``` 225 | --- 226 | 227 | ### /src/test/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplicationTests.java 228 | 229 | ```java 230 | package com.in28minutes.learnspringsecurity; 231 | 232 | import org.junit.jupiter.api.Test; 233 | import org.springframework.boot.test.context.SpringBootTest; 234 | 235 | @SpringBootTest 236 | class LearnSpringSecurityApplicationTests { 237 | 238 | @Test 239 | void contextLoads() { 240 | } 241 | 242 | } 243 | ``` 244 | --- 245 | -------------------------------------------------------------------------------- /71-spring-security/Step13.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step13.zip -------------------------------------------------------------------------------- /71-spring-security/Step15.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Complete Code Example 6 | 7 | 8 | ### /src/main/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplication.java 9 | 10 | ```java 11 | package com.in28minutes.learnspringsecurity; 12 | 13 | import org.springframework.boot.SpringApplication; 14 | import org.springframework.boot.autoconfigure.SpringBootApplication; 15 | 16 | @SpringBootApplication 17 | public class LearnSpringSecurityApplication { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(LearnSpringSecurityApplication.class, args); 21 | } 22 | 23 | } 24 | ``` 25 | --- 26 | 27 | ### /src/main/java/com/in28minutes/learnspringsecurity/basic/BasicAuthSecurityConfiguration.java 28 | 29 | ```java 30 | package com.in28minutes.learnspringsecurity.basic; 31 | 32 | import javax.sql.DataSource; 33 | 34 | import org.springframework.context.annotation.Bean; 35 | import org.springframework.context.annotation.Configuration; 36 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 37 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 38 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 39 | import org.springframework.security.config.http.SessionCreationPolicy; 40 | import org.springframework.security.core.userdetails.User; 41 | import org.springframework.security.core.userdetails.UserDetailsService; 42 | import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl; 43 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 44 | import org.springframework.security.provisioning.JdbcUserDetailsManager; 45 | import org.springframework.security.web.SecurityFilterChain; 46 | 47 | 48 | @Configuration 49 | public class BasicAuthSecurityConfiguration { 50 | 51 | @Bean 52 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 53 | 54 | http.authorizeHttpRequests( 55 | auth -> { 56 | auth.anyRequest().authenticated(); 57 | }); 58 | 59 | http.sessionManagement( 60 | session -> 61 | session.sessionCreationPolicy( 62 | SessionCreationPolicy.STATELESS) 63 | ); 64 | 65 | //http.formLogin(); 66 | http.httpBasic(withDefaults()); 67 | 68 | http.csrf(csrf -> csrf.disable()); 69 | 70 | http.headers(headersConfigurer -> headersConfigurer.frameOptions(frameOptionsConfig -> frameOptionsConfig.sameOrigin())); 71 | 72 | return http.build(); 73 | } 74 | 75 | // @Bean 76 | // public UserDetailsService userDetailService() { 77 | // 78 | // var user = User.withUsername("in28minutes") 79 | // .password("{noop}dummy") 80 | // .roles("USER") 81 | // .build(); 82 | // 83 | // 84 | // var admin = User.withUsername("admin") 85 | // .password("{noop}dummy") 86 | // .roles("ADMIN") 87 | // .build(); 88 | // 89 | // return new InMemoryUserDetailsManager(user, admin); 90 | // } 91 | 92 | @Bean 93 | public DataSource dataSource() { 94 | return new EmbeddedDatabaseBuilder() 95 | .setType(EmbeddedDatabaseType.H2) 96 | .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION) 97 | .build(); 98 | } 99 | 100 | @Bean 101 | public UserDetailsService userDetailService(DataSource dataSource) { 102 | 103 | var user = User.withUsername("in28minutes") 104 | //.password("{noop}dummy") 105 | .password("dummy") 106 | .passwordEncoder(str -> passwordEncoder().encode(str)) 107 | .roles("USER") 108 | .build(); 109 | 110 | var admin = User.withUsername("admin") 111 | //.password("{noop}dummy") 112 | .password("dummy") 113 | .passwordEncoder(str -> passwordEncoder().encode(str)) 114 | .roles("ADMIN", "USER") 115 | .build(); 116 | 117 | var jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource); 118 | jdbcUserDetailsManager.createUser(user); 119 | jdbcUserDetailsManager.createUser(admin); 120 | 121 | return jdbcUserDetailsManager; 122 | } 123 | 124 | @Bean 125 | public BCryptPasswordEncoder passwordEncoder() { 126 | return new BCryptPasswordEncoder(); 127 | } 128 | 129 | 130 | 131 | } 132 | ``` 133 | --- 134 | 135 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/HelloWorldResource.java 136 | 137 | ```java 138 | package com.in28minutes.learnspringsecurity.resources; 139 | 140 | import org.springframework.web.bind.annotation.GetMapping; 141 | import org.springframework.web.bind.annotation.RestController; 142 | 143 | @RestController 144 | public class HelloWorldResource { 145 | 146 | @GetMapping("/hello-world") 147 | public String helloWorld() { 148 | return "Hello World v1"; 149 | } 150 | 151 | } 152 | ``` 153 | --- 154 | 155 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/SpringSecurityPlayResource.java 156 | 157 | ```java 158 | package com.in28minutes.learnspringsecurity.resources; 159 | 160 | import org.springframework.security.web.csrf.CsrfToken; 161 | import org.springframework.web.bind.annotation.GetMapping; 162 | import org.springframework.web.bind.annotation.RestController; 163 | 164 | import jakarta.servlet.http.HttpServletRequest; 165 | 166 | @RestController 167 | public class SpringSecurityPlayResource { 168 | 169 | @GetMapping("/csrf-token") 170 | public CsrfToken retrieveCsrfToken(HttpServletRequest request) { 171 | return (CsrfToken) request.getAttribute("_csrf"); 172 | } 173 | 174 | } 175 | ``` 176 | --- 177 | 178 | ### /src/main/java/com/in28minutes/learnspringsecurity/resources/TodoResource.java 179 | 180 | ```java 181 | package com.in28minutes.learnspringsecurity.resources; 182 | 183 | import java.util.List; 184 | 185 | import org.slf4j.Logger; 186 | import org.slf4j.LoggerFactory; 187 | import org.springframework.web.bind.annotation.GetMapping; 188 | import org.springframework.web.bind.annotation.PathVariable; 189 | import org.springframework.web.bind.annotation.PostMapping; 190 | import org.springframework.web.bind.annotation.RequestBody; 191 | import org.springframework.web.bind.annotation.RestController; 192 | 193 | @RestController 194 | public class TodoResource { 195 | 196 | private Logger logger = LoggerFactory.getLogger(getClass()); 197 | 198 | private static final List TODOS_LIST = 199 | List.of(new Todo("in28minutes", "Learn AWS"), 200 | new Todo("in28minutes", "Get AWS Certified")); 201 | 202 | @GetMapping("/todos") 203 | public List retrieveAllTodos() { 204 | return TODOS_LIST; 205 | } 206 | 207 | @GetMapping("/users/{username}/todos") 208 | public Todo retrieveTodosForSpecificUser(@PathVariable String username) { 209 | return TODOS_LIST.get(0); 210 | } 211 | 212 | @PostMapping("/users/{username}/todos") 213 | public void createTodoForSpecificUser(@PathVariable String username 214 | , @RequestBody Todo todo) { 215 | logger.info("Create {} for {}", todo, username); 216 | } 217 | 218 | } 219 | 220 | record Todo (String username, String description) {} 221 | ``` 222 | --- 223 | 224 | ### /src/main/resources/application.properties 225 | 226 | ```properties 227 | spring.main.banner-mode=off 228 | logging.pattern.console= %d{MM-dd HH:mm:ss} - %logger{36} - %msg%n 229 | 230 | #spring.security.user.name=in28minutes 231 | #spring.security.user.password=dummy 232 | 233 | spring.datasource.url=jdbc:h2:mem:testdb 234 | ``` 235 | --- 236 | 237 | ### /src/test/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplicationTests.java 238 | 239 | ```java 240 | package com.in28minutes.learnspringsecurity; 241 | 242 | import org.junit.jupiter.api.Test; 243 | import org.springframework.boot.test.context.SpringBootTest; 244 | 245 | @SpringBootTest 246 | class LearnSpringSecurityApplicationTests { 247 | 248 | @Test 249 | void contextLoads() { 250 | } 251 | 252 | } 253 | ``` 254 | --- 255 | -------------------------------------------------------------------------------- /71-spring-security/Step15.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step15.zip -------------------------------------------------------------------------------- /71-spring-security/Step18.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step18.zip -------------------------------------------------------------------------------- /71-spring-security/Step19.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step19.zip -------------------------------------------------------------------------------- /71-spring-security/Step20.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step20.zip -------------------------------------------------------------------------------- /71-spring-security/Step22.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/Step22.zip -------------------------------------------------------------------------------- /71-spring-security/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.springframework.boot' version '3.2.2' 4 | id 'io.spring.dependency-management' version '1.1.4' 5 | } 6 | 7 | group = 'com.in28minutes' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '21' 10 | 11 | repositories { 12 | mavenCentral() 13 | maven { url 'https://repo.spring.io/milestone' } 14 | } 15 | 16 | dependencies { 17 | implementation 'org.springframework.boot:spring-boot-starter-security' 18 | implementation 'org.springframework.boot:spring-boot-starter-web' 19 | implementation 'org.springframework.boot:spring-boot-devtools' 20 | implementation 'org.springframework.boot:spring-boot-starter-jdbc' 21 | implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' 22 | implementation 'com.h2database:h2' 23 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 24 | testImplementation 'org.springframework.security:spring-security-test' 25 | } 26 | 27 | tasks.named('test') { 28 | useJUnitPlatform() 29 | } 30 | -------------------------------------------------------------------------------- /71-spring-security/gradle/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/gradle/.DS_Store -------------------------------------------------------------------------------- /71-spring-security/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /71-spring-security/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /71-spring-security/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 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /71-spring-security/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | gradlePluginPortal() 5 | } 6 | } 7 | rootProject.name = 'learn-spring-security' 8 | -------------------------------------------------------------------------------- /71-spring-security/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/src/.DS_Store -------------------------------------------------------------------------------- /71-spring-security/src/main/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnspringsecurity; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class LearnSpringSecurityApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(LearnSpringSecurityApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /71-spring-security/src/main/java/com/in28minutes/learnspringsecurity/basic/BasicAuthSecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnspringsecurity.basic; 2 | 3 | import javax.sql.DataSource; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 8 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 9 | import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.http.SessionCreationPolicy; 12 | import org.springframework.security.core.userdetails.User; 13 | import org.springframework.security.core.userdetails.UserDetailsService; 14 | import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl; 15 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 16 | import org.springframework.security.provisioning.JdbcUserDetailsManager; 17 | import org.springframework.security.web.SecurityFilterChain; 18 | 19 | import static org.springframework.security.config.Customizer.withDefaults; 20 | 21 | 22 | @Configuration 23 | @EnableMethodSecurity(jsr250Enabled = true, securedEnabled = true) 24 | public class BasicAuthSecurityConfiguration { 25 | 26 | @Bean 27 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 28 | 29 | http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated()); 30 | 31 | http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); 32 | 33 | //http.formLogin(); 34 | http.httpBasic(withDefaults()); 35 | 36 | http.csrf(csrf -> csrf.disable()); 37 | 38 | http.headers(headers -> headers.frameOptions(frameOptionsConfig -> frameOptionsConfig.disable())); 39 | 40 | return http.build(); 41 | } 42 | 43 | // @Bean 44 | // public UserDetailsService userDetailService() { 45 | // 46 | // var user = User.withUsername("in28minutes") 47 | // .password("{noop}dummy") 48 | // .roles("USER") 49 | // .build(); 50 | // 51 | // 52 | // var admin = User.withUsername("admin") 53 | // .password("{noop}dummy") 54 | // .roles("ADMIN") 55 | // .build(); 56 | // 57 | // return new InMemoryUserDetailsManager(user, admin); 58 | // } 59 | 60 | @Bean 61 | public DataSource dataSource() { 62 | return new EmbeddedDatabaseBuilder() 63 | .setType(EmbeddedDatabaseType.H2) 64 | .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION) 65 | .build(); 66 | } 67 | 68 | @Bean 69 | public UserDetailsService userDetailService(DataSource dataSource) { 70 | 71 | var user = User.withUsername("in28minutes") 72 | //.password("{noop}dummy") 73 | .password("dummy") 74 | .passwordEncoder(str -> passwordEncoder().encode(str)) 75 | .roles("USER") 76 | .build(); 77 | 78 | var admin = User.withUsername("admin") 79 | //.password("{noop}dummy") 80 | .password("dummy") 81 | .passwordEncoder(str -> passwordEncoder().encode(str)) 82 | .roles("ADMIN", "USER") 83 | .build(); 84 | 85 | var jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource); 86 | jdbcUserDetailsManager.createUser(user); 87 | jdbcUserDetailsManager.createUser(admin); 88 | 89 | return jdbcUserDetailsManager; 90 | } 91 | 92 | @Bean 93 | public BCryptPasswordEncoder passwordEncoder() { 94 | return new BCryptPasswordEncoder(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /71-spring-security/src/main/java/com/in28minutes/learnspringsecurity/jwt/JwtAuthenticationResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnspringsecurity.jwt; 2 | 3 | import java.time.Instant; 4 | import java.util.stream.Collectors; 5 | 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.oauth2.jwt.JwtClaimsSet; 8 | import org.springframework.security.oauth2.jwt.JwtEncoder; 9 | import org.springframework.security.oauth2.jwt.JwtEncoderParameters; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | 12 | //@RestController 13 | public class JwtAuthenticationResource { 14 | 15 | private JwtEncoder jwtEncoder; 16 | 17 | public JwtAuthenticationResource(JwtEncoder jwtEncoder) { 18 | this.jwtEncoder = jwtEncoder; 19 | } 20 | 21 | @PostMapping("/authenticate") 22 | public JwtResponse authenticate(Authentication authentication) { 23 | return new JwtResponse(createToken(authentication)); 24 | } 25 | 26 | private String createToken(Authentication authentication) { 27 | var claims = JwtClaimsSet.builder() 28 | .issuer("self") 29 | .issuedAt(Instant.now()) 30 | .expiresAt(Instant.now().plusSeconds(60L * 30L)) 31 | .subject(authentication.getName()) 32 | .claim("scope", createScope(authentication)) 33 | .build(); 34 | 35 | return jwtEncoder.encode(JwtEncoderParameters.from(claims)) 36 | .getTokenValue(); 37 | } 38 | 39 | private String createScope(Authentication authentication) { 40 | return authentication.getAuthorities().stream() 41 | .map(a -> a.getAuthority()) 42 | .collect(Collectors.joining(" ")); 43 | } 44 | 45 | } 46 | 47 | record JwtResponse(String token) {} 48 | -------------------------------------------------------------------------------- /71-spring-security/src/main/java/com/in28minutes/learnspringsecurity/jwt/JwtSecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnspringsecurity.jwt; 2 | 3 | import java.security.KeyPair; 4 | import java.security.KeyPairGenerator; 5 | import java.security.interfaces.RSAPublicKey; 6 | import java.util.UUID; 7 | 8 | import javax.sql.DataSource; 9 | 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 12 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 13 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 14 | import org.springframework.security.config.http.SessionCreationPolicy; 15 | import org.springframework.security.core.userdetails.User; 16 | import org.springframework.security.core.userdetails.UserDetailsService; 17 | import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl; 18 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 19 | import org.springframework.security.oauth2.jwt.JwtDecoder; 20 | import org.springframework.security.oauth2.jwt.JwtEncoder; 21 | import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; 22 | import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; 23 | import org.springframework.security.provisioning.JdbcUserDetailsManager; 24 | import org.springframework.security.web.SecurityFilterChain; 25 | 26 | import com.nimbusds.jose.JOSEException; 27 | import com.nimbusds.jose.jwk.JWKSet; 28 | import com.nimbusds.jose.jwk.RSAKey; 29 | import com.nimbusds.jose.jwk.source.JWKSource; 30 | import com.nimbusds.jose.proc.SecurityContext; 31 | 32 | import static org.springframework.security.config.Customizer.withDefaults; 33 | 34 | //@Configuration 35 | public class JwtSecurityConfiguration { 36 | 37 | @Bean 38 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 39 | 40 | http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated()); 41 | 42 | http.sessionManagement(session ->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); 43 | 44 | http.httpBasic(withDefaults()); 45 | 46 | http.csrf(csrf -> csrf.disable()); 47 | 48 | http.headers(headersConfigurer -> headersConfigurer.frameOptions(frameOptionsConfig -> frameOptionsConfig.sameOrigin())); 49 | 50 | http.oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults())); 51 | 52 | return http.build(); 53 | } 54 | 55 | @Bean 56 | public DataSource dataSource() { 57 | return new EmbeddedDatabaseBuilder() 58 | .setType(EmbeddedDatabaseType.H2) 59 | .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION) 60 | .build(); 61 | } 62 | 63 | @Bean 64 | public UserDetailsService userDetailService(DataSource dataSource) { 65 | 66 | var user = User.withUsername("in28minutes") 67 | //.password("{noop}dummy") 68 | .password("dummy") 69 | .passwordEncoder(str -> passwordEncoder().encode(str)) 70 | .roles("USER") 71 | .build(); 72 | 73 | var admin = User.withUsername("admin") 74 | //.password("{noop}dummy") 75 | .password("dummy") 76 | .passwordEncoder(str -> passwordEncoder().encode(str)) 77 | .roles("ADMIN", "USER") 78 | .build(); 79 | 80 | var jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource); 81 | jdbcUserDetailsManager.createUser(user); 82 | jdbcUserDetailsManager.createUser(admin); 83 | 84 | return jdbcUserDetailsManager; 85 | } 86 | 87 | @Bean 88 | public BCryptPasswordEncoder passwordEncoder() { 89 | return new BCryptPasswordEncoder(); 90 | } 91 | 92 | @Bean 93 | public KeyPair keyPair() { 94 | try { 95 | var keyPairGenerator = KeyPairGenerator.getInstance("RSA"); 96 | keyPairGenerator.initialize(2048); 97 | return keyPairGenerator.generateKeyPair(); 98 | } catch (Exception ex) { 99 | throw new RuntimeException(ex); 100 | } 101 | } 102 | 103 | @Bean 104 | public RSAKey rsaKey(KeyPair keyPair) { 105 | 106 | return new RSAKey 107 | .Builder((RSAPublicKey)keyPair.getPublic()) 108 | .privateKey(keyPair.getPrivate()) 109 | .keyID(UUID.randomUUID().toString()) 110 | .build(); 111 | } 112 | 113 | @Bean 114 | public JWKSource jwkSource(RSAKey rsaKey) { 115 | var jwkSet = new JWKSet(rsaKey); 116 | 117 | return (jwkSelector, context) -> jwkSelector.select(jwkSet); 118 | 119 | } 120 | 121 | @Bean 122 | public JwtDecoder jwtDecoder(RSAKey rsaKey) throws JOSEException { 123 | return NimbusJwtDecoder 124 | .withPublicKey(rsaKey.toRSAPublicKey()) 125 | .build(); 126 | 127 | } 128 | 129 | @Bean 130 | public JwtEncoder jwtEncoder(JWKSource jwkSource) { 131 | return new NimbusJwtEncoder(jwkSource); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /71-spring-security/src/main/java/com/in28minutes/learnspringsecurity/resources/HelloWorldResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnspringsecurity.resources; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class HelloWorldResource { 8 | 9 | @GetMapping("/hello-world") 10 | public String helloWorld() { 11 | return "Hello World v1"; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /71-spring-security/src/main/java/com/in28minutes/learnspringsecurity/resources/SpringSecurityPlayResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnspringsecurity.resources; 2 | 3 | import org.springframework.security.web.csrf.CsrfToken; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import jakarta.servlet.http.HttpServletRequest; 8 | 9 | @RestController 10 | public class SpringSecurityPlayResource { 11 | 12 | @GetMapping("/csrf-token") 13 | public CsrfToken retrieveCsrfToken(HttpServletRequest request) { 14 | return (CsrfToken) request.getAttribute("_csrf"); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /71-spring-security/src/main/java/com/in28minutes/learnspringsecurity/resources/TodoResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnspringsecurity.resources; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.security.access.annotation.Secured; 8 | import org.springframework.security.access.prepost.PostAuthorize; 9 | import org.springframework.security.access.prepost.PreAuthorize; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RequestBody; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import jakarta.annotation.security.RolesAllowed; 17 | 18 | @RestController 19 | public class TodoResource { 20 | 21 | private final Logger logger = LoggerFactory.getLogger(getClass()); 22 | 23 | private static final List TODOS_LIST = List.of(new Todo("in28minutes", "Learn AWS"), 24 | new Todo("in28minutes", "Get AWS Certified")); 25 | 26 | @GetMapping("/todos") 27 | public List retrieveAllTodos() { 28 | return TODOS_LIST; 29 | } 30 | 31 | @GetMapping("/users/{username}/todos") 32 | @PreAuthorize("hasRole('USER') and #username == authentication.name") 33 | @PostAuthorize("returnObject.username == 'in28minutes'") 34 | @RolesAllowed({"ADMIN", "USER"}) 35 | @Secured({"ROLE_ADMIN", "ROLE_USER"}) 36 | public Todo retrieveTodosForSpecificUser(@PathVariable String username) { 37 | return TODOS_LIST.getFirst(); 38 | } 39 | 40 | @PostMapping("/users/{username}/todos") 41 | public void createTodoForSpecificUser(@PathVariable String username, @RequestBody Todo todo) { 42 | logger.info("Create {} for {}", todo, username); 43 | } 44 | 45 | } 46 | 47 | record Todo (String username, String description) {} -------------------------------------------------------------------------------- /71-spring-security/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.main.banner-mode=off 2 | logging.pattern.console= %d{MM-dd HH:mm:ss} - %logger{36} - %msg%n 3 | 4 | #spring.security.user.name=in28minutes 5 | #spring.security.user.password=dummy 6 | 7 | spring.datasource.url=jdbc:h2:mem:testdb -------------------------------------------------------------------------------- /71-spring-security/src/test/java/com/in28minutes/learnspringsecurity/LearnSpringSecurityApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnspringsecurity; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class LearnSpringSecurityApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /71-spring-security/step-by-step-changes/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/71-spring-security/step-by-step-changes/.DS_Store -------------------------------------------------------------------------------- /72-oauth/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/72-oauth/.DS_Store -------------------------------------------------------------------------------- /72-oauth/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /72-oauth/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.springframework.boot' version '3.2.2' 4 | id 'io.spring.dependency-management' version '1.1.4' 5 | } 6 | 7 | group = 'com.in28minutes' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '21' 10 | 11 | repositories { 12 | mavenCentral() 13 | maven { url 'https://repo.spring.io/milestone' } 14 | } 15 | 16 | dependencies { 17 | implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' 18 | implementation 'org.springframework.boot:spring-boot-starter-web' 19 | developmentOnly 'org.springframework.boot:spring-boot-devtools' 20 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 21 | } 22 | 23 | tasks.named('test') { 24 | useJUnitPlatform() 25 | } 26 | -------------------------------------------------------------------------------- /72-oauth/final.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Complete Code Example 6 | 7 | 8 | ### /src/main/java/com/in28minutes/learnoauth/HelloWorldResource.java 9 | 10 | ```java 11 | package com.in28minutes.learnoauth; 12 | 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.web.bind.annotation.GetMapping; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | @RestController 18 | public class HelloWorldResource { 19 | 20 | @GetMapping("/") 21 | public String helloWorld(Authentication authentication) { 22 | System.out.println(authentication); 23 | System.out.println(authentication.getPrincipal()); 24 | return "Hello World"; 25 | } 26 | 27 | } 28 | ``` 29 | --- 30 | 31 | ### /src/main/java/com/in28minutes/learnoauth/LearnOauthApplication.java 32 | 33 | ```java 34 | package com.in28minutes.learnoauth; 35 | 36 | import org.springframework.boot.SpringApplication; 37 | import org.springframework.boot.autoconfigure.SpringBootApplication; 38 | 39 | @SpringBootApplication 40 | public class LearnOauthApplication { 41 | 42 | public static void main(String[] args) { 43 | SpringApplication.run(LearnOauthApplication.class, args); 44 | } 45 | 46 | } 47 | ``` 48 | --- 49 | 50 | ### /src/main/java/com/in28minutes/learnoauth/OauthSecurityConfiguration.java 51 | 52 | ```java 53 | package com.in28minutes.learnoauth; 54 | 55 | import org.springframework.boot.autoconfigure.security.SecurityProperties; 56 | import org.springframework.context.annotation.Bean; 57 | import org.springframework.context.annotation.Configuration; 58 | import org.springframework.core.annotation.Order; 59 | import org.springframework.security.config.Customizer; 60 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 61 | import org.springframework.security.web.SecurityFilterChain; 62 | 63 | @Configuration 64 | public class OauthSecurityConfiguration { 65 | 66 | @Bean 67 | @Order(SecurityProperties.BASIC_AUTH_ORDER) 68 | SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { 69 | http.authorizeHttpRequests(httpRequest -> httpRequest.anyRequest().authenticated()); 70 | //http.formLogin(); 71 | //http.httpBasic(); 72 | http.oauth2Login(Customizer.withDefaults()); 73 | return http.build(); 74 | } 75 | 76 | } 77 | ``` 78 | --- 79 | 80 | ### /src/main/resources/application.properties 81 | 82 | ```properties 83 | ##Google API console 84 | ##http://localhost:8080/login/oauth2/code/google 85 | 86 | spring.security.oauth2.client.registration.google.client-id=YOUR_CLIENT_ID 87 | spring.security.oauth2.client.registration.google.client-secret=YOUR_SECRET 88 | 89 | ``` 90 | --- 91 | 92 | ### /src/test/java/com/in28minutes/learnoauth/LearnOauthApplicationTests.java 93 | 94 | ```java 95 | package com.in28minutes.learnoauth; 96 | 97 | import org.junit.jupiter.api.Test; 98 | import org.springframework.boot.test.context.SpringBootTest; 99 | 100 | @SpringBootTest 101 | class LearnOauthApplicationTests { 102 | 103 | @Test 104 | void contextLoads() { 105 | } 106 | 107 | } 108 | ``` 109 | --- 110 | -------------------------------------------------------------------------------- /72-oauth/final.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/72-oauth/final.zip -------------------------------------------------------------------------------- /72-oauth/gradle/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/72-oauth/gradle/.DS_Store -------------------------------------------------------------------------------- /72-oauth/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/72-oauth/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /72-oauth/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /72-oauth/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% equ 0 goto execute 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 execute 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 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /72-oauth/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | gradlePluginPortal() 5 | } 6 | } 7 | rootProject.name = 'learn-oauth' 8 | -------------------------------------------------------------------------------- /72-oauth/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/72-oauth/src/.DS_Store -------------------------------------------------------------------------------- /72-oauth/src/main/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/72-oauth/src/main/.DS_Store -------------------------------------------------------------------------------- /72-oauth/src/main/java/com/in28minutes/learnoauth/HelloWorldResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnoauth; 2 | 3 | import org.springframework.security.core.Authentication; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class HelloWorldResource { 9 | 10 | @GetMapping("/") 11 | public String helloWorld(Authentication authentication) { 12 | System.out.println(authentication); 13 | System.out.println(authentication.getPrincipal()); 14 | return "Hello World"; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /72-oauth/src/main/java/com/in28minutes/learnoauth/LearnOauthApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnoauth; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class LearnOauthApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(LearnOauthApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /72-oauth/src/main/java/com/in28minutes/learnoauth/OauthSecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnoauth; 2 | 3 | import org.springframework.boot.autoconfigure.security.SecurityProperties; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.core.annotation.Order; 7 | import org.springframework.security.config.Customizer; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.web.SecurityFilterChain; 10 | 11 | @Configuration 12 | public class OauthSecurityConfiguration { 13 | 14 | @Bean 15 | @Order(SecurityProperties.BASIC_AUTH_ORDER) 16 | SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { 17 | http.authorizeHttpRequests(httpRequest -> httpRequest.anyRequest().authenticated()); 18 | //http.formLogin(); 19 | //http.httpBasic(); 20 | http.oauth2Login(Customizer.withDefaults()); 21 | return http.build(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /72-oauth/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.security.oauth2.client.registration.google.client-id=YOUR_CLIENT_ID 2 | spring.security.oauth2.client.registration.google.client-secret=YOUR_SECRET -------------------------------------------------------------------------------- /72-oauth/src/test/java/com/in28minutes/learnoauth/LearnOauthApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.learnoauth; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class LearnOauthApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /99-reuse/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/99-reuse/.DS_Store -------------------------------------------------------------------------------- /99-reuse/restful-web-services-v2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/99-reuse/restful-web-services-v2.zip -------------------------------------------------------------------------------- /Backup-01-Generated-From-Angular-Cli.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-01-Generated-From-Angular-Cli.zip -------------------------------------------------------------------------------- /Backup-02-After-Creating-Welcome-Component.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-02-After-Creating-Welcome-Component.zip -------------------------------------------------------------------------------- /Backup-03-Basic-Hardcoded-Validation-Introduced-ngIf.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-03-Basic-Hardcoded-Validation-Introduced-ngIf.zip -------------------------------------------------------------------------------- /Backup-04-Basic-Todo-List-Component.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-04-Basic-Todo-List-Component.zip -------------------------------------------------------------------------------- /Backup-05-FIRST-BREAK-AND-REVIEW.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-05-FIRST-BREAK-AND-REVIEW.zip -------------------------------------------------------------------------------- /Backup-06-FORMATTING-COMPLETE.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-06-FORMATTING-COMPLETE.zip -------------------------------------------------------------------------------- /Backup-07-ANGULAR-FIRST-COMPLETE-VERSION.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-07-ANGULAR-FIRST-COMPLETE-VERSION.zip -------------------------------------------------------------------------------- /Backup-08-CREATED-HELLO-WORLD-REST-SERVICES.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-08-CREATED-HELLO-WORLD-REST-SERVICES.zip -------------------------------------------------------------------------------- /Backup-09-Hello-World-Message-Call-Basic-Implementation-In-Angular.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-09-Hello-World-Message-Call-Basic-Implementation-In-Angular.zip -------------------------------------------------------------------------------- /Backup-10-Hello-World-Message-Call-With-Basic-Error-Handling.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-10-Hello-World-Message-Call-With-Basic-Error-Handling.zip -------------------------------------------------------------------------------- /Backup-11-Hello-World-With-Parameter.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-11-Hello-World-With-Parameter.zip -------------------------------------------------------------------------------- /Backup-12-Update-Todo-Display.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-12-Update-Todo-Display.zip -------------------------------------------------------------------------------- /Backup-13-Update-Todo-Display-Target-Data-2-Way-Binding.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-13-Update-Todo-Display-Target-Data-2-Way-Binding.zip -------------------------------------------------------------------------------- /Backup-14-Create-Todo-Functionality-Done.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-14-Create-Todo-Functionality-Done.zip -------------------------------------------------------------------------------- /Backup-15-Validation-Done-Section-Complete.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-15-Validation-Done-Section-Complete.zip -------------------------------------------------------------------------------- /Backup-16-Front-end-using-http-basic-for-executing-http-requests.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-16-Front-end-using-http-basic-for-executing-http-requests.zip -------------------------------------------------------------------------------- /Backup-17-Login-Working-After-disabling-http-interceptor.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-17-Login-Working-After-disabling-http-interceptor.zip -------------------------------------------------------------------------------- /Backup-18-Working-App-With-Basic-Authentication-After-Enabling-HttpInterceptor.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-18-Working-App-With-Basic-Authentication-After-Enabling-HttpInterceptor.zip -------------------------------------------------------------------------------- /Backup-19-Refactoring-To-Constants.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-19-Refactoring-To-Constants.zip -------------------------------------------------------------------------------- /Backup-20-Working-With-JWT-Authentication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-20-Working-With-JWT-Authentication.zip -------------------------------------------------------------------------------- /Backup-21-JWT-Auth-Connected-To-Jpa.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/Backup-21-JWT-Auth-Connected-To-Jpa.zip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /MigrateToAngularStandalone.md: -------------------------------------------------------------------------------- 1 | # Migration Steps for an existing Angular project to standalone 2 | 3 | ### Prerequisites 4 | When you execute `ng version`, 5 | 6 | Angular CLI: 17.1.1 7 | 8 | Node: 18.19.0 9 | 10 | Package Manager: npm 10.4.0 11 | 12 | OS: darwin x64 13 | 14 | Angular: undefined 15 | ... 16 | 17 | Package Version 18 | ------------------------------------------------------ 19 | @angular-devkit/architect 0.1701.1 (cli-only) 20 | 21 | @angular-devkit/core 17.1.1 (cli-only) 22 | 23 | @angular-devkit/schematics 17.1.1 (cli-only) 24 | 25 | @schematics/angular 17.1.1 (cli-only) 26 | 27 | ### Step 1: Update all the dependencies and dev dependencies in `package.json` 28 | 1. Angular dependencies to **17.1.1** 29 | 2. rxjs to **7.8.1** 30 | 3. tslib to **2.6.2** 31 | 4. zone.js to **0.14.3** 32 | 5. Typescript to **5.3.3** 33 | 34 | ### Step 2: Remove package-lock.json 35 | ### Step 3: Execute `npm install` command for generating the new dependencies 36 | 37 | ### Step 4: Migrate to Standalone using this command `ng g @angular/core:standalone` 38 | 39 | > puneethsai@Puneeths-MacBook-Pro todo % ng g @angular/core:standalone 40 | 41 | - ? Choose the type of migration: (Use arrow keys) 42 | - **❯ Convert all components, directives and pipes to standalone** 43 | - Remove unnecessary NgModule classes 44 | - Bootstrap the application using standalone APIs 45 | 46 | If you choose option as `Convert all components, directives and pipes to standalone` 47 | 48 | ? Choose the type of migration: Convert all components, directives and pipes to standalone 49 | ? Which path in your project should be migrated? ./ 50 | 51 | 🎉 Automated migration step has finished! 🎉 52 | IMPORTANT! Please verify manually that your application builds and behaves as expected. 53 | See https://angular.io/guide/standalone-migration for more information. 54 | - UPDATE src/app/welcome/welcome.component.ts (2471 bytes) 55 | - UPDATE src/app/login/login.component.ts (2319 bytes) 56 | - UPDATE src/app/error/error.component.ts (360 bytes) 57 | - UPDATE src/app/list-todos/list-todos.component.ts (1880 bytes) 58 | - UPDATE src/app/menu/menu.component.ts (697 bytes) 59 | - UPDATE src/app/footer/footer.component.ts (297 bytes) 60 | - UPDATE src/app/logout/logout.component.ts (521 bytes) 61 | - UPDATE src/app/todo/todo.component.ts (1539 bytes) 62 | - UPDATE src/app/app.module.ts (1456 bytes) 63 | - UPDATE src/app/app.component.spec.ts (1063 bytes) 64 | - UPDATE src/app/error/error.component.spec.ts (608 bytes) 65 | - UPDATE src/app/footer/footer.component.spec.ts (615 bytes) 66 | - UPDATE src/app/list-todos/list-todos.component.spec.ts (637 bytes) 67 | - UPDATE src/app/login/login.component.spec.ts (608 bytes) 68 | - UPDATE src/app/logout/logout.component.spec.ts (615 bytes) 69 | - UPDATE src/app/menu/menu.component.spec.ts (601 bytes) 70 | - UPDATE src/app/todo/todo.component.spec.ts (601 bytes) 71 | - UPDATE src/app/welcome/welcome.component.spec.ts (622 bytes) 72 | 73 | ### Step5: Again execute `ng g @angular/core:standalone` and choose 2nd option as `Remove unnecessary NgModule classes` 74 | > puneethsai@Puneeths-MacBook-Pro todo % ng g @angular/core:standalone 75 | - ? Choose the type of migration: 76 | - Convert all components, directives and pipes to standalone 77 | - **❯ Remove unnecessary NgModule classes** 78 | - Bootstrap the application using standalone APIs 79 | 80 | ? Choose the type of migration: Remove unnecessary NgModule classes 81 | ? Which path in your project should be migrated? ./ 82 | 🎉 Automated migration step has finished! 🎉 83 | IMPORTANT! Please verify manually that your application builds and behaves as expected. 84 | See https://angular.io/guide/standalone-migration for more information. 85 | Nothing to be done. 86 | 87 | ### Step6: Again execute `ng g @angular/core:standalone` Choose 3rd option as `Bootstrap the application using standalone APIs` 88 | 89 | > puneethsai@Puneeths-MacBook-Pro todo % ng g @angular/core:standalone 90 | 91 | - ? Choose the type of migration: Bootstrap the application using standalone APIs 92 | - ? Which path in your project should be migrated? ./ 93 | 94 | - 🎉 Automated migration step has finished! 95 | - 🎉 IMPORTANT! Please verify manually that your application builds and behaves as expected. 96 | - See https://angular.io/guide/standalone-migration for more information. 97 | - DELETE src/app/app.module.ts 98 | - UPDATE src/main.ts (1029 bytes) 99 | - UPDATE src/app/app.component.ts (535 bytes) 100 | - UPDATE src/app/app.component.spec.ts (1051 bytes) 101 | 102 | For more details, please go through the following links 103 | 1. https://angular.io/guide/standalone-migration 104 | 2. https://angular.io/guide/standalone-components 105 | 106 | -------------------------------------------------------------------------------- /frontend/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/.DS_Store -------------------------------------------------------------------------------- /frontend/todo-2023-11-angular15.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo-2023-11-angular15.zip -------------------------------------------------------------------------------- /frontend/todo/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /frontend/todo/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | .angular 11 | *.DS_Store 12 | 13 | # IDEs and editors 14 | /.idea 15 | .project 16 | .classpath 17 | .c9/ 18 | *.launch 19 | .settings/ 20 | *.sublime-workspace 21 | 22 | # IDE - VSCode 23 | .vscode/* 24 | !.vscode/settings.json 25 | !.vscode/tasks.json 26 | !.vscode/launch.json 27 | !.vscode/extensions.json 28 | 29 | # misc 30 | /.sass-cache 31 | /connect.lock 32 | /coverage 33 | /libpeerconnection.log 34 | npm-debug.log 35 | yarn-error.log 36 | testem.log 37 | /typings 38 | 39 | # System Files 40 | .DS_Store 41 | Thumbs.db 42 | -------------------------------------------------------------------------------- /frontend/todo/README.md: -------------------------------------------------------------------------------- 1 | # Todo 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.1.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /frontend/todo/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "todo": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/todo", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": [ 20 | "zone.js" 21 | ], 22 | "tsConfig": "tsconfig.app.json", 23 | "assets": [ 24 | "src/favicon.ico", 25 | "src/assets" 26 | ], 27 | "styles": [ 28 | "src/styles.css" 29 | ], 30 | "scripts": [] 31 | }, 32 | "configurations": { 33 | "production": { 34 | "budgets": [ 35 | { 36 | "type": "initial", 37 | "maximumWarning": "500kb", 38 | "maximumError": "1mb" 39 | }, 40 | { 41 | "type": "anyComponentStyle", 42 | "maximumWarning": "2kb", 43 | "maximumError": "4kb" 44 | } 45 | ], 46 | "outputHashing": "all" 47 | }, 48 | "development": { 49 | "buildOptimizer": false, 50 | "optimization": false, 51 | "vendorChunk": true, 52 | "extractLicenses": false, 53 | "sourceMap": true, 54 | "namedChunks": true 55 | } 56 | }, 57 | "defaultConfiguration": "production" 58 | }, 59 | "serve": { 60 | "builder": "@angular-devkit/build-angular:dev-server", 61 | "configurations": { 62 | "production": { 63 | "buildTarget": "todo:build:production" 64 | }, 65 | "development": { 66 | "buildTarget": "todo:build:development" 67 | } 68 | }, 69 | "defaultConfiguration": "development" 70 | }, 71 | "extract-i18n": { 72 | "builder": "@angular-devkit/build-angular:extract-i18n", 73 | "options": { 74 | "buildTarget": "todo:build" 75 | } 76 | }, 77 | "test": { 78 | "builder": "@angular-devkit/build-angular:karma", 79 | "options": { 80 | "polyfills": [ 81 | "zone.js", 82 | "zone.js/testing" 83 | ], 84 | "tsConfig": "tsconfig.spec.json", 85 | "assets": [ 86 | "src/favicon.ico", 87 | "src/assets" 88 | ], 89 | "styles": [ 90 | "src/styles.css" 91 | ], 92 | "scripts": [] 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /frontend/todo/final.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/final.zip -------------------------------------------------------------------------------- /frontend/todo/notes-2023-update.md: -------------------------------------------------------------------------------- 1 | 2 | ## STEP 06 3 | 4 | - ng lint is deprecated 5 | - e2e folder is NOT created by default 6 | - ng e2e has changed -------------------------------------------------------------------------------- /frontend/todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "^19.0.1", 14 | "@angular/common": "^19.0.1", 15 | "@angular/compiler": "^19.0.1", 16 | "@angular/core": "^19.0.1", 17 | "@angular/forms": "^19.0.1", 18 | "@angular/platform-browser": "^19.0.1", 19 | "@angular/platform-browser-dynamic": "^19.0.1", 20 | "@angular/router": "^19.0.1", 21 | "rxjs": "~7.8.1", 22 | "tslib": "^2.6.2", 23 | "zone.js": "~0.15.0" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "^19.0.2", 27 | "@angular/cli": "~19.0.2", 28 | "@angular/compiler-cli": "^19.0.1", 29 | "@types/jasmine": "~4.3.0", 30 | "jasmine-core": "~4.5.0", 31 | "karma": "~6.4.0", 32 | "karma-chrome-launcher": "~3.1.0", 33 | "karma-coverage": "~2.2.0", 34 | "karma-jasmine": "~5.1.0", 35 | "karma-jasmine-html-reporter": "~2.0.0", 36 | "typescript": "~5.5.4" 37 | } 38 | } -------------------------------------------------------------------------------- /frontend/todo/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { TodoComponent } from './todo/todo.component'; 2 | import { RouteGuardService } from './service/route-guard.service'; 3 | import { LogoutComponent } from './logout/logout.component'; 4 | import { ListTodosComponent } from './list-todos/list-todos.component'; 5 | import { WelcomeComponent } from './welcome/welcome.component'; 6 | import { NgModule } from '@angular/core'; 7 | import { Routes, RouterModule } from '@angular/router'; 8 | import { LoginComponent } from './login/login.component'; 9 | import { ErrorComponent } from './error/error.component'; 10 | 11 | // welcome 12 | const routes: Routes = [ 13 | { path: '', component: LoginComponent },//canActivate, RouteGuardService 14 | { path: 'login', component: LoginComponent }, 15 | { path: 'welcome/:name', component: WelcomeComponent, canActivate:[RouteGuardService]}, 16 | { path: 'todos', component: ListTodosComponent, canActivate:[RouteGuardService] }, 17 | { path: 'logout', component: LogoutComponent, canActivate:[RouteGuardService] }, 18 | { path: 'todos/:id', component: TodoComponent, canActivate:[RouteGuardService] }, 19 | 20 | { path: '**', component: ErrorComponent } 21 | ]; 22 | 23 | @NgModule({ 24 | imports: [RouterModule.forRoot(routes)], 25 | exports: [RouterModule] 26 | }) 27 | export class AppRoutingModule { } 28 | -------------------------------------------------------------------------------- /frontend/todo/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/src/app/app.component.css -------------------------------------------------------------------------------- /frontend/todo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /frontend/todo/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule, 10 | AppComponent 11 | ], 12 | }).compileComponents(); 13 | })); 14 | 15 | it('should create the app', () => { 16 | const fixture = TestBed.createComponent(AppComponent); 17 | const app = fixture.debugElement.componentInstance; 18 | expect(app).toBeTruthy(); 19 | }); 20 | 21 | it(`should have as title 'todo'`, () => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | const app = fixture.debugElement.componentInstance; 24 | expect(app.title).toEqual('todo'); 25 | }); 26 | 27 | it('should render title in a h1 tag', () => { 28 | const fixture = TestBed.createComponent(AppComponent); 29 | fixture.detectChanges(); 30 | const compiled = fixture.debugElement.nativeElement; 31 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to todo!'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /frontend/todo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FooterComponent } from './footer/footer.component'; 3 | import { RouterOutlet } from '@angular/router'; 4 | import { MenuComponent } from './menu/menu.component'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | //template: '

{{title}}

', 10 | styleUrls: ['./app.component.css'], 11 | standalone: true, 12 | imports: [MenuComponent, RouterOutlet, FooterComponent] 13 | }) 14 | export class AppComponent { 15 | title = 'todo'; 16 | message = 'Welcome to in28Minutes'; 17 | } -------------------------------------------------------------------------------- /frontend/todo/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig } from '@angular/core'; 2 | import { AppRoutingModule } from './app-routing.module'; // Works for all the Angular Versions 3 | import { importProvidersFrom } from '@angular/core'; 4 | 5 | import { FormsModule } from '@angular/forms'; 6 | import { BrowserModule } from '@angular/platform-browser'; 7 | import { HTTP_INTERCEPTORS, withInterceptorsFromDi, provideHttpClient } from '@angular/common/http'; 8 | import { HttpIntercepterBasicAuthService } from './service/http/http-intercepter-basic-auth.service'; 9 | import { provideRouter } from '@angular/router'; // For Angular 17 onwards 10 | import { routes } from './app.routes'; // For Angular 17 onwards 11 | 12 | export const appConfig: ApplicationConfig = { 13 | // if you're using NgModule use AppRoutingModule use the AppRoutingModule 14 | providers: [importProvidersFrom(BrowserModule, AppRoutingModule, FormsModule), 15 | // For Angular 17 onwards you can use routes and provideRouter 16 | //providers: [importProvidersFrom(BrowserModule, FormsModule), provideRouter(routes), 17 | { provide: HTTP_INTERCEPTORS, useClass: HttpIntercepterBasicAuthService, multi: true }, 18 | provideHttpClient(withInterceptorsFromDi())] 19 | }; -------------------------------------------------------------------------------- /frontend/todo/src/app/app.constants.ts: -------------------------------------------------------------------------------- 1 | export const API_URL = "http://localhost:8080" 2 | export const TODO_JPA_API_URL = "http://localhost:8080/jpa" -------------------------------------------------------------------------------- /frontend/todo/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { TodoComponent } from './todo/todo.component'; 2 | import { RouteGuardService } from './service/route-guard.service'; 3 | import { LogoutComponent } from './logout/logout.component'; 4 | import { ListTodosComponent } from './list-todos/list-todos.component'; 5 | import { WelcomeComponent } from './welcome/welcome.component'; 6 | import { Routes } from '@angular/router'; 7 | import { LoginComponent } from './login/login.component'; 8 | import { ErrorComponent } from './error/error.component'; 9 | 10 | // welcome 11 | export const routes: Routes = [ 12 | { path: '', component: LoginComponent },//canActivate, RouteGuardService 13 | { path: 'login', component: LoginComponent }, 14 | { path: 'welcome/:name', component: WelcomeComponent, canActivate:[RouteGuardService]}, 15 | { path: 'todos', component: ListTodosComponent, canActivate:[RouteGuardService] }, 16 | { path: 'logout', component: LogoutComponent, canActivate:[RouteGuardService] }, 17 | { path: 'todos/:id', component: TodoComponent, canActivate:[RouteGuardService] }, 18 | 19 | { path: '**', component: ErrorComponent } 20 | ]; -------------------------------------------------------------------------------- /frontend/todo/src/app/error/error.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/src/app/error/error.component.css -------------------------------------------------------------------------------- /frontend/todo/src/app/error/error.component.html: -------------------------------------------------------------------------------- 1 | {{errorMessage}} -------------------------------------------------------------------------------- /frontend/todo/src/app/error/error.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ErrorComponent } from './error.component'; 4 | 5 | describe('ErrorComponent', () => { 6 | let component: ErrorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [ErrorComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ErrorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/todo/src/app/error/error.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-error', 5 | templateUrl: './error.component.html', 6 | styleUrls: ['./error.component.css'], 7 | standalone: true 8 | }) 9 | export class ErrorComponent implements OnInit { 10 | 11 | errorMessage = 'An Error Occured! Contact Support at *** - ***' 12 | 13 | constructor() { } 14 | 15 | ngOnInit() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /frontend/todo/src/app/footer/footer.component.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | position: absolute; 3 | bottom: 0; 4 | width:100%; 5 | height: 40px; 6 | background-color: #222222; 7 | } -------------------------------------------------------------------------------- /frontend/todo/src/app/footer/footer.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | All Rights Reserved 2024 @in28minutes 4 |
5 | 6 |
-------------------------------------------------------------------------------- /frontend/todo/src/app/footer/footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FooterComponent } from './footer.component'; 4 | 5 | describe('FooterComponent', () => { 6 | let component: FooterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [FooterComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FooterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/todo/src/app/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-footer', 5 | templateUrl: './footer.component.html', 6 | styleUrls: ['./footer.component.css'], 7 | standalone: true 8 | }) 9 | export class FooterComponent implements OnInit { 10 | 11 | constructor() { } 12 | 13 | ngOnInit() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /frontend/todo/src/app/list-todos/list-todos.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/src/app/list-todos/list-todos.component.css -------------------------------------------------------------------------------- /frontend/todo/src/app/list-todos/list-todos.component.html: -------------------------------------------------------------------------------- 1 |

Todo

2 | 3 |
{{message}}
4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
DescriptionTarget Dateis Completed?UpdateDelete
{{todo.description}}{{todo.targetDate | date | uppercase}}{{todo.done}}
29 | 30 |
31 | 32 |
33 | 34 |
-------------------------------------------------------------------------------- /frontend/todo/src/app/list-todos/list-todos.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ListTodosComponent } from './list-todos.component'; 4 | 5 | describe('ListTodosComponent', () => { 6 | let component: ListTodosComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [ListTodosComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ListTodosComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/todo/src/app/list-todos/list-todos.component.ts: -------------------------------------------------------------------------------- 1 | import { TodoDataService } from './../service/data/todo-data.service'; 2 | import { Component, OnInit } from '@angular/core'; 3 | import { Router } from '@angular/router'; 4 | import { NgIf, NgFor, UpperCasePipe, DatePipe } from '@angular/common'; 5 | 6 | export class Todo { 7 | constructor( 8 | public id: number, 9 | public description: string, 10 | public done: boolean, 11 | public targetDate: Date 12 | ) { 13 | 14 | } 15 | } 16 | 17 | @Component({ 18 | selector: 'app-list-todos', 19 | templateUrl: './list-todos.component.html', 20 | styleUrls: ['./list-todos.component.css'], 21 | standalone: true, 22 | imports: [NgIf, NgFor, UpperCasePipe, DatePipe] 23 | }) 24 | export class ListTodosComponent implements OnInit { 25 | 26 | todos: Todo[] = []; 27 | 28 | message: string = ''; 29 | 30 | // = [ 31 | // new Todo(1, 'Learn to Dance', false, new Date()), 32 | // new Todo(2, 'Become an Expert at Angular', false, new Date()), 33 | // new Todo(3, 'Visit India', false, new Date()) 34 | // // {id : 1, description : }, 35 | // // {id : 2, description : ''}, 36 | // // {id : 3, description : 'Visit India'} 37 | // ] 38 | 39 | // todo = { 40 | // id : 1, 41 | // description: 'Learn to Dance' 42 | // } 43 | 44 | constructor( 45 | private todoService: TodoDataService, 46 | private router: Router 47 | ) { } 48 | 49 | ngOnInit() { 50 | this.refreshTodos(); 51 | } 52 | 53 | refreshTodos() { 54 | this.todoService.retrieveAllTodos('in28minutes').subscribe( 55 | response => { 56 | console.log(response); 57 | this.todos = response; 58 | } 59 | ) 60 | } 61 | 62 | deleteTodo(id: number) { 63 | console.log(`delete todo ${id}`) 64 | this.todoService.deleteTodo('in28minutes', id).subscribe( 65 | response => { 66 | console.log(response); 67 | this.message = `Delete of Todo ${id} Successful!`; 68 | this.refreshTodos(); 69 | } 70 | ) 71 | } 72 | 73 | updateTodo(id: number) { 74 | console.log(`update ${id}`) 75 | this.router.navigate(['todos', id]) 76 | } 77 | 78 | addTodo() { 79 | this.router.navigate(['todos', -1]) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /frontend/todo/src/app/login/login.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/src/app/login/login.component.css -------------------------------------------------------------------------------- /frontend/todo/src/app/login/login.component.html: -------------------------------------------------------------------------------- 1 |

Login!

2 | 3 |
4 |
{{errorMessage}}
5 | 6 |
7 | User Name : 8 | Password : 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
-------------------------------------------------------------------------------- /frontend/todo/src/app/login/login.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoginComponent } from './login.component'; 4 | 5 | describe('LoginComponent', () => { 6 | let component: LoginComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [LoginComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LoginComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/todo/src/app/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { BasicAuthenticationService } from './../service/basic-authentication.service'; 2 | import { HardcodedAuthenticationService } from './../service/hardcoded-authentication.service'; 3 | import { Component, OnInit } from '@angular/core'; 4 | import { Router } from '@angular/router'; 5 | import { FormsModule } from '@angular/forms'; 6 | import { NgIf } from '@angular/common'; 7 | 8 | @Component({ 9 | selector: 'app-login', 10 | templateUrl: './login.component.html', 11 | styleUrls: ['./login.component.css'], 12 | standalone: true, 13 | imports: [NgIf, FormsModule] 14 | }) 15 | export class LoginComponent implements OnInit { 16 | 17 | username = 'in28minutes' 18 | password = '' 19 | errorMessage = 'Invalid Credentials' 20 | invalidLogin = false 21 | 22 | //Router 23 | //Angular.giveMeRouter 24 | //Dependency Injection 25 | constructor(private router: Router, 26 | private hardcodedAuthenticationService: HardcodedAuthenticationService, 27 | private basicAuthenticationService: BasicAuthenticationService) { } 28 | 29 | ngOnInit() { 30 | } 31 | 32 | handleLogin() { 33 | // console.log(this.username); 34 | //if(this.username==="in28minutes" && this.password === 'dummy') { 35 | if(this.hardcodedAuthenticationService.authenticate(this.username, this.password)) { 36 | //Redirect to Welcome Page 37 | this.router.navigate(['welcome', this.username]) 38 | this.invalidLogin = false 39 | } else { 40 | this.invalidLogin = true 41 | } 42 | } 43 | 44 | handleBasicAuthLogin() { 45 | // console.log(this.username); 46 | //if(this.username==="in28minutes" && this.password === 'dummy') { 47 | this.basicAuthenticationService.executeAuthenticationService(this.username, this.password) 48 | .subscribe( 49 | data => { 50 | console.log(data) 51 | this.router.navigate(['welcome', this.username]) 52 | this.invalidLogin = false 53 | }, 54 | error => { 55 | console.log(error) 56 | this.invalidLogin = true 57 | } 58 | ) 59 | } 60 | 61 | handleJWTAuthLogin() { 62 | this.basicAuthenticationService.executeJWTAuthenticationService(this.username, this.password) 63 | .subscribe( 64 | data => { 65 | console.log(data) 66 | this.router.navigate(['welcome', this.username]) 67 | this.invalidLogin = false 68 | }, 69 | error => { 70 | console.log(error) 71 | this.invalidLogin = true 72 | } 73 | ) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /frontend/todo/src/app/logout/logout.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/src/app/logout/logout.component.css -------------------------------------------------------------------------------- /frontend/todo/src/app/logout/logout.component.html: -------------------------------------------------------------------------------- 1 |

You are logged out

2 |
3 | Thank You For Using Our Application. 4 |
-------------------------------------------------------------------------------- /frontend/todo/src/app/logout/logout.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LogoutComponent } from './logout.component'; 4 | 5 | describe('LogoutComponent', () => { 6 | let component: LogoutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [LogoutComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LogoutComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/todo/src/app/logout/logout.component.ts: -------------------------------------------------------------------------------- 1 | import { HardcodedAuthenticationService } from './../service/hardcoded-authentication.service'; 2 | import { Component, OnInit } from '@angular/core'; 3 | 4 | @Component({ 5 | selector: 'app-logout', 6 | templateUrl: './logout.component.html', 7 | styleUrls: ['./logout.component.css'], 8 | standalone: true 9 | }) 10 | export class LogoutComponent implements OnInit { 11 | 12 | constructor( 13 | private hardcodedAuthenticationService: HardcodedAuthenticationService 14 | ) { } 15 | 16 | ngOnInit() { 17 | this.hardcodedAuthenticationService.logout(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /frontend/todo/src/app/menu/menu.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/src/app/menu/menu.component.css -------------------------------------------------------------------------------- /frontend/todo/src/app/menu/menu.component.html: -------------------------------------------------------------------------------- 1 |
2 | 16 |
-------------------------------------------------------------------------------- /frontend/todo/src/app/menu/menu.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { MenuComponent } from './menu.component'; 4 | 5 | describe('MenuComponent', () => { 6 | let component: MenuComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [MenuComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(MenuComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/todo/src/app/menu/menu.component.ts: -------------------------------------------------------------------------------- 1 | import { HardcodedAuthenticationService } from './../service/hardcoded-authentication.service'; 2 | import { Component, OnInit } from '@angular/core'; 3 | import { RouterLink } from '@angular/router'; 4 | import { NgIf } from '@angular/common'; 5 | 6 | @Component({ 7 | selector: 'app-menu', 8 | templateUrl: './menu.component.html', 9 | styleUrls: ['./menu.component.css'], 10 | standalone: true, 11 | imports: [NgIf, RouterLink] 12 | }) 13 | export class MenuComponent implements OnInit { 14 | //isUserLoggedIn: boolean = false; 15 | 16 | constructor(public hardcodedAuthenticationService 17 | : HardcodedAuthenticationService) { } 18 | 19 | ngOnInit() { 20 | //this.isUserLoggedIn = this.hardcodedAuthenticationService.isUserLoggedIn(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/basic-authentication.service.ts: -------------------------------------------------------------------------------- 1 | import { API_URL } from './../app.constants'; 2 | import { HttpHeaders, HttpClient } from '@angular/common/http'; 3 | import { Injectable } from '@angular/core'; 4 | import { map } from 'rxjs/operators'; 5 | 6 | export const TOKEN = 'token' 7 | export const AUTHENTICATED_USER = 'authenticaterUser' 8 | 9 | @Injectable({ 10 | providedIn: 'root' 11 | }) 12 | export class BasicAuthenticationService { 13 | 14 | constructor(private http: HttpClient) { } 15 | 16 | executeJWTAuthenticationService(username: string, password: string) { 17 | 18 | return this.http.post( 19 | `${API_URL}/authenticate`, { 20 | username, 21 | password 22 | }).pipe( 23 | map( 24 | data => { 25 | sessionStorage.setItem(AUTHENTICATED_USER, username); 26 | sessionStorage.setItem(TOKEN, `Bearer ${data.token}`); 27 | return data; 28 | } 29 | ) 30 | ); 31 | //console.log("Execute Hello World Bean Service") 32 | } 33 | 34 | 35 | executeAuthenticationService(username: string, password: string) { 36 | 37 | let basicAuthHeaderString = 'Basic ' + window.btoa(username + ':' + password); 38 | 39 | let headers = new HttpHeaders({ 40 | Authorization: basicAuthHeaderString 41 | }) 42 | 43 | return this.http.get( 44 | `${API_URL}/basicauth`, 45 | { headers }).pipe( 46 | map( 47 | data => { 48 | sessionStorage.setItem(AUTHENTICATED_USER, username); 49 | sessionStorage.setItem(TOKEN, basicAuthHeaderString); 50 | return data; 51 | } 52 | ) 53 | ); 54 | //console.log("Execute Hello World Bean Service") 55 | } 56 | 57 | getAuthenticatedUser() { 58 | return sessionStorage.getItem(AUTHENTICATED_USER) 59 | } 60 | 61 | getAuthenticatedToken() { 62 | if (this.getAuthenticatedUser()) 63 | return sessionStorage.getItem(TOKEN) 64 | return null 65 | } 66 | 67 | isUserLoggedIn() { 68 | let user = sessionStorage.getItem(AUTHENTICATED_USER) 69 | return !(user === null) 70 | } 71 | 72 | logout() { 73 | sessionStorage.removeItem(AUTHENTICATED_USER) 74 | sessionStorage.removeItem(TOKEN) 75 | } 76 | 77 | } 78 | 79 | export class AuthenticationBean { 80 | constructor(public message: string) { } 81 | } -------------------------------------------------------------------------------- /frontend/todo/src/app/service/data/todo-data.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { TodoDataService } from './todo-data.service'; 4 | 5 | describe('TodoDataService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: TodoDataService = TestBed.get(TodoDataService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/data/todo-data.service.ts: -------------------------------------------------------------------------------- 1 | import { TODO_JPA_API_URL } from './../../app.constants'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Injectable } from '@angular/core'; 4 | import { Todo } from '../../list-todos/list-todos.component'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class TodoDataService { 10 | 11 | constructor( 12 | private http: HttpClient 13 | ) { } 14 | 15 | retrieveAllTodos(username: string) { 16 | return this.http.get(`${TODO_JPA_API_URL}/users/${username}/todos`); 17 | //console.log("Execute Hello World Bean Service") 18 | } 19 | 20 | deleteTodo(username: string, id: number) { 21 | return this.http.delete(`${TODO_JPA_API_URL}/users/${username}/todos/${id}`); 22 | } 23 | 24 | retrieveTodo(username: string, id: number) { 25 | return this.http.get(`${TODO_JPA_API_URL}/users/${username}/todos/${id}`); 26 | } 27 | 28 | updateTodo(username: string, id: number, todo: Todo) { 29 | return this.http.put( 30 | `${TODO_JPA_API_URL}/users/${username}/todos/${id}` 31 | , todo); 32 | } 33 | 34 | createTodo(username: string, todo: Todo) { 35 | return this.http.post( 36 | `${TODO_JPA_API_URL}/users/${username}/todos` 37 | , todo); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/data/welcome-data.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { WelcomeDataService } from './welcome-data.service'; 4 | 5 | describe('WelcomeDataService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: WelcomeDataService = TestBed.get(WelcomeDataService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/data/welcome-data.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | 4 | export class HelloWorldBean { 5 | constructor(public message: string) { } 6 | } 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class WelcomeDataService { 12 | 13 | constructor( 14 | private http: HttpClient 15 | ) { } 16 | 17 | executeHelloWorldBeanService() { 18 | return this.http.get('http://localhost:8080/hello-world-bean'); 19 | //console.log("Execute Hello World Bean Service") 20 | } 21 | //http://localhost:8080/hello-world/path-variable/in28minutes 22 | 23 | executeHelloWorldServiceWithPathVariable(name: string) { 24 | // let basicAuthHeaderString = this.createBasicAuthenticationHttpHeader(); 25 | 26 | // let headers = new HttpHeaders({ 27 | // Authorization: basicAuthHeaderString 28 | // }) 29 | 30 | return this.http.get( 31 | `http://localhost:8080/hello-world/path-variable/${name}`, 32 | //{headers} 33 | ); 34 | //console.log("Execute Hello World Bean Service") 35 | } 36 | 37 | // createBasicAuthenticationHttpHeader() { 38 | // let username = 'in28minutes' 39 | // let password = 'dummy' 40 | // let basicAuthHeaderString = 'Basic ' + window.btoa(username + ':' + password); 41 | // return basicAuthHeaderString; 42 | // } 43 | //Access to XMLHttpRequest at 44 | //'http://localhost:8080/hello-world/path-variable/in28minutes' 45 | //from origin 'http://localhost:4200' has been blocked by CORS policy: 46 | //No 'Access-Control-Allow-Origin' header is present on the requested resource. 47 | 48 | //Access to XMLHttpRequest at 'http://localhost:8080/hello-world/path-variable/in28minutes' from origin 'http://localhost:4200' 49 | //has been blocked by CORS policy: 50 | //Response to preflight request doesn't pass 51 | //access control check: It does not have HTTP ok status 52 | } 53 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/hardcoded-authentication.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { HardcodedAuthenticationService } from './hardcoded-authentication.service'; 4 | 5 | describe('HardcodedAuthenticationService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: HardcodedAuthenticationService = TestBed.get(HardcodedAuthenticationService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/hardcoded-authentication.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class HardcodedAuthenticationService { 7 | 8 | constructor() { } 9 | 10 | authenticate(username: string, password: string) { 11 | //console.log('before ' + this.isUserLoggedIn()); 12 | if (username === "in28minutes" && password === 'dummy') { 13 | sessionStorage.setItem('authenticaterUser', username); 14 | //console.log('after ' + this.isUserLoggedIn()); 15 | return true; 16 | } 17 | return false; 18 | } 19 | 20 | isUserLoggedIn() { 21 | let user = sessionStorage.getItem('authenticaterUser') 22 | return !(user === null) 23 | } 24 | 25 | logout() { 26 | sessionStorage.removeItem('authenticaterUser') 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/http/http-intercepter-basic-auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { HttpIntercepterBasicAuthService } from './http-intercepter-basic-auth.service'; 4 | 5 | describe('HttpIntercepterBasicAuthService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: HttpIntercepterBasicAuthService = TestBed.get(HttpIntercepterBasicAuthService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/http/http-intercepter-basic-auth.service.ts: -------------------------------------------------------------------------------- 1 | import { BasicAuthenticationService } from './../basic-authentication.service'; 2 | import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; 3 | import { Injectable } from '@angular/core'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class HttpIntercepterBasicAuthService implements HttpInterceptor{ 9 | 10 | constructor( 11 | private basicAuthenticationService : BasicAuthenticationService 12 | ) { } 13 | 14 | intercept(request: HttpRequest, next: HttpHandler){ 15 | // let username = 'in28minutes' 16 | // let password = 'dummy' 17 | //let basicAuthHeaderString = 'Basic ' + window.btoa(username + ':' + password); 18 | let basicAuthHeaderString = this.basicAuthenticationService.getAuthenticatedToken(); 19 | let username = this.basicAuthenticationService.getAuthenticatedUser() 20 | 21 | if(basicAuthHeaderString && username) { 22 | request = request.clone({ 23 | setHeaders : { 24 | Authorization : basicAuthHeaderString 25 | } 26 | }) 27 | } 28 | return next.handle(request); 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/route-guard.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { RouteGuardService } from './route-guard.service'; 4 | 5 | describe('RouteGuardService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: RouteGuardService = TestBed.get(RouteGuardService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/todo/src/app/service/route-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { HardcodedAuthenticationService } from './hardcoded-authentication.service'; 2 | import { Injectable } from '@angular/core'; 3 | import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class RouteGuardService implements CanActivate { 9 | 10 | constructor( 11 | private hardcodedAuthenticationService: HardcodedAuthenticationService, 12 | private router: Router) { 13 | 14 | } 15 | 16 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { 17 | 18 | if (this.hardcodedAuthenticationService.isUserLoggedIn()) 19 | return true; 20 | 21 | this.router.navigate(['login']); 22 | 23 | return false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /frontend/todo/src/app/todo/todo.component.css: -------------------------------------------------------------------------------- 1 | .ng-invalid:not(form) { 2 | border-left: 5px solid red; 3 | } -------------------------------------------------------------------------------- /frontend/todo/src/app/todo/todo.component.html: -------------------------------------------------------------------------------- 1 |

Todo

2 | 3 |
4 |
Enter valid values
5 |
Enter valid Target Date
6 |
Enter atleast 5 characters in Description
7 | 8 |
9 |
10 | 11 | 14 |
15 | 16 |
17 | 18 | 22 |
23 | 24 | 25 |
26 |
-------------------------------------------------------------------------------- /frontend/todo/src/app/todo/todo.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TodoComponent } from './todo.component'; 4 | 5 | describe('TodoComponent', () => { 6 | let component: TodoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [TodoComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TodoComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/todo/src/app/todo/todo.component.ts: -------------------------------------------------------------------------------- 1 | import { ActivatedRoute, Router } from '@angular/router'; 2 | import { TodoDataService } from './../service/data/todo-data.service'; 3 | import { Component, OnInit } from '@angular/core'; 4 | import { Todo } from '../list-todos/list-todos.component'; 5 | import { FormsModule } from '@angular/forms'; 6 | import { NgIf, DatePipe } from '@angular/common'; 7 | 8 | @Component({ 9 | selector: 'app-todo', 10 | templateUrl: './todo.component.html', 11 | styleUrls: ['./todo.component.css'], 12 | standalone: true, 13 | imports: [NgIf, FormsModule, DatePipe] 14 | }) 15 | export class TodoComponent implements OnInit { 16 | 17 | id: number = 0; 18 | todo: Todo = new Todo(this.id, '', false, new Date()); 19 | 20 | constructor( 21 | private todoService: TodoDataService, 22 | private route: ActivatedRoute, 23 | private router: Router 24 | ) { } 25 | 26 | ngOnInit() { 27 | 28 | this.id = this.route.snapshot.params['id']; 29 | 30 | this.todo = new Todo(this.id, '', false, new Date()); 31 | 32 | if (this.id != -1) { 33 | this.todoService.retrieveTodo('in28minutes', this.id) 34 | .subscribe( 35 | data => this.todo = data 36 | ) 37 | } 38 | } 39 | 40 | saveTodo() { 41 | if (this.id == -1) { //=== == 42 | this.todoService.createTodo('in28minutes', this.todo) 43 | .subscribe( 44 | data => { 45 | console.log(data) 46 | this.router.navigate(['todos']) 47 | } 48 | ) 49 | } else { 50 | this.todoService.updateTodo('in28minutes', this.id, this.todo) 51 | .subscribe( 52 | data => { 53 | console.log(data) 54 | this.router.navigate(['todos']) 55 | } 56 | ) 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /frontend/todo/src/app/welcome/welcome.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/src/app/welcome/welcome.component.css -------------------------------------------------------------------------------- /frontend/todo/src/app/welcome/welcome.component.html: -------------------------------------------------------------------------------- 1 |

Welcome!

2 | 3 |
4 | Welcome {{name}}. You can manage your todos here 5 |
6 | 7 |
8 | Click here to get a customized welcome message 9 | 10 |
11 | 12 |
13 |

Your Customized Welcome Message

14 | {{welcomeMessageFromService}} 15 |
-------------------------------------------------------------------------------- /frontend/todo/src/app/welcome/welcome.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { WelcomeComponent } from './welcome.component'; 4 | 5 | describe('WelcomeComponent', () => { 6 | let component: WelcomeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [WelcomeComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(WelcomeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/todo/src/app/welcome/welcome.component.ts: -------------------------------------------------------------------------------- 1 | import { WelcomeDataService } from './../service/data/welcome-data.service'; 2 | import { ActivatedRoute, RouterLink } from '@angular/router'; 3 | //package com.in28minutes.springboot.web; 4 | 5 | //import org.springframework.boot.SpringApplication; 6 | import { Component, OnInit } from '@angular/core';//'./app.component'; 7 | import { NgIf } from '@angular/common'; 8 | //import { AppComponent } from '../app.component'; 9 | 10 | //@ComponentScan( 11 | // value = "com.in28minutes.springboot.web") 12 | @Component({ 13 | selector: 'app-welcome', 14 | templateUrl: './welcome.component.html', 15 | styleUrls: ['./welcome.component.css'], 16 | standalone: true, 17 | imports: [RouterLink, NgIf] 18 | }) 19 | 20 | //public class SpringBootFirstWebApplication implements SomeInterface{ 21 | export class WelcomeComponent implements OnInit { 22 | 23 | message = 'Some Welcome Message' 24 | welcomeMessageFromService: string = '' 25 | name = '' 26 | //String message = "Some Welcome Message" 27 | 28 | //public SpringBootFirstWebApplication() { 29 | 30 | //ActivatedRoute 31 | constructor( 32 | private route: ActivatedRoute, 33 | private service: WelcomeDataService) { 34 | 35 | } 36 | 37 | // void init() { 38 | ngOnInit() { 39 | //COMPILATION ERROR this.message = 5 40 | //console.log(this.message) 41 | // console.log(this.route.snapshot.params['name']) 42 | this.name = this.route.snapshot.params['name']; 43 | 44 | } 45 | 46 | getWelcomeMessage() { 47 | //console.log(this.service.executeHelloWorldBeanService()); 48 | 49 | this.service.executeHelloWorldBeanService().subscribe( 50 | response => this.handleSuccessfulResponse(response), 51 | error => this.handleErrorResponse(error) 52 | ); 53 | 54 | //console.log('last line of getWelcomeMessage') 55 | 56 | //console.log("get welcome message"); 57 | } 58 | 59 | getWelcomeMessageWithParameter() { 60 | //console.log(this.service.executeHelloWorldBeanService()); 61 | 62 | this.service.executeHelloWorldServiceWithPathVariable(this.name).subscribe( 63 | response => this.handleSuccessfulResponse(response), 64 | error => this.handleErrorResponse(error) 65 | ); 66 | 67 | //console.log('last line of getWelcomeMessage') 68 | 69 | //console.log("get welcome message"); 70 | } 71 | 72 | 73 | handleSuccessfulResponse(response: any) { 74 | this.welcomeMessageFromService = response.message 75 | //console.log(response); 76 | //console.log(response.message); 77 | } 78 | 79 | handleErrorResponse(error: any) { 80 | //console.log(error); 81 | //console.log(error.error); 82 | //console.log(error.error.message); 83 | this.welcomeMessageFromService = error.error.message 84 | } 85 | } 86 | 87 | export class Class1 { 88 | 89 | } 90 | 91 | export class Class2 { 92 | 93 | } -------------------------------------------------------------------------------- /frontend/todo/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/src/assets/.gitkeep -------------------------------------------------------------------------------- /frontend/todo/src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /frontend/todo/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/todo/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /frontend/todo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/frontend/todo/src/favicon.ico -------------------------------------------------------------------------------- /frontend/todo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | My Todos Application 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /frontend/todo/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /frontend/todo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { environment } from './environments/environment'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | import { bootstrapApplication } from '@angular/platform-browser'; 6 | import { appConfig } from './app/app.config'; 7 | 8 | if (environment.production) { 9 | enableProdMode(); 10 | } 11 | 12 | bootstrapApplication(AppComponent, appConfig) 13 | .catch(err => console.error(err)); 14 | -------------------------------------------------------------------------------- /frontend/todo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** 38 | * If the application will be indexed by Google Search, the following is required. 39 | * Googlebot uses a renderer based on Chrome 41. 40 | * https://developers.google.com/search/docs/guides/rendering 41 | **/ 42 | // import 'core-js/es6/array'; 43 | 44 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 45 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 46 | 47 | /** IE10 and IE11 requires the following for the Reflect API. */ 48 | // import 'core-js/es6/reflect'; 49 | 50 | /** 51 | * Web Animations `@angular/platform-browser/animations` 52 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 53 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 54 | **/ 55 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 56 | 57 | /** 58 | * By default, zone.js will patch all possible macroTask and DomEvents 59 | * user can disable parts of macroTask/DomEvents patch by setting following flags 60 | */ 61 | 62 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 63 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 64 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 65 | 66 | /* 67 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 68 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 69 | */ 70 | // (window as any).__Zone_enable_cross_context_check = true; 71 | 72 | /*************************************************************************************************** 73 | * Zone JS is required by default for Angular itself. 74 | */ 75 | import 'zone.js/dist/zone'; // Included with Angular CLI. 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /frontend/todo/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css) 3 | -------------------------------------------------------------------------------- /frontend/todo/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /frontend/todo/src/todos.txt: -------------------------------------------------------------------------------- 1 | - -------------------------------------------------------------------------------- /frontend/todo/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /frontend/todo/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /frontend/todo/src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /frontend/todo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /frontend/todo/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "useDefineForClassFields": false, 22 | "lib": [ 23 | "ES2022", 24 | "dom" 25 | ] 26 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /frontend/todo/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /npm/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "first-package-with-npm", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "jquery": { 8 | "version": "3.3.1", 9 | "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", 10 | "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "first-package-with-npm", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "jquery": "^3.3.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /restful-web-services/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /restful-web-services/.gitignore: -------------------------------------------------------------------------------- 1 | *.idea 2 | /target -------------------------------------------------------------------------------- /restful-web-services/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | restful-web-services 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /restful-web-services/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/main/resources=UTF-8 4 | encoding//src/test/java=UTF-8 5 | encoding/=UTF-8 6 | -------------------------------------------------------------------------------- /restful-web-services/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 4 | org.eclipse.jdt.core.compiler.compliance=17 5 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 6 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 7 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore 8 | org.eclipse.jdt.core.compiler.release=disabled 9 | org.eclipse.jdt.core.compiler.source=17 10 | -------------------------------------------------------------------------------- /restful-web-services/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /restful-web-services/.springBeans: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /restful-web-services/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.in28minutes.rest.webservices 7 | restful-web-services 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | restful-web-services 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 3.2.2 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 21 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-data-jpa 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-web 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-security 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-oauth2-resource-server 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-configuration-processor 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-devtools 57 | runtime 58 | 59 | 60 | 61 | com.h2database 62 | h2 63 | runtime 64 | 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-starter-test 69 | test 70 | 71 | 72 | 73 | 74 | 75 | 76 | org.springframework.boot 77 | spring-boot-maven-plugin 78 | 79 | 80 | 81 | 82 | 83 | 84 | spring-milestones 85 | Spring Milestones 86 | https://repo.spring.io/milestone 87 | 88 | false 89 | 90 | 91 | 92 | 93 | 94 | spring-milestones 95 | Spring Milestones 96 | https://repo.spring.io/milestone 97 | 98 | false 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/basic/auth/AuthenticationBean.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.basic.auth; 2 | 3 | public class AuthenticationBean { 4 | 5 | private String message; 6 | 7 | public AuthenticationBean(String message) { 8 | this.message = message; 9 | } 10 | 11 | public String getMessage() { 12 | return message; 13 | } 14 | 15 | public void setMessage(String message) { 16 | this.message = message; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return String.format("HelloWorldBean [message=%s]", message); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/basic/auth/BasicAuthenticationController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.basic.auth; 2 | 3 | import org.springframework.web.bind.annotation.CrossOrigin; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | //Controller 8 | @CrossOrigin(origins="http://localhost:4200") 9 | @RestController 10 | public class BasicAuthenticationController { 11 | 12 | @GetMapping(path = "/basicauth") 13 | public AuthenticationBean helloWorldBean() { 14 | //throw new RuntimeException("Some Error has Happened! Contact Support at ***-***"); 15 | return new AuthenticationBean("You are authenticated"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/basic/auth/SpringSecurityConfigurationBasicAuth.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.basic.auth; 2 | 3 | import static org.springframework.security.config.Customizer.withDefaults; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.http.HttpMethod; 8 | import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; 9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 11 | import org.springframework.security.web.SecurityFilterChain; 12 | 13 | @Configuration 14 | @EnableWebSecurity 15 | @EnableMethodSecurity 16 | public class SpringSecurityConfigurationBasicAuth { 17 | 18 | @Bean 19 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 20 | http.authorizeHttpRequests(auth -> auth.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()); 21 | 22 | http.csrf(csrf -> csrf.disable()); 23 | 24 | http.httpBasic(withDefaults()); 25 | 26 | return http.build(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/BcryptEncoderTest.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | 5 | public class BcryptEncoderTest { 6 | 7 | public static void main(String[] args) { 8 | BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); 9 | 10 | for(int i=1; i<=10; i++) { 11 | String encodedString = encoder.encode("password@!23@#!"); 12 | System.out.println(encodedString); 13 | } 14 | 15 | // TODO Auto-generated method stub 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 8 | 9 | @SpringBootApplication 10 | public class RestfulWebServicesApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(RestfulWebServicesApplication.class, args); 14 | } 15 | 16 | //http://localhost:4200/ to 8080 17 | //Cross Origin Requests 18 | //Allow all requests only from http://localhost:4200/ 19 | @Bean 20 | public WebMvcConfigurer corsConfigurer() { 21 | return new WebMvcConfigurer() { 22 | public void addCorsMappings(CorsRegistry registry) { 23 | registry.addMapping("/**") 24 | .allowedMethods("*") 25 | .allowedOrigins("http://localhost:4200"); 26 | } 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/helloworld/HelloWorldBean.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.helloworld; 2 | 3 | public class HelloWorldBean { 4 | 5 | private String message; 6 | 7 | public HelloWorldBean(String message) { 8 | this.message = message; 9 | } 10 | 11 | public String getMessage() { 12 | return message; 13 | } 14 | 15 | public void setMessage(String message) { 16 | this.message = message; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return String.format("HelloWorldBean [message=%s]", message); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/helloworld/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.helloworld; 2 | 3 | import org.springframework.web.bind.annotation.CrossOrigin; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.PathVariable; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | //Controller 9 | @CrossOrigin(origins="http://localhost:4200") 10 | @RestController 11 | public class HelloWorldController { 12 | 13 | @GetMapping(path = "/hello-world") 14 | public String helloWorld() { 15 | return "Hello World"; 16 | } 17 | 18 | @GetMapping(path = "/hello-world-bean") 19 | public HelloWorldBean helloWorldBean() { 20 | //throw new RuntimeException("Some Error has Happened! Contact Support at ***-***"); 21 | return new HelloWorldBean("Hello World - Changed"); 22 | } 23 | 24 | ///hello-world/path-variable/in28minutes 25 | @GetMapping(path = "/hello-world/path-variable/{name}") 26 | public HelloWorldBean helloWorldPathVariable(@PathVariable String name) { 27 | return new HelloWorldBean(String.format("Hello World, %s", name)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JWTWebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.jwt; 2 | 3 | import java.security.KeyPair; 4 | import java.security.KeyPairGenerator; 5 | import java.security.interfaces.RSAPrivateKey; 6 | import java.security.interfaces.RSAPublicKey; 7 | import java.util.UUID; 8 | 9 | import org.springframework.boot.autoconfigure.security.servlet.PathRequest; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.http.HttpMethod; 13 | import org.springframework.security.authentication.AuthenticationManager; 14 | import org.springframework.security.authentication.ProviderManager; 15 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 16 | import org.springframework.security.config.Customizer; 17 | import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; 18 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 19 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 20 | import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; 21 | import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; 22 | import org.springframework.security.config.http.SessionCreationPolicy; 23 | import org.springframework.security.core.userdetails.User; 24 | import org.springframework.security.core.userdetails.UserDetails; 25 | import org.springframework.security.core.userdetails.UserDetailsService; 26 | import org.springframework.security.oauth2.jwt.JwtDecoder; 27 | import org.springframework.security.oauth2.jwt.JwtEncoder; 28 | import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; 29 | import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; 30 | import org.springframework.security.provisioning.InMemoryUserDetailsManager; 31 | import org.springframework.security.web.SecurityFilterChain; 32 | 33 | import com.nimbusds.jose.JOSEException; 34 | import com.nimbusds.jose.jwk.JWKSet; 35 | import com.nimbusds.jose.jwk.RSAKey; 36 | import com.nimbusds.jose.jwk.source.JWKSource; 37 | import com.nimbusds.jose.proc.SecurityContext; 38 | 39 | import static org.springframework.security.config.Customizer.withDefaults; 40 | 41 | @Configuration 42 | @EnableWebSecurity 43 | @EnableMethodSecurity 44 | public class JWTWebSecurityConfig { 45 | 46 | @Bean 47 | public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { 48 | // https://github.com/spring-projects/spring-security/issues/1231 49 | // https://docs.spring.io/spring-boot/docs/current/reference/html/data.html#data.sql.h2-web-console.spring-security 50 | return httpSecurity 51 | .authorizeHttpRequests(auth -> auth 52 | .requestMatchers("/authenticate", "/hello-world", "/hello-world-bean").permitAll() 53 | .requestMatchers(PathRequest.toH2Console()).permitAll() // h2-console is a servlet and NOT recommended for a production 54 | .requestMatchers(HttpMethod.OPTIONS,"/**") 55 | .permitAll() 56 | .anyRequest() 57 | .authenticated()) 58 | .csrf(AbstractHttpConfigurer::disable) 59 | .sessionManagement(session -> session. 60 | sessionCreationPolicy(SessionCreationPolicy.STATELESS)) 61 | .oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults())) 62 | .httpBasic(withDefaults()) 63 | .headers(header -> header.frameOptions(frameOptionsConfig -> frameOptionsConfig.sameOrigin())) 64 | .build(); 65 | } 66 | 67 | @Bean 68 | public AuthenticationManager authenticationManager(UserDetailsService userDetailsService) { 69 | var authenticationProvider = new DaoAuthenticationProvider(); 70 | authenticationProvider.setUserDetailsService(userDetailsService); 71 | 72 | return new ProviderManager(authenticationProvider); 73 | } 74 | 75 | @Bean 76 | public UserDetailsService userDetailsService() { 77 | UserDetails user = User.withUsername("in28minutes") 78 | .password("{noop}dummy") 79 | .authorities("read") 80 | .roles("USER") 81 | .build(); 82 | 83 | return new InMemoryUserDetailsManager(user); 84 | } 85 | 86 | @Bean 87 | public JWKSource jwkSource() { 88 | JWKSet jwkSet = new JWKSet(rsaKey()); 89 | 90 | return ((jwkSelector, securityContext) -> jwkSelector.select(jwkSet)); 91 | } 92 | 93 | @Bean 94 | JwtEncoder jwtEncoder(JWKSource jwkSource) { 95 | return new NimbusJwtEncoder(jwkSource); 96 | } 97 | 98 | @Bean 99 | JwtDecoder jwtDecoder() throws JOSEException { 100 | return NimbusJwtDecoder 101 | .withPublicKey(rsaKey().toRSAPublicKey()) 102 | .build(); 103 | } 104 | 105 | @Bean 106 | public RSAKey rsaKey() { 107 | KeyPair keyPair = keyPair(); 108 | 109 | return new RSAKey 110 | .Builder((RSAPublicKey) keyPair.getPublic()) 111 | .privateKey((RSAPrivateKey) keyPair.getPrivate()) 112 | .keyID(UUID.randomUUID().toString()) 113 | .build(); 114 | } 115 | 116 | @Bean 117 | public KeyPair keyPair() { 118 | try { 119 | var keyPairGenerator = KeyPairGenerator.getInstance("RSA"); 120 | keyPairGenerator.initialize(2048); 121 | return keyPairGenerator.generateKeyPair(); 122 | } catch (Exception e) { 123 | throw new IllegalStateException("Unable to generate an RSA Key Pair", e); 124 | } 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtAuthenticationRestController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.jwt; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.security.authentication.AuthenticationManager; 5 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class JwtAuthenticationRestController { 12 | 13 | private final JwtTokenService tokenService; 14 | 15 | private final AuthenticationManager authenticationManager; 16 | 17 | public JwtAuthenticationRestController(JwtTokenService tokenService, 18 | AuthenticationManager authenticationManager) { 19 | this.tokenService = tokenService; 20 | this.authenticationManager = authenticationManager; 21 | } 22 | 23 | @PostMapping("${jwt.get.token.uri}") 24 | public ResponseEntity generateToken( 25 | @RequestBody JwtTokenRequest jwtTokenRequest) { 26 | 27 | var authenticationToken = new UsernamePasswordAuthenticationToken(jwtTokenRequest.username(), jwtTokenRequest.password()); 28 | 29 | var authentication = authenticationManager.authenticate(authenticationToken); 30 | 31 | var token = tokenService.generateToken(authentication); 32 | 33 | return ResponseEntity.ok(new JwtTokenResponse(token)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtTokenRequest.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.jwt; 2 | 3 | public record JwtTokenRequest(String username, String password) { 4 | } 5 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtTokenResponse.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.jwt; 2 | 3 | public record JwtTokenResponse(String token) { 4 | } 5 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtTokenService.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.jwt; 2 | 3 | import java.time.Instant; 4 | import java.time.temporal.ChronoUnit; 5 | import java.util.stream.Collectors; 6 | 7 | import org.springframework.security.core.Authentication; 8 | import org.springframework.security.core.GrantedAuthority; 9 | import org.springframework.security.oauth2.jwt.JwtClaimsSet; 10 | import org.springframework.security.oauth2.jwt.JwtEncoder; 11 | import org.springframework.security.oauth2.jwt.JwtEncoderParameters; 12 | import org.springframework.stereotype.Service; 13 | 14 | @Service 15 | public class JwtTokenService { 16 | 17 | private final JwtEncoder jwtEncoder; 18 | 19 | public JwtTokenService(JwtEncoder jwtEncoder) { 20 | this.jwtEncoder = jwtEncoder; 21 | } 22 | 23 | public String generateToken(Authentication authentication) { 24 | 25 | var scope = authentication 26 | .getAuthorities() 27 | .stream() 28 | .map(GrantedAuthority::getAuthority) 29 | .collect(Collectors.joining(" ")); 30 | 31 | var claims = JwtClaimsSet.builder() 32 | .issuer("self") 33 | .issuedAt(Instant.now()) 34 | .expiresAt(Instant.now().plus(90, ChronoUnit.MINUTES)) 35 | .subject(authentication.getName()) 36 | .claim("scope", scope) 37 | .build(); 38 | 39 | return this.jwtEncoder 40 | .encode(JwtEncoderParameters.from(claims)) 41 | .getTokenValue(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/todo/Todo.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.todo; 2 | 3 | import java.util.Date; 4 | 5 | import jakarta.persistence.Entity; 6 | import jakarta.persistence.GeneratedValue; 7 | import jakarta.persistence.Id; 8 | 9 | @Entity 10 | public class Todo { 11 | 12 | @Id 13 | @GeneratedValue 14 | private Long id; 15 | 16 | private String username; 17 | private String description; 18 | private Date targetDate; 19 | private boolean isDone; 20 | 21 | protected Todo() { 22 | 23 | } 24 | 25 | public Todo(long id, String username, String description, Date targetDate, boolean isDone) { 26 | super(); 27 | this.id = id; 28 | this.username = username; 29 | this.description = description; 30 | this.targetDate = targetDate; 31 | this.isDone = isDone; 32 | } 33 | 34 | public Long getId() { 35 | return id; 36 | } 37 | 38 | public void setId(Long id) { 39 | this.id = id; 40 | } 41 | 42 | public String getUsername() { 43 | return username; 44 | } 45 | 46 | public void setUsername(String username) { 47 | this.username = username; 48 | } 49 | 50 | public String getDescription() { 51 | return description; 52 | } 53 | 54 | public void setDescription(String description) { 55 | this.description = description; 56 | } 57 | 58 | public Date getTargetDate() { 59 | return targetDate; 60 | } 61 | 62 | public void setTargetDate(Date targetDate) { 63 | this.targetDate = targetDate; 64 | } 65 | 66 | public boolean isDone() { 67 | return isDone; 68 | } 69 | 70 | public void setDone(boolean isDone) { 71 | this.isDone = isDone; 72 | } 73 | 74 | @Override 75 | public int hashCode() { 76 | final int prime = 31; 77 | int result = 1; 78 | result = prime * result + (int) (id ^ (id >>> 32)); 79 | return result; 80 | } 81 | 82 | @Override 83 | public boolean equals(Object obj) { 84 | if (this == obj) 85 | return true; 86 | if (obj == null) 87 | return false; 88 | if (getClass() != obj.getClass()) 89 | return false; 90 | Todo other = (Todo) obj; 91 | if (id != other.id) 92 | return false; 93 | return true; 94 | } 95 | 96 | 97 | } 98 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/todo/TodoHardcodedService.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.todo; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class TodoHardcodedService { 11 | 12 | private static List todos = new ArrayList<>(); 13 | private static long idCounter = 0; 14 | 15 | static { 16 | todos.add(new Todo(++idCounter, "in28minutes","Learn to Dance 2", new Date(), false )); 17 | todos.add(new Todo(++idCounter, "in28minutes","Learn about Microservices 2", new Date(), false )); 18 | todos.add(new Todo(++idCounter, "in28minutes","Learn about Angular", new Date(), false )); 19 | } 20 | 21 | public List findAll() { 22 | return todos; 23 | } 24 | 25 | public Todo save(Todo todo) { 26 | if(todo.getId()==-1 || todo.getId()==0) { 27 | todo.setId(++idCounter); 28 | todos.add(todo); 29 | } else { 30 | deleteById(todo.getId()); 31 | todos.add(todo); 32 | } 33 | return todo; 34 | } 35 | 36 | public Todo deleteById(long id) { 37 | Todo todo = findById(id); 38 | 39 | if(todo==null) return null; 40 | 41 | if(todos.remove(todo)) { 42 | return todo; 43 | } 44 | 45 | return null; 46 | } 47 | 48 | public Todo findById(long id) { 49 | for(Todo todo:todos) { 50 | if(todo.getId() == id) { 51 | return todo; 52 | } 53 | } 54 | 55 | return null; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/todo/TodoJpaRepository.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.todo; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | @Repository 9 | public interface TodoJpaRepository extends JpaRepository{ 10 | List findByUsername(String username); 11 | } 12 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/todo/TodoJpaResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.todo; 2 | 3 | import java.net.URI; 4 | import java.util.List; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.CrossOrigin; 10 | import org.springframework.web.bind.annotation.DeleteMapping; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.PutMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RestController; 17 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 18 | 19 | import com.in28minutes.rest.webservices.restfulwebservices.todo.Todo; 20 | 21 | @CrossOrigin(origins="http://localhost:4200") 22 | @RestController 23 | public class TodoJpaResource { 24 | 25 | @Autowired 26 | private TodoHardcodedService todoService; 27 | 28 | @Autowired 29 | private TodoJpaRepository todoJpaRepository; 30 | 31 | 32 | @GetMapping("/jpa/users/{username}/todos") 33 | public List getAllTodos(@PathVariable String username){ 34 | return todoJpaRepository.findByUsername(username); 35 | //return todoService.findAll(); 36 | } 37 | 38 | @GetMapping("/jpa/users/{username}/todos/{id}") 39 | public Todo getTodo(@PathVariable String username, @PathVariable long id){ 40 | return todoJpaRepository.findById(id).get(); 41 | //return todoService.findById(id); 42 | } 43 | 44 | //DELETE /users/{username}/todos/{id} 45 | @DeleteMapping("/jpa/users/{username}/todos/{id}") 46 | public ResponseEntity deleteTodo( 47 | @PathVariable String username, @PathVariable long id){ 48 | 49 | //Todo todo = todoService.deleteById(id); 50 | todoJpaRepository.deleteById(id); 51 | 52 | return ResponseEntity.noContent().build(); 53 | //return ResponseEntity.notFound().build(); 54 | } 55 | 56 | 57 | //Edit/Update a Todo 58 | //PUT /users/{user_name}/todos/{todo_id} 59 | @PutMapping("/jpa/users/{username}/todos/{id}") 60 | public ResponseEntity updateTodo( 61 | @PathVariable String username, 62 | @PathVariable long id, @RequestBody Todo todo){ 63 | 64 | //Todo todoUpdated = todoService.save(todo); 65 | Todo todoUpdated = todoJpaRepository.save(todo); 66 | 67 | return new ResponseEntity(todo, HttpStatus.OK); 68 | } 69 | 70 | @PostMapping("/jpa/users/{username}/todos") 71 | public ResponseEntity createTodo( 72 | @PathVariable String username, @RequestBody Todo todo){ 73 | 74 | //Todo createdTodo = todoService.save(todo); 75 | todo.setUsername(username); 76 | Todo createdTodo = todoJpaRepository.save(todo); 77 | 78 | //Location 79 | //Get current resource url 80 | ///{id} 81 | URI uri = ServletUriComponentsBuilder.fromCurrentRequest() 82 | .path("/{id}").buildAndExpand(createdTodo.getId()).toUri(); 83 | 84 | return ResponseEntity.created(uri).build(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/todo/TodoResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices.todo; 2 | 3 | import java.net.URI; 4 | import java.util.List; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.CrossOrigin; 10 | import org.springframework.web.bind.annotation.DeleteMapping; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.PutMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RestController; 17 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 18 | 19 | import com.in28minutes.rest.webservices.restfulwebservices.todo.Todo; 20 | 21 | @CrossOrigin(origins="http://localhost:4200") 22 | @RestController 23 | public class TodoResource { 24 | 25 | @Autowired 26 | private TodoHardcodedService todoService; 27 | 28 | @GetMapping("/users/{username}/todos") 29 | public List getAllTodos(@PathVariable String username){ 30 | return todoService.findAll(); 31 | } 32 | 33 | @GetMapping("/users/{username}/todos/{id}") 34 | public Todo getTodo(@PathVariable String username, @PathVariable long id){ 35 | return todoService.findById(id); 36 | } 37 | 38 | //DELETE /users/{username}/todos/{id} 39 | @DeleteMapping("/users/{username}/todos/{id}") 40 | public ResponseEntity deleteTodo( 41 | @PathVariable String username, @PathVariable long id){ 42 | 43 | Todo todo = todoService.deleteById(id); 44 | 45 | if(todo!=null) { 46 | return ResponseEntity.noContent().build(); 47 | } 48 | 49 | return ResponseEntity.notFound().build(); 50 | } 51 | 52 | 53 | //Edit/Update a Todo 54 | //PUT /users/{user_name}/todos/{todo_id} 55 | @PutMapping("/users/{username}/todos/{id}") 56 | public ResponseEntity updateTodo( 57 | @PathVariable String username, 58 | @PathVariable long id, @RequestBody Todo todo){ 59 | 60 | Todo todoUpdated = todoService.save(todo); 61 | 62 | return new ResponseEntity(todo, HttpStatus.OK); 63 | } 64 | 65 | @PostMapping("/users/{username}/todos") 66 | public ResponseEntity updateTodo( 67 | @PathVariable String username, @RequestBody Todo todo){ 68 | 69 | Todo createdTodo = todoService.save(todo); 70 | 71 | //Location 72 | //Get current resource url 73 | ///{id} 74 | URI uri = ServletUriComponentsBuilder.fromCurrentRequest() 75 | .path("/{id}").buildAndExpand(createdTodo.getId()).toUri(); 76 | 77 | return ResponseEntity.created(uri).build(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /restful-web-services/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | logging.level.org.springframework = info 2 | 3 | #spring.security.user.name=in28minutes 4 | #spring.security.user.password=dummy 5 | 6 | jwt.get.token.uri=/authenticate 7 | 8 | spring.datasource.url=jdbc:h2:mem:testdb 9 | spring.jpa.show-sql=true 10 | spring.h2.console.enabled=true 11 | spring.jpa.defer-datasource-initialization=true -------------------------------------------------------------------------------- /restful-web-services/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | insert into todo(id, username,description,target_date,is_done) 2 | values(10001, 'in28minutes', 'Learn JPA', CURRENT_DATE(), false); 3 | 4 | insert into todo(id, username,description,target_date,is_done) 5 | values(10002, 'in28minutes', 'Learn Data JPA', CURRENT_DATE(), false); 6 | 7 | insert into todo(id, username,description,target_date,is_done) 8 | values(10003, 'in28minutes', 'Learn Microservices', CURRENT_DATE(), false); -------------------------------------------------------------------------------- /restful-web-services/src/test/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.rest.webservices.restfulwebservices; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class RestfulWebServicesApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/zz-spring-boot-in-10-steps/.DS_Store -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/.gitignore: -------------------------------------------------------------------------------- 1 | *.idea 2 | /target 3 | .DS_Store -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/HELP.md: -------------------------------------------------------------------------------- 1 | # Read Me First 2 | The following was discovered as part of building this project: 3 | 4 | * The original package name 'com.in28minutes.springboot.learn-spring-boot' is invalid and this project uses 'com.in28minutes.springboot.learnspringboot' instead. 5 | 6 | # Getting Started 7 | 8 | ### Reference Documentation 9 | For further reference, please consider the following sections: 10 | 11 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 12 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.0.0-M3/maven-plugin/reference/html/) 13 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.0.0-M3/maven-plugin/reference/html/#build-image) 14 | * [Spring Web](https://docs.spring.io/spring-boot/docs/3.0.0-M3/reference/htmlsingle/#web) 15 | 16 | ### Guides 17 | The following guides illustrate how to use some features concretely: 18 | 19 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) 20 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) 21 | * [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) 22 | 23 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/notes.txt: -------------------------------------------------------------------------------- 1 | 2 | dev 3 | ~~~~ 4 | logging.level.org.springframework=trace 5 | 6 | prod 7 | ~~~~ 8 | logging.level.org.springframework=info 9 | 10 | 11 | trace 12 | debug 13 | info 14 | warning 15 | error 16 | 17 | off 18 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.0.1 9 | 10 | 11 | com.in28minutes.springboot 12 | learn-spring-boot 13 | 0.0.1-SNAPSHOT 14 | learn-spring-boot 15 | Demo project for Spring Boot 16 | 17 | 17 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-actuator 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-devtools 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-test 39 | test 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-maven-plugin 49 | 50 | 51 | 52 | 53 | 54 | spring-milestones 55 | Spring Milestones 56 | https://repo.spring.io/milestone 57 | 58 | false 59 | 60 | 61 | 62 | 63 | 64 | spring-milestones 65 | Spring Milestones 66 | https://repo.spring.io/milestone 67 | 68 | false 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/full-stack-with-angular-and-spring-boot/1e3250b8602305edb8b34358c7e67b7ce7243664/zz-spring-boot-in-10-steps/src/.DS_Store -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/main/java/com/in28minutes/springboot/learnspringboot/Course.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.learnspringboot; 2 | 3 | public class Course { 4 | private long id; 5 | private String name; 6 | private String author; 7 | 8 | public Course(long id, String name, String author) { 9 | super(); 10 | this.id = id; 11 | this.name = name; 12 | this.author = author; 13 | } 14 | 15 | public long getId() { 16 | return id; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public String getAuthor() { 24 | return author; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "Course [id=" + id + ", name=" + name + ", author=" + author + "]"; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/main/java/com/in28minutes/springboot/learnspringboot/CourseController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.learnspringboot; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | public class CourseController { 10 | 11 | @RequestMapping("/courses") 12 | public List retrieveAllCourses() { 13 | return List.of( 14 | new Course(1, "Learn AWS", "in28minutes"), 15 | new Course(2, "Learn DevOps", "in28minutes"), 16 | new Course(3, "Learn Azure", "in28minutes"), 17 | new Course(4, "Learn GCP", "in28minutes") 18 | 19 | ); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/main/java/com/in28minutes/springboot/learnspringboot/CurrencyConfigurationController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.learnspringboot; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class CurrencyConfigurationController { 9 | 10 | @Autowired 11 | private CurrencyServiceConfiguration configuration; 12 | 13 | @RequestMapping("/currency-configuration") 14 | public CurrencyServiceConfiguration retrieveAllCourses() { 15 | return configuration; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/main/java/com/in28minutes/springboot/learnspringboot/CurrencyServiceConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.learnspringboot; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.stereotype.Component; 5 | 6 | //currency-service.url= 7 | //currency-service.username= 8 | //currency-service.key= 9 | 10 | @ConfigurationProperties(prefix = "currency-service") 11 | @Component 12 | public class CurrencyServiceConfiguration { 13 | 14 | private String url; 15 | private String username; 16 | private String key; 17 | 18 | public String getUrl() { 19 | return url; 20 | } 21 | 22 | public void setUrl(String url) { 23 | this.url = url; 24 | } 25 | 26 | public String getUsername() { 27 | return username; 28 | } 29 | 30 | public void setUsername(String username) { 31 | this.username = username; 32 | } 33 | 34 | public String getKey() { 35 | return key; 36 | } 37 | 38 | public void setKey(String key) { 39 | this.key = key; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/main/java/com/in28minutes/springboot/learnspringboot/LearnSpringBootApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.learnspringboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class LearnSpringBootApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(LearnSpringBootApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | logging.level.org.springframework=trace 2 | 3 | currency-service.url=http://dev.in28minutes.com 4 | currency-service.username=devusername 5 | currency-service.key=devkey 6 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/main/resources/application-prod.properties: -------------------------------------------------------------------------------- 1 | logging.level.org.springframework=info 2 | -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | logging.level.org.springframework=debug 2 | spring.profiles.active=dev 3 | 4 | 5 | currency-service.url=http://default1.in28minutes.com 6 | currency-service.username=defaultusername 7 | currency-service.key=defaultkey 8 | 9 | management.endpoints.web.exposure.include=health,metrics -------------------------------------------------------------------------------- /zz-spring-boot-in-10-steps/src/test/java/com/in28minutes/springboot/learnspringboot/LearnSpringBootApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.learnspringboot; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class LearnSpringBootApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------