├── .idea
├── .gitignore
├── compiler.xml
├── dataSources.xml
├── encodings.xml
├── jarRepositories.xml
├── libraries
│ ├── Maven__antlr_antlr_2_7_7.xml
│ ├── Maven__ch_qos_logback_logback_classic_1_2_11.xml
│ ├── Maven__ch_qos_logback_logback_core_1_2_11.xml
│ ├── Maven__com_fasterxml_classmate_1_5_1.xml
│ ├── Maven__com_fasterxml_jackson_core_jackson_annotations_2_13_4.xml
│ ├── Maven__com_fasterxml_jackson_core_jackson_core_2_13_4.xml
│ ├── Maven__com_fasterxml_jackson_core_jackson_databind_2_13_4_2.xml
│ ├── Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_13_4.xml
│ ├── Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_13_4.xml
│ ├── Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_13_4.xml
│ ├── Maven__com_github_stephenc_jcip_jcip_annotations_1_0_1.xml
│ ├── Maven__com_jayway_jsonpath_json_path_2_7_0.xml
│ ├── Maven__com_nimbusds_nimbus_jose_jwt_9_22.xml
│ ├── Maven__com_sun_activation_jakarta_activation_1_2_2.xml
│ ├── Maven__com_sun_istack_istack_commons_runtime_3_0_12.xml
│ ├── Maven__com_vaadin_external_google_android_json_0_0_20131108_vaadin1.xml
│ ├── Maven__com_zaxxer_HikariCP_4_0_3.xml
│ ├── Maven__jakarta_activation_jakarta_activation_api_1_2_2.xml
│ ├── Maven__jakarta_annotation_jakarta_annotation_api_1_3_5.xml
│ ├── Maven__jakarta_persistence_jakarta_persistence_api_2_2_3.xml
│ ├── Maven__jakarta_transaction_jakarta_transaction_api_1_3_3.xml
│ ├── Maven__jakarta_validation_jakarta_validation_api_2_0_2.xml
│ ├── Maven__jakarta_xml_bind_jakarta_xml_bind_api_2_3_3.xml
│ ├── Maven__net_bytebuddy_byte_buddy_1_12_18.xml
│ ├── Maven__net_bytebuddy_byte_buddy_agent_1_12_18.xml
│ ├── Maven__net_minidev_accessors_smart_2_4_8.xml
│ ├── Maven__net_minidev_json_smart_2_4_8.xml
│ ├── Maven__org_apache_logging_log4j_log4j_api_2_17_2.xml
│ ├── Maven__org_apache_logging_log4j_log4j_to_slf4j_2_17_2.xml
│ ├── Maven__org_apache_tomcat_embed_tomcat_embed_core_9_0_68.xml
│ ├── Maven__org_apache_tomcat_embed_tomcat_embed_el_9_0_68.xml
│ ├── Maven__org_apache_tomcat_embed_tomcat_embed_websocket_9_0_68.xml
│ ├── Maven__org_apiguardian_apiguardian_api_1_1_2.xml
│ ├── Maven__org_aspectj_aspectjweaver_1_9_7.xml
│ ├── Maven__org_assertj_assertj_core_3_22_0.xml
│ ├── Maven__org_checkerframework_checker_qual_3_5_0.xml
│ ├── Maven__org_glassfish_jaxb_jaxb_runtime_2_3_7.xml
│ ├── Maven__org_glassfish_jaxb_txw2_2_3_7.xml
│ ├── Maven__org_hamcrest_hamcrest_2_2.xml
│ ├── Maven__org_hibernate_common_hibernate_commons_annotations_5_1_2_Final.xml
│ ├── Maven__org_hibernate_hibernate_core_5_6_12_Final.xml
│ ├── Maven__org_hibernate_validator_hibernate_validator_6_2_5_Final.xml
│ ├── Maven__org_jboss_jandex_2_4_2_Final.xml
│ ├── Maven__org_jboss_logging_jboss_logging_3_4_3_Final.xml
│ ├── Maven__org_junit_jupiter_junit_jupiter_5_8_2.xml
│ ├── Maven__org_junit_jupiter_junit_jupiter_api_5_8_2.xml
│ ├── Maven__org_junit_jupiter_junit_jupiter_engine_5_8_2.xml
│ ├── Maven__org_junit_jupiter_junit_jupiter_params_5_8_2.xml
│ ├── Maven__org_junit_platform_junit_platform_commons_1_8_2.xml
│ ├── Maven__org_junit_platform_junit_platform_engine_1_8_2.xml
│ ├── Maven__org_mockito_mockito_core_4_5_1.xml
│ ├── Maven__org_mockito_mockito_junit_jupiter_4_5_1.xml
│ ├── Maven__org_objenesis_objenesis_3_2.xml
│ ├── Maven__org_opentest4j_opentest4j_1_2_0.xml
│ ├── Maven__org_ow2_asm_asm_9_1.xml
│ ├── Maven__org_postgresql_postgresql_42_3_7.xml
│ ├── Maven__org_projectlombok_lombok_1_18_24.xml
│ ├── Maven__org_skyscreamer_jsonassert_1_5_1.xml
│ ├── Maven__org_slf4j_jul_to_slf4j_1_7_36.xml
│ ├── Maven__org_slf4j_slf4j_api_1_7_36.xml
│ ├── Maven__org_springframework_boot_spring_boot_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_autoconfigure_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_aop_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_data_jpa_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_jdbc_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_json_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_logging_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_oauth2_resource_server_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_security_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_test_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_tomcat_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_validation_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_starter_web_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_test_2_7_5.xml
│ ├── Maven__org_springframework_boot_spring_boot_test_autoconfigure_2_7_5.xml
│ ├── Maven__org_springframework_data_spring_data_commons_2_7_5.xml
│ ├── Maven__org_springframework_data_spring_data_jpa_2_7_5.xml
│ ├── Maven__org_springframework_security_spring_security_config_5_7_4.xml
│ ├── Maven__org_springframework_security_spring_security_core_5_7_4.xml
│ ├── Maven__org_springframework_security_spring_security_crypto_5_7_4.xml
│ ├── Maven__org_springframework_security_spring_security_oauth2_core_5_7_4.xml
│ ├── Maven__org_springframework_security_spring_security_oauth2_jose_5_7_4.xml
│ ├── Maven__org_springframework_security_spring_security_oauth2_resource_server_5_7_4.xml
│ ├── Maven__org_springframework_security_spring_security_test_5_7_4.xml
│ ├── Maven__org_springframework_security_spring_security_web_5_7_4.xml
│ ├── Maven__org_springframework_spring_aop_5_3_23.xml
│ ├── Maven__org_springframework_spring_aspects_5_3_23.xml
│ ├── Maven__org_springframework_spring_beans_5_3_23.xml
│ ├── Maven__org_springframework_spring_context_5_3_23.xml
│ ├── Maven__org_springframework_spring_core_5_3_23.xml
│ ├── Maven__org_springframework_spring_expression_5_3_23.xml
│ ├── Maven__org_springframework_spring_jcl_5_3_23.xml
│ ├── Maven__org_springframework_spring_jdbc_5_3_23.xml
│ ├── Maven__org_springframework_spring_orm_5_3_23.xml
│ ├── Maven__org_springframework_spring_test_5_3_23.xml
│ ├── Maven__org_springframework_spring_tx_5_3_23.xml
│ ├── Maven__org_springframework_spring_web_5_3_23.xml
│ ├── Maven__org_springframework_spring_webmvc_5_3_23.xml
│ ├── Maven__org_xmlunit_xmlunit_core_2_9_0.xml
│ └── Maven__org_yaml_snakeyaml_1_30.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── README.md
├── angular-app
├── .browserslistrc
├── .editorconfig
├── .gitignore
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── tasks.json
├── README.md
├── angular-app.iml
├── angular.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── proxy.conf.json
├── src
│ ├── app
│ │ ├── access-denied
│ │ │ ├── access-denied.component.css
│ │ │ ├── access-denied.component.html
│ │ │ ├── access-denied.component.spec.ts
│ │ │ └── access-denied.component.ts
│ │ ├── api
│ │ │ └── web-api.service.ts
│ │ ├── app-routing.module.ts
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── auth
│ │ │ ├── auth.guard.ts
│ │ │ └── auth.service.ts
│ │ └── user-info
│ │ │ ├── user-info.component.css
│ │ │ ├── user-info.component.html
│ │ │ └── user-info.component.ts
│ ├── assets
│ │ └── .gitkeep
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.css
│ ├── test.ts
│ └── utils
│ │ └── app-init.ts
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
├── docker-compose.yml
├── docs
├── aimg.png
├── aimg_1.png
├── aimg_10.png
├── aimg_11.png
├── aimg_12.png
├── aimg_13.png
├── aimg_14.png
├── aimg_2.png
├── aimg_3.png
├── aimg_4.png
├── aimg_5.png
├── aimg_6.png
├── aimg_7.png
├── aimg_8.png
├── aimg_9.png
├── img.png
├── img_1.png
├── img_10.png
├── img_11.png
├── img_12.png
├── img_13.png
├── img_14.png
├── img_15.png
├── img_16.png
├── img_2.png
├── img_3.png
├── img_4.png
├── img_5.png
├── img_6.png
├── img_7.png
├── img_8.png
└── img_9.png
├── spring-app
├── .gitignore
├── .mvn
│ └── wrapper
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── springapp
│ │ │ ├── SpringAppApplication.java
│ │ │ ├── controller
│ │ │ └── WebController.java
│ │ │ ├── entity
│ │ │ └── User.java
│ │ │ ├── repository
│ │ │ └── UserRepository.java
│ │ │ ├── security
│ │ │ ├── JwtUserSyncFilter.java
│ │ │ └── SecurityConfig.java
│ │ │ └── service
│ │ │ └── UserService.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── com
│ └── example
│ └── springapp
│ └── SpringAppApplicationTests.java
└── springboot-keycloak-angular.iml
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/dataSources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | postgresql
6 | true
7 | org.postgresql.Driver
8 | jdbc:postgresql://localhost:5432/ewizitingDB
9 | $ProjectFileDir$
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__antlr_antlr_2_7_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_2_11.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__ch_qos_logback_logback_core_1_2_11.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_fasterxml_classmate_1_5_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_13_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_13_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_13_4_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_13_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_13_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_13_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_github_stephenc_jcip_jcip_annotations_1_0_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_jayway_jsonpath_json_path_2_7_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_nimbusds_nimbus_jose_jwt_9_22.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_sun_activation_jakarta_activation_1_2_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_sun_istack_istack_commons_runtime_3_0_12.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_vaadin_external_google_android_json_0_0_20131108_vaadin1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_zaxxer_HikariCP_4_0_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__jakarta_activation_jakarta_activation_api_1_2_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__jakarta_annotation_jakarta_annotation_api_1_3_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__jakarta_persistence_jakarta_persistence_api_2_2_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__jakarta_transaction_jakarta_transaction_api_1_3_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__jakarta_validation_jakarta_validation_api_2_0_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__jakarta_xml_bind_jakarta_xml_bind_api_2_3_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__net_bytebuddy_byte_buddy_1_12_18.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__net_bytebuddy_byte_buddy_agent_1_12_18.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__net_minidev_accessors_smart_2_4_8.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__net_minidev_json_smart_2_4_8.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_apache_logging_log4j_log4j_api_2_17_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_apache_logging_log4j_log4j_to_slf4j_2_17_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_core_9_0_68.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_el_9_0_68.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_websocket_9_0_68.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_aspectj_aspectjweaver_1_9_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_assertj_assertj_core_3_22_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_checkerframework_checker_qual_3_5_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_glassfish_jaxb_jaxb_runtime_2_3_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_glassfish_jaxb_txw2_2_3_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_hamcrest_hamcrest_2_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_hibernate_common_hibernate_commons_annotations_5_1_2_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_hibernate_hibernate_core_5_6_12_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_hibernate_validator_hibernate_validator_6_2_5_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_jboss_jandex_2_4_2_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_jboss_logging_jboss_logging_3_4_3_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_8_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_8_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_8_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_8_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_8_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_8_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_mockito_mockito_core_4_5_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_mockito_mockito_junit_jupiter_4_5_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_objenesis_objenesis_3_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_ow2_asm_asm_9_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_postgresql_postgresql_42_3_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_projectlombok_lombok_1_18_24.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_skyscreamer_jsonassert_1_5_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_slf4j_jul_to_slf4j_1_7_36.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_36.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_autoconfigure_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_aop_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_data_jpa_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_jdbc_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_json_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_logging_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_oauth2_resource_server_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_security_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_test_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_tomcat_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_validation_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_web_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_test_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_boot_spring_boot_test_autoconfigure_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_data_spring_data_commons_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_data_spring_data_jpa_2_7_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_security_spring_security_config_5_7_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_security_spring_security_core_5_7_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_security_spring_security_crypto_5_7_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_security_spring_security_oauth2_core_5_7_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_security_spring_security_oauth2_jose_5_7_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_security_spring_security_oauth2_resource_server_5_7_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_security_spring_security_test_5_7_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_security_spring_security_web_5_7_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_aop_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_aspects_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_beans_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_context_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_core_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_expression_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_jcl_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_jdbc_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_orm_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_test_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_tx_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_web_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_springframework_spring_webmvc_5_3_23.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_xmlunit_xmlunit_core_2_9_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_yaml_snakeyaml_1_30.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Overview
4 | * Keycloak version: 20.0
5 |
6 | ## Useful links
7 | * [Setting up keycloack with spring boot](https://www.baeldung.com/spring-boot-keycloak)
8 | * [Secure Spring Boot + Angular 9 Application using KeyCloak(1/3)](https://medium.com/@kamleshbadgujar00/secure-spring-boot-angular-9-application-using-keycloak-1-3-b00e801ba693)
9 | * [Github - Backend (Spring Boot)](https://github.com/kamleshbadgujar/heroes-backend)
10 | * [Github - Frontend (Angular)](https://github.com/kamleshbadgujar/heroes-app)
11 |
12 | # Tutorial
13 |
14 | ## 1. Deploying Keycloak + Postgres with Docker
15 | > Remember to create database in postgres for Keycloak. By default, docker compose creates database for Spring Boot only. To do this purpose you can use `pgadmin`.
16 |
17 | *docker-compose.yml*
18 | ```yaml
19 | version: "3.8"
20 | services:
21 |
22 | postgresql:
23 | container_name: postgres
24 | image: postgres:14.4
25 | restart: always
26 | ports:
27 | - "5432:5432" # External port access for the development profile
28 | environment:
29 | POSTGRES_PASSWORD: admin
30 | POSTGRES_USER: admin
31 | POSTGRES_DB: ewizitingDB
32 |
33 | pgadmin:
34 | container_name: pgadmin
35 | image: dpage/pgadmin4:latest
36 | restart: always
37 | ports:
38 | - "81:80"
39 | environment:
40 | PGADMIN_DEFAULT_EMAIL: admin@admin.com
41 | PGADMIN_DEFAULT_PASSWORD: admin
42 |
43 | keycloak:
44 | image: quay.io/keycloak/keycloak:20.0.0
45 | environment:
46 | KC_DB: postgres
47 | KC_DB_URL: jdbc:postgresql://postgresql:5432/keycloak
48 | KC_DB_USERNAME: admin
49 | KC_DB_PASSWORD: admin
50 | KEYCLOAK_ADMIN: admin
51 | KEYCLOAK_ADMIN_PASSWORD: admin
52 | # Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.
53 | #JDBC_PARAMS: "ssl=true"
54 | ports:
55 | - "8081:8081"
56 | command: [ "start-dev", "--http-port=8081" ]
57 | depends_on:
58 | - postgresql
59 | ```
60 | Start docker with `docker compose up`.
61 |
62 | ## 2. Keycloak - configuration
63 | > Remember to create database in postgres for Keycloak. By default, docker compose creates database for Spring Boot only. To do this purpose you can use `pgadmin`.
64 |
65 | ### 2.1. Creating a realm
66 | Realm is a separate "container" for our application. It contains set of users, clients and global roles.
67 | We can say **realm** represents our application infrastructure, which can have multiple **clients (e.g. backend and frontend)**.
68 |
69 | 1. Go to http://localhost:8081 -> administrator console and login with username and password: `admin`
70 | 2. Click upper top corner to create new realm. By default, you will only see `master` (ignore my `hospital-app` realm).
71 | 
72 | 3. Let's add new realm and call it `test-app`. After clicking the `Create` button, a new realm will be created.
73 | 
74 | 4. **Remember to perform all the operations in this new realm.**
75 |
76 | ### 2.2. Creating a client for backend (Spring Boot)
77 | **Client** is a thing that will access data from our **realm**. For example a client can be **backend - Spring Boot** and/or **frontened - Angular**.
78 |
79 | 1. Go to `Clients -> Create client`
80 | 
81 | 2. As we want to create a client for Spring Boot, populate `Client ID` with `backend` and smash next.
82 | 
83 | 3. Select `Standard flow` and `Direct access grants`, because Spring Boot backend service **will only verify bearer token, it will never initiate the login**. Click save.
84 | 
85 | 4. Now go to `Clients` and select our newly created `backend` client.
86 | Find `Valid redirect URIs` and type `http://localhost:8080/*` (the Spring Boot url - remember to add `*`) and save.
87 | 
88 |
89 | ### 2.3. Creating a client for frontend (Angular)
90 | 1. Go to `Clients -> Create client`
91 | 
92 | 2. Fill in `Client ID` with `frontend`. Next.
93 | 
94 | 3. Select `Standard flow`, `Direct access grants` and `Implicit flow`.
95 | 
96 | 4. Now go to `Clients` and select our newly created `frontend` client.
97 | Fill in `Valid redirect URIs` and `Valid post logout redirect URIs` with URIs you redirect after login/logout on frontend e.g. I used my local ip address.
98 | Fill in `Web origins` with `*` and save.
99 | 
100 |
101 | ### 2.4. Creating `Roles`
102 | We have 2 types of roles. Global (for every client) called `Realm roles`, and local `Client roles` available only for specific client.
103 | In the tutorial we use only global roles - `Realm roles`.
104 |
105 | 1. Go to `Realm roles -> Create role`
106 | 2. Our role will be `user`
107 | 
108 |
109 | ### 2.5. Enabling Registration
110 | 1. Go to `Realm settings -> Login`
111 | 2. Check `User registration` and `Email as username`
112 | 
113 |
114 | ### 2.6. Making role to be added by default for every registered user
115 | 1. Go to `Realm settings -> User registration`
116 | 2. Click `Assign role`
117 | 
118 | 3. Choose our role `user` and click `Assign`
119 | 
120 |
121 | Now every user registered using our Angular frontend will be automatically assigned to role `user`.
122 |
123 | ### 2.7. Creating 'test' users
124 | I call it `test user`, because this user is created 'artificially', we will add a registration process later.
125 |
126 | 1. Go to `Users -> Add user`
127 | 2. Fill in `Email` and check `Email verified`.
128 | Make sure `Required user actions` field is empty that we can start using this account without any further adjustments (e.g. first time log in password change, email confirmation etc.). Click create.
129 | 
130 | 3. Go to `Users` and select newly create user.
131 | Go to `Credentials tab` and add password by clicking `Set password`.
132 | Make sure to uncheck `Temporary` and `Save`.
133 | 
134 |
135 | ### 2.8. Adding roles to user
136 | > This step is just to inform you, how to add role to user. We don't have to do that because our role `user` is automatically assigned for every user.
137 |
138 | 1. Go to `Users` and select newly create user.
139 | 2. Go to `Role mapping` tab
140 | 
141 | 3. Click `Assign role`, choose your newly created role and hit `Assign`
142 | (here some random created role `teacher`)
143 | 
144 |
145 |
146 | ## 3. Spring Boot - configuration
147 |
148 | ### 3.1. Maven thing - *pom.xml*
149 | We just need basci stuff (postgres, lombok and spring).
150 | *pom.xml*
151 | ```xml
152 |
153 |
155 | 4.0.0
156 |
157 | org.springframework.boot
158 | spring-boot-starter-parent
159 | 2.7.5
160 |
161 |
162 | com.example
163 | spring-app
164 | 0.0.1-SNAPSHOT
165 | spring-app
166 | spring-app
167 |
168 | 11
169 |
170 |
171 |
172 | org.springframework.boot
173 | spring-boot-starter-data-jpa
174 |
175 |
176 | org.springframework.boot
177 | spring-boot-starter-security
178 |
179 |
180 | org.springframework.boot
181 | spring-boot-starter-validation
182 |
183 |
184 |
185 | org.postgresql
186 | postgresql
187 | runtime
188 |
189 |
190 | org.projectlombok
191 | lombok
192 | true
193 |
194 |
195 | org.springframework.boot
196 | spring-boot-starter-test
197 | test
198 |
199 |
200 | org.springframework.security
201 | spring-security-test
202 | test
203 |
204 |
205 | org.springframework.boot
206 | spring-boot-starter-web
207 |
208 |
209 |
210 |
211 | org.springframework.boot
212 | spring-boot-starter-oauth2-resource-server
213 |
214 |
215 |
216 |
217 |
218 |
219 | org.springframework.boot
220 | spring-boot-maven-plugin
221 |
222 |
223 |
224 | org.projectlombok
225 | lombok
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | ```
235 |
236 | ### 3.2. *application.properties*
237 | *application.properties*
238 | ```yaml
239 | # -- Spring Boot --
240 | server.port=8080
241 |
242 | # -- Postgresql database --
243 | spring.datasource.url=jdbc:postgresql://localhost:5432/ewizitingDB
244 | spring.datasource.username=admin
245 | spring.datasource.password=admin
246 | spring.jpa.hibernate.ddl-auto=create
247 |
248 | # -- Keycloak --
249 | spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8081/realms/test-app
250 | ```
251 |
252 | ### 3.3. SecurityConfig.java
253 |
254 | Basic spring security configuration.
255 | - `jwtAuthenticationConverterForKeycloak` - maps Keycloak roles (REALM and CLIENT level) to get them all with user info. By default, spring doesn't retrieve realm? level roles.
256 | - `http.anonymous().disable();` - disable anonymous requests (not logged in users).
257 | - `http.authorizeRequests().anyRequest().hasRole("user");` - allow to use site only for logged users with role `user`.
258 | - `jwtAuthUserFilterBean()`, `http.addFilterAfter(jwtAuthUserFilterBean(), SwitchUserFilter.class);` - in my case I need a 'brief' user database to be able to (create relation) link user with other table in Spring Boot e.g. users are teachers and I link them with classes. To be able to do that, i need `Users` table. For that matter, filter synchronizes logged user with Spring Boot user table.
259 |
260 | *SecurityConfig.java*
261 | ```java
262 | package com.example.springapp.security;
263 |
264 | import lombok.RequiredArgsConstructor;
265 | import lombok.extern.slf4j.Slf4j;
266 | import org.springframework.context.annotation.Bean;
267 | import org.springframework.context.annotation.Configuration;
268 | import org.springframework.core.convert.converter.Converter;
269 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
270 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
271 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
272 | import org.springframework.security.config.http.SessionCreationPolicy;
273 | import org.springframework.security.core.GrantedAuthority;
274 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
275 | import org.springframework.security.oauth2.jwt.Jwt;
276 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
277 | import org.springframework.security.web.SecurityFilterChain;
278 | import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
279 |
280 | import java.util.Collection;
281 | import java.util.Map;
282 | import java.util.stream.Collectors;
283 |
284 | @Configuration
285 | @EnableWebSecurity
286 | @EnableGlobalMethodSecurity(prePostEnabled = true)
287 | @RequiredArgsConstructor
288 | @Slf4j
289 | public class SecurityConfig {
290 |
291 | /**
292 | * Map Keycloak roles (REALM and CLIENT level) to get them all
293 | * */
294 | @Bean
295 | public JwtAuthenticationConverter jwtAuthenticationConverterForKeycloak() {
296 |
297 | log.warn("CONVERT");
298 |
299 | Converter> jwtGrantedAuthoritiesConverter = jwt -> {
300 | Map> realmAccess = jwt.getClaim("realm_access");
301 | Collection roles = realmAccess.get("roles");
302 | return roles.stream()
303 | .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
304 | .collect(Collectors.toList());
305 | };
306 |
307 | var jwtAuthenticationConverter = new JwtAuthenticationConverter();
308 | jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
309 |
310 | return jwtAuthenticationConverter;
311 | }
312 |
313 | /**
314 | * Sync local minimal user database with Keycloak db
315 | * */
316 | @Bean
317 | public JwtUserSyncFilter jwtAuthUserFilterBean() {
318 | return new JwtUserSyncFilter();
319 | }
320 |
321 | /**
322 | * Configure security
323 | * */
324 | @Bean
325 | public SecurityFilterChain configure(HttpSecurity http) throws Exception {
326 | http.csrf().disable();
327 | http.cors().disable();
328 | http.anonymous().disable();
329 |
330 | http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
331 | http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverterForKeycloak());
332 |
333 | http.authorizeRequests()
334 | .anyRequest()
335 | .hasRole("user");
336 |
337 | http.addFilterAfter(jwtAuthUserFilterBean(), SwitchUserFilter.class);
338 |
339 | return http.build();
340 | }
341 |
342 | }
343 | ```
344 |
345 | ### 3.4. User.java - `User` table
346 | Stores basic user info retrieved from token.
347 | *User.java*
348 | ```java
349 | package com.example.springapp.entity;
350 |
351 | import lombok.*;
352 | import javax.persistence.*;
353 |
354 | @Entity
355 | @Table(name = "USER_")
356 | @Setter @Getter
357 | @Builder
358 | @AllArgsConstructor
359 | @NoArgsConstructor
360 | public class User {
361 |
362 | @Id
363 | @Column
364 | private String email;
365 |
366 | @Column
367 | private String firstname;
368 |
369 | @Column
370 | private String lastname;
371 |
372 | @Column
373 | @Enumerated(EnumType.STRING)
374 | private Gender gender;
375 |
376 | public enum Gender {
377 | MALE, FEMALE
378 | }
379 |
380 | }
381 | ```
382 |
383 | ### 3.5. UserService.java
384 | Manages `User` table.
385 |
386 | *UserService.java*
387 | ```java
388 | package com.example.springapp.service;
389 |
390 | import com.example.springapp.entity.User;
391 | import com.example.springapp.repository.UserRepository;
392 | import lombok.RequiredArgsConstructor;
393 | import org.springframework.security.core.context.SecurityContextHolder;
394 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
395 | import org.springframework.stereotype.Service;
396 |
397 | import javax.persistence.EntityNotFoundException;
398 | import java.util.Optional;
399 |
400 | @Service
401 | @RequiredArgsConstructor
402 | public class UserService {
403 |
404 | private final UserRepository userRepository;
405 |
406 | public User getLoggedUser() {
407 | JwtAuthenticationToken token = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
408 |
409 | String email = String.valueOf(token.getTokenAttributes().get("email"));
410 | User user = userRepository.findByEmail(email).orElseThrow(() -> new EntityNotFoundException("Error while fetching user"));
411 |
412 | return user;
413 | }
414 |
415 | public void syncUser(User user) {
416 | if (user == null) {
417 | throw new EntityNotFoundException("Error while user sync");
418 | }
419 |
420 | User saveUser = user;
421 | Optional optionalUser = userRepository.findByEmail(user.getEmail());
422 |
423 | if (optionalUser.isPresent()) {
424 | saveUser = optionalUser.get();
425 | saveUser.setFirstname(user.getFirstname());
426 | saveUser.setLastname(user.getLastname());
427 | }
428 |
429 | userRepository.save(saveUser);
430 | }
431 |
432 | }
433 | ```
434 |
435 | ### 3.6. JwtUserSyncFilter.java
436 | Retrieve data from token and sync local user table.
437 |
438 | *JwtUserSyncFilter.java*
439 | ```java
440 | package com.example.springapp.security;
441 |
442 | import com.example.springapp.entity.User;
443 | import com.example.springapp.service.UserService;
444 | import org.springframework.beans.factory.annotation.Autowired;
445 | import org.springframework.security.core.context.SecurityContextHolder;
446 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
447 | import org.springframework.web.filter.OncePerRequestFilter;
448 |
449 | import javax.servlet.FilterChain;
450 | import javax.servlet.ServletException;
451 | import javax.servlet.http.HttpServletRequest;
452 | import javax.servlet.http.HttpServletResponse;
453 | import java.io.IOException;
454 |
455 |
456 | public class JwtUserSyncFilter extends OncePerRequestFilter {
457 |
458 | @Autowired
459 | private UserService userService;
460 |
461 | @Override
462 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
463 |
464 | try {
465 | JwtAuthenticationToken token = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
466 |
467 | String firstname = String.valueOf(token.getTokenAttributes().get("given_name"));
468 | String lastname = String.valueOf(token.getTokenAttributes().get("family_name"));
469 | String email = String.valueOf(token.getTokenAttributes().get("email"));
470 | User.Gender gender = token.getTokenAttributes().get("gender") == null ? null :
471 | User.Gender.valueOf(String.valueOf(token.getTokenAttributes().get("gender")).toUpperCase());
472 |
473 | User user = User.builder()
474 | .firstname(firstname)
475 | .lastname(lastname)
476 | .email(email)
477 | .gender(gender)
478 | .build();
479 |
480 | userService.syncUser(user);
481 | } catch (Exception e) {
482 | throw new IllegalArgumentException("Unable to auth user");
483 | }
484 |
485 | filterChain.doFilter(request, response);
486 | }
487 |
488 | }
489 | ```
490 |
491 | ### 3.7. WebController.java
492 | Controller for demonstration purposes.
493 | - `getUserInfo1()` - gets user info from synced `User` Spring Boot database
494 | - `getUserInfo2()` - gets user info directly from the token
495 | > We need to add `@CrossOrigin(origins = "http://localhost:4200")` to be able to use endpoint from Angular frontend.
496 |
497 | *WebController.java*
498 | ```java
499 | package com.example.springapp.controller;
500 |
501 | import com.example.springapp.entity.User;
502 | import com.example.springapp.service.UserService;
503 | import lombok.RequiredArgsConstructor;
504 | import org.slf4j.Logger;
505 | import org.slf4j.LoggerFactory;
506 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
507 | import org.springframework.web.bind.annotation.CrossOrigin;
508 | import org.springframework.web.bind.annotation.GetMapping;
509 | import org.springframework.web.bind.annotation.RestController;
510 |
511 |
512 | @RestController
513 | @CrossOrigin(origins = "http://localhost:4200")
514 | @RequiredArgsConstructor
515 | public class WebController {
516 |
517 | private static final Logger logger = LoggerFactory.getLogger(WebController.class);
518 |
519 | private final UserService userService;
520 |
521 | @GetMapping(path = "/userInfo1")
522 | public String getUserInfo1() {
523 | User user = userService.getLoggedUser();
524 |
525 | return "UserInfo1: " + user.getFirstname() + " " + user.getLastname() + ", " + user.getEmail();
526 | }
527 |
528 | @GetMapping("/userInfo2")
529 | public String getUserInfo2(JwtAuthenticationToken auth) {
530 | String firstname = auth.getTokenAttributes().get("given_name").toString();
531 | String lastname = auth.getTokenAttributes().get("family_name").toString();
532 | String email = auth.getTokenAttributes().get("email").toString();
533 | String authorities = auth.getAuthorities().toString();
534 |
535 | return "UserInfo2: " + firstname + " " + lastname + ", " + email + ", " + authorities ;
536 | }
537 |
538 | }
539 | ```
540 |
541 | ## 4. Angular - configuration
542 |
543 | ### 4.0. Open your Angular project or create new
544 | Create new project using:
545 |
546 | ```
547 | ng new angular-app
548 | ```
549 |
550 | While creating project make sure you use routing.
551 |
552 | ### 4.1. Install dependencies
553 | ```
554 | npm install keycloak-angular --save
555 | npm install keycloak-js --save
556 | ```
557 |
558 | ### 4.2. Configure proxy to avoid any CORS policy
559 | Every call made to `http://localhost:4200/api`, will be redirected to our Spring Boot application `http:localhost:8080`.
560 |
561 | 1. Create file `proxy.conf.json` inside project directory
562 | ```json
563 | {
564 | "/api": {
565 | "target": "http://localhost:8080",
566 | "secure": false,
567 | "pathRewrite": {"^/api" : ""}
568 | }
569 | }
570 | ```
571 |
572 | 2. Edit `package.json` (or start script using this command by hand)
573 | ```
574 | "start": "ng serve --proxy-config proxy.conf.json",
575 | ```
576 |
577 | ### 4.3. Config Keycloak using `enviroment`
578 | Update enviroment file.
579 |
580 | *src/enviroments/enviroment.ts*
581 | ```typescript
582 | export const environment = {
583 | production: false,
584 | apiUrl: '/api',
585 | keycloak: {
586 | // Keycloak url
587 | issuer: 'http://localhost:8081',
588 | // Realm
589 | realm: 'test-app',
590 | clientId: 'frontend'
591 | },
592 | };
593 | ```
594 |
595 | ### 4.4. Initialize Keycloak service
596 | In order to make sure Keycloak is initialized you will have to add an APP_INITIALIZER provider to your AppModule.
597 | This provider will call the initializeKeycloak function shown below which will set up the Keycloak service.
598 |
599 | 1. Create folder and file *utils/app-init.ts*
600 |
601 | *utils/app-init.ts*
602 | ```typescript
603 | import { KeycloakService } from "keycloak-angular";
604 | import { environment } from "../environments/environment";
605 |
606 | export function initializer(keycloak: KeycloakService): () => Promise {
607 | return (): Promise => {
608 | return new Promise(async (resolve, reject) => {
609 | try {
610 | await keycloak.init({
611 | config: {
612 | url: environment.keycloak.issuer,
613 | realm: environment.keycloak.realm,
614 | clientId: environment.keycloak.clientId,
615 | },
616 | // If set a false you cannot get any information about the user (e.g. username)
617 | loadUserProfileAtStartUp: true,
618 | initOptions: {
619 | onLoad: 'login-required',
620 | checkLoginIframe: true,
621 | // silentCheckSsoRedirectUri: window.location.origin + '/assets/silent-check-sso.html',
622 | },
623 | // By default, the keycloak-angular library add 'Authorization: Bearer TOKEN' header to all http requests
624 | // Add here if u want to exclude urls (to not have that header)
625 | bearerExcludedUrls: ['/assets']
626 | });
627 |
628 | resolve(resolve);
629 | } catch (err) {
630 | reject(err);
631 | }
632 | });
633 | };
634 |
635 | }
636 | ```
637 |
638 | 2. Update *src/app/app.module.ts*
639 |
640 | *app.module.ts*
641 | ```typescript
642 | import {APP_INITIALIZER, NgModule} from '@angular/core';
643 | import { BrowserModule } from '@angular/platform-browser';
644 |
645 | import { AppComponent } from './app.component';
646 | import {initializer} from "../utils/app-init";
647 | import {KeycloakAngularModule, KeycloakService} from "keycloak-angular";
648 | import { AppRoutingModule } from './app-routing.module';
649 | import { AccessDeniedComponent } from './access-denied/access-denied.component';
650 | import { UserInfoComponent } from './user-info/user-info.component';
651 | import {HttpClientModule} from "@angular/common/http";
652 |
653 | @NgModule({
654 | declarations: [
655 | AppComponent,
656 | AccessDeniedComponent,
657 | UserInfoComponent
658 | ],
659 | imports: [
660 | BrowserModule,
661 | AppRoutingModule,
662 | KeycloakAngularModule,
663 | HttpClientModule
664 | ],
665 | providers: [
666 | {
667 | provide: APP_INITIALIZER,
668 | useFactory: initializer,
669 | deps: [ KeycloakService ],
670 | multi: true
671 | }
672 | ],
673 | bootstrap: [AppComponent]
674 | })
675 | export class AppModule { }
676 | ```
677 |
678 | ### 4.5. Protect pages using guard
679 | In order to make some pages available for logged users with enough roles, we will create guard and service for authorization purposes.
680 | Create folder *src/app/auth*.
681 |
682 | *src/app/auth/auth.guard.ts*
683 | ```typescript
684 | import {Injectable} from "@angular/core";
685 | import {ActivatedRouteSnapshot, Router, RouterStateSnapshot} from "@angular/router";
686 | import {KeycloakAuthGuard, KeycloakService} from "keycloak-angular";
687 | import Keycloak from "keycloak-js";
688 |
689 | @Injectable({
690 | providedIn: 'root'
691 | })
692 | export class AuthGuard extends KeycloakAuthGuard {
693 |
694 | constructor(protected override readonly router: Router,
695 | protected readonly keycloak: KeycloakService
696 | ) {
697 | super(router, keycloak);
698 | }
699 |
700 | public async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
701 |
702 | // If not logged, redirect to login page
703 | if (!this.authenticated) {
704 | await this.keycloak.login({
705 | redirectUri: window.location.origin + state.url
706 | });
707 | }
708 |
709 | // Get the roles from keycloak
710 | const keycloakRoles = this.roles;
711 |
712 | // Get the roles from app.routing.module.ts
713 | const requiredRoles = route.data['roles'];
714 |
715 | // If page doesn't need any extra roles
716 | if (!(requiredRoles instanceof Array) || requiredRoles.length == 0) {
717 | return true;
718 | }
719 |
720 | // Check whether user has role to visit page (check keycloak roles against app.routing.module.ts config)
721 | if (requiredRoles.every(role => keycloakRoles.includes(role))) {
722 | return true;
723 | }
724 |
725 | // If user doesn't have necessary roles, redirect to error page
726 | this.router.navigate(['access-denied']);
727 | return false;
728 | }
729 |
730 | }
731 | ```
732 |
733 | *src/app/auth/auth.service.ts*
734 | ```typescript
735 | import {Injectable} from "@angular/core";
736 | import {KeycloakService} from "keycloak-angular";
737 |
738 | @Injectable({
739 | providedIn: "root"
740 | })
741 | export class AuthService {
742 |
743 | constructor(private keycloakService: KeycloakService) { }
744 |
745 | public getUsername(): string {
746 | return this.keycloakService.getUsername();
747 | }
748 |
749 | public logout(): void {
750 | this.keycloakService.logout().then(() => this.keycloakService.clearToken());
751 | }
752 |
753 | }
754 | ```
755 |
756 | ### 4.6. Create and configure pages (components)
757 | 1. Create page for `access-denied` purposes.
758 | ```
759 | ng g c access-denied
760 | ````
761 |
762 | 2. Create page for `user-info`.
763 | ```
764 | ng g c user-info
765 | ````
766 |
767 | 3. Update *src/app/user-info/user-info.component.ts*
768 |
769 | *user-info.component.ts*
770 | ```typescript
771 | import { Component, OnInit } from '@angular/core';
772 | import {HttpClient} from "@angular/common/http";
773 | import {AuthService} from "../auth/auth.service";
774 | import {WebApiService} from "../api/web-api.service";
775 |
776 | @Component({
777 | selector: 'app-user-info',
778 | templateUrl: './user-info.component.html',
779 | styleUrls: ['./user-info.component.css']
780 | })
781 | export class UserInfoComponent implements OnInit {
782 |
783 | message: string = 'null';
784 |
785 | constructor(private authService: AuthService, private webApiService: WebApiService, private http: HttpClient) { }
786 |
787 | ngOnInit(): void {
788 | this.message = this.authService.getUsername();
789 |
790 | this.webApiService.getUserInfo().subscribe({
791 | next: data => {
792 | this.message += ", " + data;
793 | }, error: err => {
794 | console.log(err);
795 | }
796 | });
797 |
798 | }
799 |
800 | }
801 | ```
802 | 4. Update *src/app/user-info/user-info.component.html*
803 |
804 | *user-info.component.html*
805 | ```html
806 | Username: {{ message }}
807 | ```
808 |
809 | ### 4.7. Configure which pages should be protected
810 | Update *src/app/app-routing.module.ts*.
811 |
812 | *app-routing.module.ts*
813 | ```typescript
814 | import { NgModule } from '@angular/core';
815 | import { CommonModule } from '@angular/common';
816 | import {RouterModule, Routes} from "@angular/router";
817 | import {AccessDeniedComponent} from "./access-denied/access-denied.component";
818 | import {AuthGuard} from "./auth/auth.guard";
819 | import {UserInfoComponent} from "./user-info/user-info.component";
820 |
821 | const routes: Routes = [
822 | {
823 | path: 'access-denied',
824 | component: AccessDeniedComponent,
825 | canActivate: [AuthGuard]
826 | },
827 | {
828 | path: 'user-info',
829 | component: UserInfoComponent,
830 | canActivate: [AuthGuard],
831 | // The user need to have these roles to access page
832 | data: { roles: ['user'] }
833 | }
834 | ];
835 |
836 | @NgModule({
837 | imports: [RouterModule.forRoot(routes)],
838 | exports: [RouterModule]
839 | })
840 | export class AppRoutingModule { }
841 | ```
842 |
843 | ### 4.8. Update main page to show load components
844 | Update *src/app/app.component.ts* and *src/app/app.component.html*.
845 |
846 | *app.component.ts*
847 | ```typescript
848 | import { Component } from '@angular/core';
849 | import {AuthService} from "./auth/auth.service";
850 |
851 | @Component({
852 | selector: 'app-root',
853 | templateUrl: './app.component.html',
854 | styleUrls: ['./app.component.css']
855 | })
856 | export class AppComponent {
857 | title = 'angular-app';
858 |
859 | constructor(private authService: AuthService) {
860 | }
861 |
862 | public logout() {
863 | this.authService.logout();
864 | }
865 |
866 | }
867 | ```
868 |
869 | *app.component.html*
870 | ```html
871 |
872 |
873 |
874 | ```
875 |
876 | ## 5. That's it
877 | Start Spring Boot and Angular. Go to http://localhost:4200 and you sould see Keycloak login/register page.
878 |
--------------------------------------------------------------------------------
/angular-app/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # For the full list of supported browsers by the Angular framework, please see:
6 | # https://angular.io/guide/browser-support
7 |
8 | # You can see what browsers were selected by your queries by running:
9 | # npx browserslist
10 |
11 | last 1 Chrome version
12 | last 1 Firefox version
13 | last 2 Edge major versions
14 | last 2 Safari major versions
15 | last 2 iOS major versions
16 | Firefox ESR
17 |
--------------------------------------------------------------------------------
/angular-app/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://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 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/angular-app/.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 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/angular-app/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3 | "recommendations": ["angular.ng-template"]
4 | }
5 |
--------------------------------------------------------------------------------
/angular-app/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "name": "ng serve",
7 | "type": "pwa-chrome",
8 | "request": "launch",
9 | "preLaunchTask": "npm: start",
10 | "url": "http://localhost:4200/"
11 | },
12 | {
13 | "name": "ng test",
14 | "type": "chrome",
15 | "request": "launch",
16 | "preLaunchTask": "npm: test",
17 | "url": "http://localhost:9876/debug.html"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/angular-app/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "type": "npm",
7 | "script": "start",
8 | "isBackground": true,
9 | "problemMatcher": {
10 | "owner": "typescript",
11 | "pattern": "$tsc",
12 | "background": {
13 | "activeOnStart": true,
14 | "beginsPattern": {
15 | "regexp": "(.*?)"
16 | },
17 | "endsPattern": {
18 | "regexp": "bundle generation complete"
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "type": "npm",
25 | "script": "test",
26 | "isBackground": true,
27 | "problemMatcher": {
28 | "owner": "typescript",
29 | "pattern": "$tsc",
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": {
33 | "regexp": "(.*?)"
34 | },
35 | "endsPattern": {
36 | "regexp": "bundle generation complete"
37 | }
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/angular-app/README.md:
--------------------------------------------------------------------------------
1 | # AngularApp
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.9.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application 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.
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 a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
28 |
--------------------------------------------------------------------------------
/angular-app/angular-app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/angular-app/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "angular-app": {
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/angular-app",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "polyfills": "src/polyfills.ts",
20 | "tsConfig": "tsconfig.app.json",
21 | "assets": [
22 | "src/favicon.ico",
23 | "src/assets"
24 | ],
25 | "styles": [
26 | "src/styles.css"
27 | ],
28 | "scripts": []
29 | },
30 | "configurations": {
31 | "production": {
32 | "budgets": [
33 | {
34 | "type": "initial",
35 | "maximumWarning": "500kb",
36 | "maximumError": "1mb"
37 | },
38 | {
39 | "type": "anyComponentStyle",
40 | "maximumWarning": "2kb",
41 | "maximumError": "4kb"
42 | }
43 | ],
44 | "fileReplacements": [
45 | {
46 | "replace": "src/environments/environment.ts",
47 | "with": "src/environments/environment.prod.ts"
48 | }
49 | ],
50 | "outputHashing": "all"
51 | },
52 | "development": {
53 | "buildOptimizer": false,
54 | "optimization": false,
55 | "vendorChunk": true,
56 | "extractLicenses": false,
57 | "sourceMap": true,
58 | "namedChunks": true
59 | }
60 | },
61 | "defaultConfiguration": "production"
62 | },
63 | "serve": {
64 | "builder": "@angular-devkit/build-angular:dev-server",
65 | "configurations": {
66 | "production": {
67 | "browserTarget": "angular-app:build:production"
68 | },
69 | "development": {
70 | "browserTarget": "angular-app:build:development"
71 | }
72 | },
73 | "defaultConfiguration": "development"
74 | },
75 | "extract-i18n": {
76 | "builder": "@angular-devkit/build-angular:extract-i18n",
77 | "options": {
78 | "browserTarget": "angular-app:build"
79 | }
80 | },
81 | "test": {
82 | "builder": "@angular-devkit/build-angular:karma",
83 | "options": {
84 | "main": "src/test.ts",
85 | "polyfills": "src/polyfills.ts",
86 | "tsConfig": "tsconfig.spec.json",
87 | "karmaConfig": "karma.conf.js",
88 | "assets": [
89 | "src/favicon.ico",
90 | "src/assets"
91 | ],
92 | "styles": [
93 | "src/styles.css"
94 | ],
95 | "scripts": []
96 | }
97 | }
98 | }
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/angular-app/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'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | jasmineHtmlReporter: {
25 | suppressAll: true // removes the duplicated traces
26 | },
27 | coverageReporter: {
28 | dir: require('path').join(__dirname, './coverage/angular-app'),
29 | subdir: '.',
30 | reporters: [
31 | { type: 'html' },
32 | { type: 'text-summary' }
33 | ]
34 | },
35 | reporters: ['progress', 'kjhtml'],
36 | port: 9876,
37 | colors: true,
38 | logLevel: config.LOG_INFO,
39 | autoWatch: true,
40 | browsers: ['Chrome'],
41 | singleRun: false,
42 | restartOnFileChange: true
43 | });
44 | };
45 |
--------------------------------------------------------------------------------
/angular-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-app",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve --proxy-config proxy.conf.json",
7 | "build": "ng build",
8 | "watch": "ng build --watch --configuration development",
9 | "test": "ng test"
10 | },
11 | "private": true,
12 | "dependencies": {
13 | "-": "^0.0.1",
14 | "@angular/animations": "^14.2.0",
15 | "@angular/common": "^14.2.0",
16 | "@angular/compiler": "^14.2.0",
17 | "@angular/core": "^14.2.0",
18 | "@angular/forms": "^14.2.0",
19 | "@angular/platform-browser": "^14.2.0",
20 | "@angular/platform-browser-dynamic": "^14.2.0",
21 | "@angular/router": "^14.2.0",
22 | "keycloak-angular": "^12.1.0",
23 | "keycloak-js": "^20.0.1",
24 | "rxjs": "~7.5.0",
25 | "save": "^2.9.0",
26 | "tslib": "^2.3.0",
27 | "zone.js": "~0.11.4"
28 | },
29 | "devDependencies": {
30 | "@angular-devkit/build-angular": "^14.2.9",
31 | "@angular/cli": "~14.2.9",
32 | "@angular/compiler-cli": "^14.2.0",
33 | "@types/jasmine": "~4.0.0",
34 | "jasmine-core": "~4.3.0",
35 | "karma": "~6.4.0",
36 | "karma-chrome-launcher": "~3.1.0",
37 | "karma-coverage": "~2.2.0",
38 | "karma-jasmine": "~5.1.0",
39 | "karma-jasmine-html-reporter": "~2.0.0",
40 | "typescript": "~4.7.2"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/angular-app/proxy.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api": {
3 | "target": "http://localhost:8080",
4 | "secure": false,
5 | "pathRewrite": {"^/api" : ""}
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/angular-app/src/app/access-denied/access-denied.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/angular-app/src/app/access-denied/access-denied.component.css
--------------------------------------------------------------------------------
/angular-app/src/app/access-denied/access-denied.component.html:
--------------------------------------------------------------------------------
1 | access-denied works!
2 |
--------------------------------------------------------------------------------
/angular-app/src/app/access-denied/access-denied.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AccessDeniedComponent } from './access-denied.component';
4 |
5 | describe('AccessDeniedComponent', () => {
6 | let component: AccessDeniedComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ AccessDeniedComponent ]
12 | })
13 | .compileComponents();
14 |
15 | fixture = TestBed.createComponent(AccessDeniedComponent);
16 | component = fixture.componentInstance;
17 | fixture.detectChanges();
18 | });
19 |
20 | it('should create', () => {
21 | expect(component).toBeTruthy();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/angular-app/src/app/access-denied/access-denied.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-access-denied',
5 | templateUrl: './access-denied.component.html',
6 | styleUrls: ['./access-denied.component.css']
7 | })
8 | export class AccessDeniedComponent implements OnInit {
9 |
10 | constructor() { }
11 |
12 | ngOnInit(): void {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/angular-app/src/app/api/web-api.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import {HttpClient} from "@angular/common/http";
3 | import {environment} from "../../environments/environment";
4 | import {Observable} from "rxjs";
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class WebApiService {
10 |
11 | constructor(private http: HttpClient) { }
12 |
13 | public getUserInfo(): Observable {
14 | return this.http.get(`${environment.apiUrl}/userInfo1`, {responseType:'text'});
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/angular-app/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import {RouterModule, Routes} from "@angular/router";
4 | import {AccessDeniedComponent} from "./access-denied/access-denied.component";
5 | import {AuthGuard} from "./auth/auth.guard";
6 | import {UserInfoComponent} from "./user-info/user-info.component";
7 |
8 | const routes: Routes = [
9 | {
10 | path: 'access-denied',
11 | component: AccessDeniedComponent,
12 | canActivate: [AuthGuard]
13 | },
14 | {
15 | path: 'user-info',
16 | component: UserInfoComponent,
17 | canActivate: [AuthGuard],
18 | // The user need to have these roles to access page
19 | data: { roles: ['user'] }
20 | }
21 | ];
22 |
23 | @NgModule({
24 | imports: [RouterModule.forRoot(routes)],
25 | exports: [RouterModule]
26 | })
27 | export class AppRoutingModule { }
28 |
--------------------------------------------------------------------------------
/angular-app/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/angular-app/src/app/app.component.css
--------------------------------------------------------------------------------
/angular-app/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/angular-app/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | beforeEach(async () => {
6 | await TestBed.configureTestingModule({
7 | declarations: [
8 | AppComponent
9 | ],
10 | }).compileComponents();
11 | });
12 |
13 | it('should create the app', () => {
14 | const fixture = TestBed.createComponent(AppComponent);
15 | const app = fixture.componentInstance;
16 | expect(app).toBeTruthy();
17 | });
18 |
19 | it(`should have as title 'angular-app'`, () => {
20 | const fixture = TestBed.createComponent(AppComponent);
21 | const app = fixture.componentInstance;
22 | expect(app.title).toEqual('angular-app');
23 | });
24 |
25 | it('should render title', () => {
26 | const fixture = TestBed.createComponent(AppComponent);
27 | fixture.detectChanges();
28 | const compiled = fixture.nativeElement as HTMLElement;
29 | expect(compiled.querySelector('.content span')?.textContent).toContain('angular-app app is running!');
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/angular-app/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import {AuthService} from "./auth/auth.service";
3 |
4 | @Component({
5 | selector: 'app-root',
6 | templateUrl: './app.component.html',
7 | styleUrls: ['./app.component.css']
8 | })
9 | export class AppComponent {
10 | title = 'angular-app';
11 |
12 | constructor(private authService: AuthService) {
13 | }
14 |
15 | public logout() {
16 | this.authService.logout();
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/angular-app/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import {APP_INITIALIZER, NgModule} from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 |
4 | import { AppComponent } from './app.component';
5 | import {initializer} from "../utils/app-init";
6 | import {KeycloakAngularModule, KeycloakService} from "keycloak-angular";
7 | import { AppRoutingModule } from './app-routing.module';
8 | import { AccessDeniedComponent } from './access-denied/access-denied.component';
9 | import { UserInfoComponent } from './user-info/user-info.component';
10 | import {HttpClientModule} from "@angular/common/http";
11 |
12 | @NgModule({
13 | declarations: [
14 | AppComponent,
15 | AccessDeniedComponent,
16 | UserInfoComponent
17 | ],
18 | imports: [
19 | BrowserModule,
20 | AppRoutingModule,
21 | KeycloakAngularModule,
22 | HttpClientModule
23 | ],
24 | providers: [
25 | {
26 | provide: APP_INITIALIZER,
27 | useFactory: initializer,
28 | deps: [ KeycloakService ],
29 | multi: true
30 | }
31 | ],
32 | bootstrap: [AppComponent]
33 | })
34 | export class AppModule { }
35 |
--------------------------------------------------------------------------------
/angular-app/src/app/auth/auth.guard.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from "@angular/core";
2 | import {ActivatedRouteSnapshot, Router, RouterStateSnapshot} from "@angular/router";
3 | import {KeycloakAuthGuard, KeycloakService} from "keycloak-angular";
4 | import Keycloak from "keycloak-js";
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class AuthGuard extends KeycloakAuthGuard {
10 |
11 | constructor(protected override readonly router: Router,
12 | protected readonly keycloak: KeycloakService
13 | ) {
14 | super(router, keycloak);
15 | }
16 |
17 | public async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
18 |
19 | // If not logged, redirect to login page
20 | if (!this.authenticated) {
21 | await this.keycloak.login({
22 | redirectUri: window.location.origin + state.url
23 | });
24 | }
25 |
26 | // Get the roles from keycloak
27 | const keycloakRoles = this.roles;
28 |
29 | // Get the roles from app.routing.module.ts
30 | const requiredRoles = route.data['roles'];
31 |
32 | // If page doesn't need any extra roles
33 | if (!(requiredRoles instanceof Array) || requiredRoles.length == 0) {
34 | return true;
35 | }
36 |
37 | // Check whether user has role to visit page (check keycloak roles against app.routing.module.ts config)
38 | if (requiredRoles.every(role => keycloakRoles.includes(role))) {
39 | return true;
40 | }
41 |
42 | // If user doesn't have necessary roles, redirect to error page
43 | this.router.navigate(['access-denied']);
44 | return false;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/angular-app/src/app/auth/auth.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from "@angular/core";
2 | import {KeycloakService} from "keycloak-angular";
3 |
4 | @Injectable({
5 | providedIn: "root"
6 | })
7 | export class AuthService {
8 |
9 | constructor(private keycloakService: KeycloakService) { }
10 |
11 | public getUsername(): string {
12 | return this.keycloakService.getUsername();
13 | }
14 |
15 | public logout(): void {
16 | this.keycloakService.logout().then(() => this.keycloakService.clearToken());
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/angular-app/src/app/user-info/user-info.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/angular-app/src/app/user-info/user-info.component.css
--------------------------------------------------------------------------------
/angular-app/src/app/user-info/user-info.component.html:
--------------------------------------------------------------------------------
1 | Username: {{ message }}
2 |
--------------------------------------------------------------------------------
/angular-app/src/app/user-info/user-info.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {HttpClient} from "@angular/common/http";
3 | import {AuthService} from "../auth/auth.service";
4 | import {WebApiService} from "../api/web-api.service";
5 |
6 | @Component({
7 | selector: 'app-user-info',
8 | templateUrl: './user-info.component.html',
9 | styleUrls: ['./user-info.component.css']
10 | })
11 | export class UserInfoComponent implements OnInit {
12 |
13 | message: string = 'null';
14 |
15 | constructor(private authService: AuthService, private webApiService: WebApiService, private http: HttpClient) { }
16 |
17 | ngOnInit(): void {
18 | this.message = this.authService.getUsername();
19 |
20 | this.webApiService.getUserInfo().subscribe({
21 | next: data => {
22 | this.message += ", " + data;
23 | }, error: err => {
24 | console.log(err);
25 | }
26 | });
27 |
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/angular-app/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/angular-app/src/assets/.gitkeep
--------------------------------------------------------------------------------
/angular-app/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/angular-app/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build` 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 | apiUrl: '/api',
8 | keycloak: {
9 | // Url of the Identity Provider
10 | issuer: 'http://localhost:8081',
11 | // Realm
12 | realm: 'test-app',
13 | clientId: 'frontend'
14 | },
15 | };
16 |
17 | /*
18 | * For easier debugging in development mode, you can import the following file
19 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
20 | *
21 | * This import should be commented out in production mode because it will have a negative impact
22 | * on performance if an error is thrown.
23 | */
24 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
25 |
--------------------------------------------------------------------------------
/angular-app/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/angular-app/src/favicon.ico
--------------------------------------------------------------------------------
/angular-app/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AngularApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/angular-app/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/angular-app/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 recent versions of Safari, Chrome (including
12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/angular-app/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/angular-app/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/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: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | (id: string): T;
13 | keys(): string[];
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(),
21 | );
22 |
23 | // Then we find all the tests.
24 | const context = require.context('./', true, /\.spec\.ts$/);
25 | // And load the modules.
26 | context.keys().forEach(context);
27 |
--------------------------------------------------------------------------------
/angular-app/src/utils/app-init.ts:
--------------------------------------------------------------------------------
1 | import { KeycloakService } from "keycloak-angular";
2 | import { environment } from "../environments/environment";
3 |
4 | export function initializer(keycloak: KeycloakService): () => Promise {
5 | return (): Promise => {
6 | return new Promise(async (resolve, reject) => {
7 | try {
8 | await keycloak.init({
9 | config: {
10 | url: environment.keycloak.issuer,
11 | realm: environment.keycloak.realm,
12 | clientId: environment.keycloak.clientId,
13 | },
14 | // If set a false you cannot get any information about the user (e.g. username)
15 | loadUserProfileAtStartUp: true,
16 | initOptions: {
17 | // This is an action we specified on keycloak load
18 | // We have two options : 'login-required'|'check-sso'
19 | // If is set to 'login-required' this means your browser will do a full redirect to the Keycloak server and back to your application.
20 | // If is set to 'check-sso' instead this action will be performed in a hidden iframe, so your application resources only need to be loaded and parsed once by the browser.
21 | // Then you will need to add the silentCheckSsoRedirectUri and create a html file silent-check-sso.html with this content
22 | //
23 | //
24 | //
27 | //
28 | //
29 | onLoad: 'login-required',
30 | checkLoginIframe: true,
31 | // silentCheckSsoRedirectUri: window.location.origin + '/assets/silent-check-sso.html',
32 | },
33 | // By default, the keycloak-angular library add 'Authorization: Bearer TOKEN' header to all http requests
34 | // Add here if u want to exclude urls (to not have that header)
35 | bearerExcludedUrls: ['/assets']
36 | });
37 |
38 | resolve(resolve);
39 | } catch (err) {
40 | reject(err);
41 | }
42 | });
43 | };
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/angular-app/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 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/angular-app/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": "es2020",
20 | "module": "es2020",
21 | "lib": [
22 | "es2020",
23 | "dom"
24 | ]
25 | },
26 | "angularCompilerOptions": {
27 | "enableI18nLegacyMessageIdFormat": false,
28 | "strictInjectionParameters": true,
29 | "strictInputAccessModifiers": true,
30 | "strictTemplates": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/angular-app/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 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 | services:
3 |
4 | postgresql:
5 | container_name: postgres
6 | image: postgres:14.4
7 | restart: always
8 | ports:
9 | - "5432:5432" # External port access for the development profile
10 | environment:
11 | POSTGRES_PASSWORD: admin
12 | POSTGRES_USER: admin
13 | POSTGRES_DB: keycloak
14 |
15 | pgadmin:
16 | container_name: pgadmin
17 | image: dpage/pgadmin4:latest
18 | restart: always
19 | ports:
20 | - "81:80"
21 | environment:
22 | PGADMIN_DEFAULT_EMAIL: admin@admin.com
23 | PGADMIN_DEFAULT_PASSWORD: admin
24 |
25 | keycloak:
26 | image: quay.io/keycloak/keycloak:20.0.0
27 | environment:
28 | KC_DB: postgres
29 | KC_DB_URL: jdbc:postgresql://postgresql:5432/keycloak
30 | KC_DB_USERNAME: admin
31 | KC_DB_PASSWORD: admin
32 | KEYCLOAK_ADMIN: admin
33 | KEYCLOAK_ADMIN_PASSWORD: admin
34 | # Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.
35 | #JDBC_PARAMS: "ssl=true"
36 | ports:
37 | - "8081:8081"
38 | command: [ "start-dev", "--http-port=8081" ]
39 | depends_on:
40 | - postgres
41 |
--------------------------------------------------------------------------------
/docs/aimg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg.png
--------------------------------------------------------------------------------
/docs/aimg_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_1.png
--------------------------------------------------------------------------------
/docs/aimg_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_10.png
--------------------------------------------------------------------------------
/docs/aimg_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_11.png
--------------------------------------------------------------------------------
/docs/aimg_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_12.png
--------------------------------------------------------------------------------
/docs/aimg_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_13.png
--------------------------------------------------------------------------------
/docs/aimg_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_14.png
--------------------------------------------------------------------------------
/docs/aimg_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_2.png
--------------------------------------------------------------------------------
/docs/aimg_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_3.png
--------------------------------------------------------------------------------
/docs/aimg_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_4.png
--------------------------------------------------------------------------------
/docs/aimg_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_5.png
--------------------------------------------------------------------------------
/docs/aimg_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_6.png
--------------------------------------------------------------------------------
/docs/aimg_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_7.png
--------------------------------------------------------------------------------
/docs/aimg_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_8.png
--------------------------------------------------------------------------------
/docs/aimg_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/aimg_9.png
--------------------------------------------------------------------------------
/docs/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img.png
--------------------------------------------------------------------------------
/docs/img_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_1.png
--------------------------------------------------------------------------------
/docs/img_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_10.png
--------------------------------------------------------------------------------
/docs/img_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_11.png
--------------------------------------------------------------------------------
/docs/img_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_12.png
--------------------------------------------------------------------------------
/docs/img_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_13.png
--------------------------------------------------------------------------------
/docs/img_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_14.png
--------------------------------------------------------------------------------
/docs/img_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_15.png
--------------------------------------------------------------------------------
/docs/img_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_16.png
--------------------------------------------------------------------------------
/docs/img_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_2.png
--------------------------------------------------------------------------------
/docs/img_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_3.png
--------------------------------------------------------------------------------
/docs/img_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_4.png
--------------------------------------------------------------------------------
/docs/img_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_5.png
--------------------------------------------------------------------------------
/docs/img_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_6.png
--------------------------------------------------------------------------------
/docs/img_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_7.png
--------------------------------------------------------------------------------
/docs/img_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_8.png
--------------------------------------------------------------------------------
/docs/img_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/docs/img_9.png
--------------------------------------------------------------------------------
/spring-app/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/spring-app/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heytechv/springboot-keycloak-angular/0299b01008eafc43b952c9b6d12f27962ab2ea02/spring-app/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/spring-app/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
3 |
--------------------------------------------------------------------------------
/spring-app/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # https://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /usr/local/etc/mavenrc ] ; then
40 | . /usr/local/etc/mavenrc
41 | fi
42 |
43 | if [ -f /etc/mavenrc ] ; then
44 | . /etc/mavenrc
45 | fi
46 |
47 | if [ -f "$HOME/.mavenrc" ] ; then
48 | . "$HOME/.mavenrc"
49 | fi
50 |
51 | fi
52 |
53 | # OS specific support. $var _must_ be set to either true or false.
54 | cygwin=false;
55 | darwin=false;
56 | mingw=false
57 | case "`uname`" in
58 | CYGWIN*) cygwin=true ;;
59 | MINGW*) mingw=true;;
60 | Darwin*) darwin=true
61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
63 | if [ -z "$JAVA_HOME" ]; then
64 | if [ -x "/usr/libexec/java_home" ]; then
65 | export JAVA_HOME="`/usr/libexec/java_home`"
66 | else
67 | export JAVA_HOME="/Library/Java/Home"
68 | fi
69 | fi
70 | ;;
71 | esac
72 |
73 | if [ -z "$JAVA_HOME" ] ; then
74 | if [ -r /etc/gentoo-release ] ; then
75 | JAVA_HOME=`java-config --jre-home`
76 | fi
77 | fi
78 |
79 | if [ -z "$M2_HOME" ] ; then
80 | ## resolve links - $0 may be a link to maven's home
81 | PRG="$0"
82 |
83 | # need this for relative symlinks
84 | while [ -h "$PRG" ] ; do
85 | ls=`ls -ld "$PRG"`
86 | link=`expr "$ls" : '.*-> \(.*\)$'`
87 | if expr "$link" : '/.*' > /dev/null; then
88 | PRG="$link"
89 | else
90 | PRG="`dirname "$PRG"`/$link"
91 | fi
92 | done
93 |
94 | saveddir=`pwd`
95 |
96 | M2_HOME=`dirname "$PRG"`/..
97 |
98 | # make it fully qualified
99 | M2_HOME=`cd "$M2_HOME" && pwd`
100 |
101 | cd "$saveddir"
102 | # echo Using m2 at $M2_HOME
103 | fi
104 |
105 | # For Cygwin, ensure paths are in UNIX format before anything is touched
106 | if $cygwin ; then
107 | [ -n "$M2_HOME" ] &&
108 | M2_HOME=`cygpath --unix "$M2_HOME"`
109 | [ -n "$JAVA_HOME" ] &&
110 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
111 | [ -n "$CLASSPATH" ] &&
112 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
113 | fi
114 |
115 | # For Mingw, ensure paths are in UNIX format before anything is touched
116 | if $mingw ; then
117 | [ -n "$M2_HOME" ] &&
118 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
119 | [ -n "$JAVA_HOME" ] &&
120 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
121 | fi
122 |
123 | if [ -z "$JAVA_HOME" ]; then
124 | javaExecutable="`which javac`"
125 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
126 | # readlink(1) is not available as standard on Solaris 10.
127 | readLink=`which readlink`
128 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
129 | if $darwin ; then
130 | javaHome="`dirname \"$javaExecutable\"`"
131 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
132 | else
133 | javaExecutable="`readlink -f \"$javaExecutable\"`"
134 | fi
135 | javaHome="`dirname \"$javaExecutable\"`"
136 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
137 | JAVA_HOME="$javaHome"
138 | export JAVA_HOME
139 | fi
140 | fi
141 | fi
142 |
143 | if [ -z "$JAVACMD" ] ; then
144 | if [ -n "$JAVA_HOME" ] ; then
145 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
146 | # IBM's JDK on AIX uses strange locations for the executables
147 | JAVACMD="$JAVA_HOME/jre/sh/java"
148 | else
149 | JAVACMD="$JAVA_HOME/bin/java"
150 | fi
151 | else
152 | JAVACMD="`\\unset -f command; \\command -v java`"
153 | fi
154 | fi
155 |
156 | if [ ! -x "$JAVACMD" ] ; then
157 | echo "Error: JAVA_HOME is not defined correctly." >&2
158 | echo " We cannot execute $JAVACMD" >&2
159 | exit 1
160 | fi
161 |
162 | if [ -z "$JAVA_HOME" ] ; then
163 | echo "Warning: JAVA_HOME environment variable is not set."
164 | fi
165 |
166 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
167 |
168 | # traverses directory structure from process work directory to filesystem root
169 | # first directory with .mvn subdirectory is considered project base directory
170 | find_maven_basedir() {
171 |
172 | if [ -z "$1" ]
173 | then
174 | echo "Path not specified to find_maven_basedir"
175 | return 1
176 | fi
177 |
178 | basedir="$1"
179 | wdir="$1"
180 | while [ "$wdir" != '/' ] ; do
181 | if [ -d "$wdir"/.mvn ] ; then
182 | basedir=$wdir
183 | break
184 | fi
185 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
186 | if [ -d "${wdir}" ]; then
187 | wdir=`cd "$wdir/.."; pwd`
188 | fi
189 | # end of workaround
190 | done
191 | echo "${basedir}"
192 | }
193 |
194 | # concatenates all lines of a file
195 | concat_lines() {
196 | if [ -f "$1" ]; then
197 | echo "$(tr -s '\n' ' ' < "$1")"
198 | fi
199 | }
200 |
201 | BASE_DIR=`find_maven_basedir "$(pwd)"`
202 | if [ -z "$BASE_DIR" ]; then
203 | exit 1;
204 | fi
205 |
206 | ##########################################################################################
207 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
208 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
209 | ##########################################################################################
210 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
211 | if [ "$MVNW_VERBOSE" = true ]; then
212 | echo "Found .mvn/wrapper/maven-wrapper.jar"
213 | fi
214 | else
215 | if [ "$MVNW_VERBOSE" = true ]; then
216 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
217 | fi
218 | if [ -n "$MVNW_REPOURL" ]; then
219 | jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
220 | else
221 | jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
222 | fi
223 | while IFS="=" read key value; do
224 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
225 | esac
226 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
227 | if [ "$MVNW_VERBOSE" = true ]; then
228 | echo "Downloading from: $jarUrl"
229 | fi
230 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
231 | if $cygwin; then
232 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
233 | fi
234 |
235 | if command -v wget > /dev/null; then
236 | if [ "$MVNW_VERBOSE" = true ]; then
237 | echo "Found wget ... using wget"
238 | fi
239 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
240 | wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
241 | else
242 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
243 | fi
244 | elif command -v curl > /dev/null; then
245 | if [ "$MVNW_VERBOSE" = true ]; then
246 | echo "Found curl ... using curl"
247 | fi
248 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
249 | curl -o "$wrapperJarPath" "$jarUrl" -f
250 | else
251 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
252 | fi
253 |
254 | else
255 | if [ "$MVNW_VERBOSE" = true ]; then
256 | echo "Falling back to using Java to download"
257 | fi
258 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
259 | # For Cygwin, switch paths to Windows format before running javac
260 | if $cygwin; then
261 | javaClass=`cygpath --path --windows "$javaClass"`
262 | fi
263 | if [ -e "$javaClass" ]; then
264 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
265 | if [ "$MVNW_VERBOSE" = true ]; then
266 | echo " - Compiling MavenWrapperDownloader.java ..."
267 | fi
268 | # Compiling the Java class
269 | ("$JAVA_HOME/bin/javac" "$javaClass")
270 | fi
271 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
272 | # Running the downloader
273 | if [ "$MVNW_VERBOSE" = true ]; then
274 | echo " - Running MavenWrapperDownloader.java ..."
275 | fi
276 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
277 | fi
278 | fi
279 | fi
280 | fi
281 | ##########################################################################################
282 | # End of extension
283 | ##########################################################################################
284 |
285 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
286 | if [ "$MVNW_VERBOSE" = true ]; then
287 | echo $MAVEN_PROJECTBASEDIR
288 | fi
289 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
290 |
291 | # For Cygwin, switch paths to Windows format before running java
292 | if $cygwin; then
293 | [ -n "$M2_HOME" ] &&
294 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
295 | [ -n "$JAVA_HOME" ] &&
296 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
297 | [ -n "$CLASSPATH" ] &&
298 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
299 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
300 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
301 | fi
302 |
303 | # Provide a "standardized" way to retrieve the CLI args that will
304 | # work with both Windows and non-Windows executions.
305 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
306 | export MAVEN_CMD_LINE_ARGS
307 |
308 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
309 |
310 | exec "$JAVACMD" \
311 | $MAVEN_OPTS \
312 | $MAVEN_DEBUG_OPTS \
313 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
314 | "-Dmaven.home=${M2_HOME}" \
315 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
316 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
317 |
--------------------------------------------------------------------------------
/spring-app/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM https://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM set title of command window
39 | title %0
40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
124 |
125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
127 | )
128 |
129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
131 | if exist %WRAPPER_JAR% (
132 | if "%MVNW_VERBOSE%" == "true" (
133 | echo Found %WRAPPER_JAR%
134 | )
135 | ) else (
136 | if not "%MVNW_REPOURL%" == "" (
137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
138 | )
139 | if "%MVNW_VERBOSE%" == "true" (
140 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
141 | echo Downloading from: %DOWNLOAD_URL%
142 | )
143 |
144 | powershell -Command "&{"^
145 | "$webclient = new-object System.Net.WebClient;"^
146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
148 | "}"^
149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
150 | "}"
151 | if "%MVNW_VERBOSE%" == "true" (
152 | echo Finished downloading %WRAPPER_JAR%
153 | )
154 | )
155 | @REM End of extension
156 |
157 | @REM Provide a "standardized" way to retrieve the CLI args that will
158 | @REM work with both Windows and non-Windows executions.
159 | set MAVEN_CMD_LINE_ARGS=%*
160 |
161 | %MAVEN_JAVA_EXE% ^
162 | %JVM_CONFIG_MAVEN_PROPS% ^
163 | %MAVEN_OPTS% ^
164 | %MAVEN_DEBUG_OPTS% ^
165 | -classpath %WRAPPER_JAR% ^
166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
168 | if ERRORLEVEL 1 goto error
169 | goto end
170 |
171 | :error
172 | set ERROR_CODE=1
173 |
174 | :end
175 | @endlocal & set ERROR_CODE=%ERROR_CODE%
176 |
177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
181 | :skipRcPost
182 |
183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause
185 |
186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
187 |
188 | cmd /C exit /B %ERROR_CODE%
189 |
--------------------------------------------------------------------------------
/spring-app/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.7.5
9 |
10 |
11 | com.example
12 | spring-app
13 | 0.0.1-SNAPSHOT
14 | spring-app
15 | spring-app
16 |
17 | 11
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-data-jpa
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-security
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-validation
31 |
32 |
33 |
34 | org.postgresql
35 | postgresql
36 | runtime
37 |
38 |
39 | org.projectlombok
40 | lombok
41 | true
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-starter-test
46 | test
47 |
48 |
49 | org.springframework.security
50 | spring-security-test
51 | test
52 |
53 |
54 | org.springframework.boot
55 | spring-boot-starter-web
56 |
57 |
58 |
59 |
60 | org.springframework.boot
61 | spring-boot-starter-oauth2-resource-server
62 |
63 |
64 |
65 |
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-maven-plugin
70 |
71 |
72 |
73 | org.projectlombok
74 | lombok
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/spring-app/src/main/java/com/example/springapp/SpringAppApplication.java:
--------------------------------------------------------------------------------
1 | package com.example.springapp;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class SpringAppApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(SpringAppApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/spring-app/src/main/java/com/example/springapp/controller/WebController.java:
--------------------------------------------------------------------------------
1 | package com.example.springapp.controller;
2 |
3 | import com.example.springapp.entity.User;
4 | import com.example.springapp.service.UserService;
5 | import lombok.RequiredArgsConstructor;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
9 | import org.springframework.web.bind.annotation.CrossOrigin;
10 | import org.springframework.web.bind.annotation.GetMapping;
11 | import org.springframework.web.bind.annotation.RestController;
12 |
13 |
14 | @RestController
15 | @CrossOrigin(origins = "http://localhost:4200")
16 | @RequiredArgsConstructor
17 | public class WebController {
18 |
19 | private static final Logger logger = LoggerFactory.getLogger(WebController.class);
20 |
21 | private final UserService userService;
22 |
23 | @GetMapping(path = "/userInfo1")
24 | public String getUserInfo1() {
25 | User user = userService.getLoggedUser();
26 |
27 | return "UserInfo1: " + user.getFirstname() + " " + user.getLastname() + ", " + user.getEmail();
28 | }
29 |
30 | @GetMapping("/userInfo2")
31 | public String getUserInfo2(JwtAuthenticationToken auth) {
32 | String firstname = auth.getTokenAttributes().get("given_name").toString();
33 | String lastname = auth.getTokenAttributes().get("family_name").toString();
34 | String email = auth.getTokenAttributes().get("email").toString();
35 | String authorities = auth.getAuthorities().toString();
36 |
37 | return "UserInfo2: " + firstname + " " + lastname + ", " + email + ", " + authorities ;
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/spring-app/src/main/java/com/example/springapp/entity/User.java:
--------------------------------------------------------------------------------
1 | package com.example.springapp.entity;
2 |
3 | import lombok.*;
4 |
5 | import javax.persistence.*;
6 |
7 | @Entity
8 | @Table(name = "USER_")
9 | @Setter @Getter
10 | @Builder
11 | @AllArgsConstructor
12 | @NoArgsConstructor
13 | public class User {
14 |
15 | @Id
16 | @Column
17 | private String email;
18 |
19 | @Column
20 | private String firstname;
21 |
22 | @Column
23 | private String lastname;
24 |
25 | @Column
26 | @Enumerated(EnumType.STRING)
27 | private Gender gender;
28 |
29 |
30 | public enum Gender {
31 | MALE, FEMALE
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/spring-app/src/main/java/com/example/springapp/repository/UserRepository.java:
--------------------------------------------------------------------------------
1 | package com.example.springapp.repository;
2 |
3 | import com.example.springapp.entity.User;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | import java.util.Optional;
8 |
9 | @Repository
10 | public interface UserRepository extends JpaRepository {
11 |
12 | Optional findByEmail(String email);
13 | boolean existsByEmail(String email);
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/spring-app/src/main/java/com/example/springapp/security/JwtUserSyncFilter.java:
--------------------------------------------------------------------------------
1 | package com.example.springapp.security;
2 |
3 | import com.example.springapp.entity.User;
4 | import com.example.springapp.service.UserService;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.security.core.context.SecurityContextHolder;
7 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
8 | import org.springframework.web.filter.OncePerRequestFilter;
9 |
10 | import javax.servlet.FilterChain;
11 | import javax.servlet.ServletException;
12 | import javax.servlet.http.HttpServletRequest;
13 | import javax.servlet.http.HttpServletResponse;
14 | import java.io.IOException;
15 |
16 |
17 | public class JwtUserSyncFilter extends OncePerRequestFilter {
18 |
19 | @Autowired
20 | private UserService userService;
21 |
22 | @Override
23 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
24 |
25 | try {
26 | JwtAuthenticationToken token = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
27 |
28 | String firstname = String.valueOf(token.getTokenAttributes().get("given_name"));
29 | String lastname = String.valueOf(token.getTokenAttributes().get("family_name"));
30 | String email = String.valueOf(token.getTokenAttributes().get("email"));
31 | User.Gender gender = token.getTokenAttributes().get("gender") == null ? null :
32 | User.Gender.valueOf(String.valueOf(token.getTokenAttributes().get("gender")).toUpperCase());
33 |
34 | User user = User.builder()
35 | .firstname(firstname)
36 | .lastname(lastname)
37 | .email(email)
38 | .gender(gender)
39 | .build();
40 |
41 | userService.syncUser(user);
42 | } catch (Exception e) {
43 | throw new IllegalArgumentException("Unable to auth user");
44 | }
45 |
46 | filterChain.doFilter(request, response);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/spring-app/src/main/java/com/example/springapp/security/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.example.springapp.security;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.core.convert.converter.Converter;
8 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.config.http.SessionCreationPolicy;
12 | import org.springframework.security.core.GrantedAuthority;
13 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
14 | import org.springframework.security.oauth2.jwt.Jwt;
15 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
16 | import org.springframework.security.web.SecurityFilterChain;
17 | import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
18 |
19 | import java.util.Collection;
20 | import java.util.Map;
21 | import java.util.stream.Collectors;
22 |
23 | @Configuration
24 | @EnableWebSecurity
25 | @EnableGlobalMethodSecurity(prePostEnabled = true)
26 | @RequiredArgsConstructor
27 | @Slf4j
28 | public class SecurityConfig {
29 |
30 | /**
31 | * Map Keycloak roles (REALM and CLIENT level) to get them all
32 | * */
33 | @Bean
34 | public JwtAuthenticationConverter jwtAuthenticationConverterForKeycloak() {
35 |
36 | log.warn("CONVERT");
37 |
38 | Converter> jwtGrantedAuthoritiesConverter = jwt -> {
39 | Map> realmAccess = jwt.getClaim("realm_access");
40 | Collection roles = realmAccess.get("roles");
41 | return roles.stream()
42 | .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
43 | .collect(Collectors.toList());
44 | };
45 |
46 | var jwtAuthenticationConverter = new JwtAuthenticationConverter();
47 | jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
48 |
49 | return jwtAuthenticationConverter;
50 | }
51 |
52 | /**
53 | * Sync local minimal user database with Keycloak db
54 | * */
55 | @Bean
56 | public JwtUserSyncFilter jwtAuthUserFilterBean() {
57 | return new JwtUserSyncFilter();
58 | }
59 |
60 | /**
61 | * Configure security
62 | * */
63 | @Bean
64 | public SecurityFilterChain configure(HttpSecurity http) throws Exception {
65 | http.csrf().disable();
66 | http.cors().disable();
67 | http.anonymous().disable();
68 |
69 | http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
70 | http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverterForKeycloak());
71 |
72 | http.authorizeRequests()
73 | .anyRequest()
74 | .hasRole("user");
75 |
76 | http.addFilterAfter(jwtAuthUserFilterBean(), SwitchUserFilter.class);
77 |
78 | return http.build();
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/spring-app/src/main/java/com/example/springapp/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.example.springapp.service;
2 |
3 | import com.example.springapp.entity.User;
4 | import com.example.springapp.repository.UserRepository;
5 | import lombok.RequiredArgsConstructor;
6 | import org.springframework.security.core.context.SecurityContextHolder;
7 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
8 | import org.springframework.stereotype.Service;
9 |
10 | import javax.persistence.EntityNotFoundException;
11 | import java.util.Optional;
12 |
13 | @Service
14 | @RequiredArgsConstructor
15 | public class UserService {
16 |
17 | private final UserRepository userRepository;
18 |
19 | public User getLoggedUser() {
20 | // nie uzywa sie UserDetails
21 | // https://stackoverflow.com/questions/64514346/spring-boot-oauth2-resource-server-userdetailsservice
22 | // wiec lepiej chyba to z hasId
23 | // https://stackoverflow.com/questions/62662036/how-do-i-configure-preauthorize-to-recognize-the-id-of-my-logged-in-user
24 |
25 | JwtAuthenticationToken token = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
26 |
27 | String email = String.valueOf(token.getTokenAttributes().get("email"));
28 | User user = userRepository.findByEmail(email).orElseThrow(() -> new EntityNotFoundException("Error while fetching user"));
29 |
30 | return user;
31 | }
32 |
33 | public void syncUser(User user) {
34 | if (user == null) {
35 | throw new EntityNotFoundException("Error while user sync");
36 | }
37 |
38 | User saveUser = user;
39 | Optional optionalUser = userRepository.findByEmail(user.getEmail());
40 |
41 | if (optionalUser.isPresent()) {
42 | saveUser = optionalUser.get();
43 | saveUser.setFirstname(user.getFirstname());
44 | saveUser.setLastname(user.getLastname());
45 | }
46 |
47 | userRepository.save(saveUser);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/spring-app/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | # -- Spring Boot --
2 | server.port=8080
3 |
4 | # -- Postgresql database --
5 | spring.datasource.url=jdbc:postgresql://localhost:5432/ewizitingDB
6 | spring.datasource.username=admin
7 | spring.datasource.password=admin
8 | spring.jpa.hibernate.ddl-auto=create
9 |
10 | # -- Keycloak --
11 | spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8081/realms/test-app
12 |
--------------------------------------------------------------------------------
/spring-app/src/test/java/com/example/springapp/SpringAppApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.example.springapp;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class SpringAppApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/springboot-keycloak-angular.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------