├── README.md ├── client ├── .docker │ ├── entrypoint │ │ └── run-app.sh │ └── ssl │ │ └── .gitkeep ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── deploy.bat ├── deploy.sh ├── lombok.config ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── docs │ └── asciidoc │ │ └── api-app.adoc │ ├── main │ ├── java │ │ └── com │ │ │ └── patternhelloworld │ │ │ └── securityhelper │ │ │ └── oauth2 │ │ │ └── client │ │ │ ├── SpringSecurityOauth2PasswordJpaImplApplication.java │ │ │ ├── config │ │ │ ├── database │ │ │ │ ├── CommonDataSourceConfiguration.java │ │ │ │ ├── CommonQuerydslRepositorySupport.java │ │ │ │ ├── QueryDslConfig.java │ │ │ │ ├── SelectablePersistenceConst.java │ │ │ │ └── dialect │ │ │ │ │ ├── CustomMySQL8Dialect.java │ │ │ │ │ └── CustomSQLServerDialect.java │ │ │ ├── logger │ │ │ │ ├── common │ │ │ │ │ └── CommonLoggingRequest.java │ │ │ │ └── module │ │ │ │ │ ├── ExcelAsyncUploadErrorLogConfig.java │ │ │ │ │ ├── NonStopErrorLogConfig.java │ │ │ │ │ ├── ResponseErrorLogConfig.java │ │ │ │ │ ├── ResponseSuccessLogConfig.java │ │ │ │ │ ├── RestTemplateClientErrorLogConfig.java │ │ │ │ │ ├── RestTemplateClientLogConfig.java │ │ │ │ │ └── SchedulingTaskErrorLogConfig.java │ │ │ ├── response │ │ │ │ └── error │ │ │ │ │ ├── CustomExceptionUtils.java │ │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ │ ├── exception │ │ │ │ │ ├── ErrorMessagesContainedException.java │ │ │ │ │ ├── auth │ │ │ │ │ │ ├── AlreadySocialRegisteredException.java │ │ │ │ │ │ ├── CustomAuthGuardException.java │ │ │ │ │ │ ├── NoSocialRegisteredException.java │ │ │ │ │ │ ├── OtpValueUnauthorizedException.java │ │ │ │ │ │ ├── SocialEmailNotProvidedException.java │ │ │ │ │ │ ├── SocialUnauthorizedException.java │ │ │ │ │ │ ├── UnauthorizedException.java │ │ │ │ │ │ ├── UserDeletedException.java │ │ │ │ │ │ └── UserRestoredException.java │ │ │ │ │ ├── data │ │ │ │ │ │ ├── AlreadyExistsException.java │ │ │ │ │ │ ├── AlreadyInProgressException.java │ │ │ │ │ │ └── ResourceNotFoundException.java │ │ │ │ │ ├── payload │ │ │ │ │ │ └── SearchFilterException.java │ │ │ │ │ ├── push │ │ │ │ │ │ └── PushException.java │ │ │ │ │ └── util │ │ │ │ │ │ └── EncodingProcessException.java │ │ │ │ │ └── message │ │ │ │ │ └── GeneralErrorMessage.java │ │ │ └── securityimpl │ │ │ │ ├── aop │ │ │ │ └── CustomSecurityPointCutImpl.java │ │ │ │ ├── cors │ │ │ │ ├── CorsFilter.java │ │ │ │ └── CorsFilterConfig.java │ │ │ │ ├── guard │ │ │ │ ├── AccessTokenUserInfoConverter.java │ │ │ │ ├── CustomAuthenticationPrincipal.java │ │ │ │ ├── CustomizedUserInfo.java │ │ │ │ ├── ResourceServerAuthorityChecker.java │ │ │ │ ├── UserCustomerOnly.java │ │ │ │ ├── UserCustomerOnlyImpl.java │ │ │ │ └── resolver │ │ │ │ │ └── AccessTokenUserInfoArgumentResolver.java │ │ │ │ ├── introspector │ │ │ │ └── CustomResourceServerTokenIntrospector.java │ │ │ │ ├── message │ │ │ │ ├── CustomSecurityMessageServiceImpl.java │ │ │ │ └── CustomSecurityUserExceptionMessage.java │ │ │ │ ├── response │ │ │ │ ├── CustomApiAuthenticationFailureHandlerImpl.java │ │ │ │ ├── CustomApiAuthenticationSuccessHandlerImpl.java │ │ │ │ ├── CustomAuthenticationEntryPointImpl.java │ │ │ │ ├── CustomWebAuthenticationFailureHandlerImpl.java │ │ │ │ └── CustomWebAuthenticationSuccessHandlerImpl.java │ │ │ │ ├── serivce │ │ │ │ ├── permission │ │ │ │ │ └── CustomAuthorityService.java │ │ │ │ └── userdetail │ │ │ │ │ ├── AdminDetailsService.java │ │ │ │ │ ├── CustomUserDetailsServiceFactory.java │ │ │ │ │ └── CustomerDetailsService.java │ │ │ │ └── web │ │ │ │ └── WebMvcConfig.java │ │ │ ├── domain │ │ │ ├── admin │ │ │ │ ├── api │ │ │ │ │ └── AdminApi.java │ │ │ │ ├── dao │ │ │ │ │ ├── AdminRepository.java │ │ │ │ │ ├── AdminRepositorySupport.java │ │ │ │ │ └── AdminRoleRepository.java │ │ │ │ ├── dto │ │ │ │ │ ├── AdminCreate.java │ │ │ │ │ ├── AdminDTO.java │ │ │ │ │ └── AdminSearchFilter.java │ │ │ │ ├── entity │ │ │ │ │ ├── Admin.java │ │ │ │ │ ├── AdminRole.java │ │ │ │ │ └── Password.java │ │ │ │ ├── exception │ │ │ │ │ ├── AccountNotFoundException.java │ │ │ │ │ ├── IdNameDuplicationException.java │ │ │ │ │ └── PasswordFailedExceededOauth2AuthenticationException.java │ │ │ │ └── service │ │ │ │ │ └── AdminService.java │ │ │ ├── common │ │ │ │ ├── api │ │ │ │ │ └── BaseApi.java │ │ │ │ ├── dto │ │ │ │ │ ├── DateRangeFilter.java │ │ │ │ │ └── SorterValueFilter.java │ │ │ │ └── web │ │ │ │ │ └── AuthorizationCodeRequestWeb.java │ │ │ ├── customer │ │ │ │ ├── api │ │ │ │ │ └── CustomerApi.java │ │ │ │ ├── dao │ │ │ │ │ ├── CustomerRepository.java │ │ │ │ │ ├── CustomerRepositorySupport.java │ │ │ │ │ └── CustomerRoleRepository.java │ │ │ │ ├── dto │ │ │ │ │ ├── AdminType.java │ │ │ │ │ ├── CustomerReqDTO.java │ │ │ │ │ └── CustomerResDTO.java │ │ │ │ ├── entity │ │ │ │ │ ├── Customer.java │ │ │ │ │ ├── CustomerRole.java │ │ │ │ │ ├── Email.java │ │ │ │ │ └── Password.java │ │ │ │ ├── exception │ │ │ │ │ ├── AccountNotFoundException.java │ │ │ │ │ ├── EmailDuplicationException.java │ │ │ │ │ └── PasswordFailedExceededException.java │ │ │ │ ├── service │ │ │ │ │ └── CustomerService.java │ │ │ │ └── validator │ │ │ │ │ ├── Password.java │ │ │ │ │ └── PasswordValidator.java │ │ │ ├── role │ │ │ │ ├── api │ │ │ │ │ └── RoleApi.java │ │ │ │ ├── dao │ │ │ │ │ └── RoleRepository.java │ │ │ │ └── entity │ │ │ │ │ └── Role.java │ │ │ └── traditionaloauth │ │ │ │ └── api │ │ │ │ └── v1 │ │ │ │ └── TraditionalOauthApi.java │ │ │ └── util │ │ │ ├── AES256.java │ │ │ ├── BitConverter.java │ │ │ ├── CommonConstant.java │ │ │ ├── CustomUtils.java │ │ │ ├── IPUtils.java │ │ │ ├── PaginationUtil.java │ │ │ ├── PathUtils.java │ │ │ ├── ReflectionUtils.java │ │ │ └── ValidatorUtils.java │ └── resources │ │ ├── application.properties │ │ ├── logback-spring.xml │ │ └── templates │ │ ├── consent.html │ │ ├── css │ │ └── signin.css │ │ ├── device-activate.html │ │ ├── device-activated.html │ │ ├── error.html │ │ └── login.html │ └── test │ └── java │ └── com │ └── patternhelloworld │ └── securityhelper │ └── oauth2 │ └── client │ ├── SpringSecurityOauth2PasswordJpaImplApplicationTests.java │ ├── integration │ └── auth │ │ ├── AuthorizationIntegrationTest.java │ │ └── TokenIntegrationTest.java │ ├── unit │ └── customer │ │ ├── CustomerApiTest.java │ │ └── CustomerRepositoryTest.java │ └── util │ ├── NameGenerator.java │ ├── TestUtil.java │ └── auth │ ├── AbstractMockAuth.java │ ├── AbstractRestDocsTests.java │ ├── IntegrationMockAuth.java │ ├── MockAuth.java │ └── UnitMockAuth.java ├── lib ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── deploy.bat ├── deploy.sh ├── files │ └── .gitkeep ├── logs │ └── .gitkeep ├── lombok.config ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── docs │ └── asciidoc │ │ └── api-app.adoc │ ├── main │ └── java │ │ └── io │ │ └── github │ │ └── patternhelloworld │ │ └── securityhelper │ │ └── oauth2 │ │ └── api │ │ ├── config │ │ ├── logger │ │ │ └── EasyPlusSecurityLogConfig.java │ │ ├── security │ │ │ ├── aop │ │ │ │ ├── DefaultSecurityPointCut.java │ │ │ │ └── SecurityPointCut.java │ │ │ ├── converter │ │ │ │ └── auth │ │ │ │ │ └── endpoint │ │ │ │ │ ├── CodeAuthorizationConditionalConverter.java │ │ │ │ │ ├── IntrospectionRequestConverter.java │ │ │ │ │ └── TokenRequestAfterClientBasicSecretAuthenticatedConverter.java │ │ │ ├── core │ │ │ │ └── EasyPlusUserInfo.java │ │ │ ├── dao │ │ │ │ ├── EasyPlusAuthorizationConsentRepository.java │ │ │ │ ├── EasyPlusAuthorizationRepository.java │ │ │ │ └── EasyPlusClientRepository.java │ │ │ ├── entity │ │ │ │ ├── EasyPlusAuthorization.java │ │ │ │ ├── EasyPlusAuthorizationConsent.java │ │ │ │ └── EasyPlusClient.java │ │ │ ├── enums │ │ │ │ └── MobileOSType.java │ │ │ ├── introspector │ │ │ │ └── DefaultResourceServerTokenIntrospector.java │ │ │ ├── message │ │ │ │ ├── DefaultSecurityMessageServiceImpl.java │ │ │ │ ├── DefaultSecurityUserExceptionMessage.java │ │ │ │ ├── ExceptionMessageInterface.java │ │ │ │ └── ISecurityUserExceptionMessageService.java │ │ │ ├── provider │ │ │ │ └── auth │ │ │ │ │ ├── endpoint │ │ │ │ │ ├── authorization │ │ │ │ │ │ ├── CodeAuthenticationProvider.java │ │ │ │ │ │ └── CodeRequestAuthenticationProvider.java │ │ │ │ │ └── token │ │ │ │ │ │ └── OpaqueGrantTypeAuthenticationProvider.java │ │ │ │ │ └── introspectionendpoint │ │ │ │ │ └── IntrospectOpaqueTokenAuthenticationProvider.java │ │ │ ├── response │ │ │ │ ├── auth │ │ │ │ │ └── authentication │ │ │ │ │ │ ├── DefaultApiAuthenticationFailureHandlerImpl.java │ │ │ │ │ │ ├── DefaultApiAuthenticationSuccessHandlerImpl.java │ │ │ │ │ │ ├── DefaultWebAuthenticationFailureHandlerImpl.java │ │ │ │ │ │ └── DefaultWebAuthenticationSuccessHandlerImpl.java │ │ │ │ ├── error │ │ │ │ │ ├── dto │ │ │ │ │ │ ├── EasyPlusErrorMessages.java │ │ │ │ │ │ ├── ISecurityErrorMessages.java │ │ │ │ │ │ └── SecurityEasyPlusErrorResponsePayload.java │ │ │ │ │ ├── exception │ │ │ │ │ │ ├── EasyPlusOauth2AuthenticationException.java │ │ │ │ │ │ └── EasyPlusOauth2AuthorizationException.java │ │ │ │ │ ├── handler │ │ │ │ │ │ └── SecurityEasyPlusExceptionHandler.java │ │ │ │ │ └── util │ │ │ │ │ │ └── ExceptionEasyPlusUtils.java │ │ │ │ └── resource │ │ │ │ │ └── authentication │ │ │ │ │ └── DefaultAuthenticationEntryPoint.java │ │ │ ├── serivce │ │ │ │ ├── CommonOAuth2AuthorizationSaver.java │ │ │ │ ├── CommonOAuth2AuthorizationSaverImpl.java │ │ │ │ ├── DefaultOauth2AuthenticationHashCheckService.java │ │ │ │ ├── IOauth2AuthenticationHashCheckService.java │ │ │ │ ├── authentication │ │ │ │ │ ├── OAuth2AuthorizationBuildingService.java │ │ │ │ │ └── OAuth2AuthorizationBuildingServiceImpl.java │ │ │ │ ├── persistence │ │ │ │ │ ├── authorization │ │ │ │ │ │ ├── OAuth2AuthorizationConsentServiceImpl.java │ │ │ │ │ │ └── OAuth2AuthorizationServiceImpl.java │ │ │ │ │ └── client │ │ │ │ │ │ └── CacheableRegisteredClientRepositoryImpl.java │ │ │ │ └── userdetail │ │ │ │ │ ├── ConditionalDetailsService.java │ │ │ │ │ └── UserDetailsServiceFactory.java │ │ │ ├── server │ │ │ │ ├── EasyPlusJwtConfig.java │ │ │ │ └── EasyPlusServerConfig.java │ │ │ ├── token │ │ │ │ ├── EasyPlusGrantAuthenticationToken.java │ │ │ │ └── generator │ │ │ │ │ ├── CustomAccessTokenCustomizer.java │ │ │ │ │ ├── CustomAuthenticationKeyGenerator.java │ │ │ │ │ └── CustomDelegatingOAuth2TokenGenerator.java │ │ │ └── validator │ │ │ │ └── endpoint │ │ │ │ ├── AbstractEasyPlusBaseValidator.java │ │ │ │ ├── authorization │ │ │ │ ├── CodeRequestValidator.java │ │ │ │ └── CodeValidationResult.java │ │ │ │ └── token │ │ │ │ ├── CodeValidationResult.java │ │ │ │ └── TokenRequestValidator.java │ │ └── util │ │ │ ├── EasyPlusErrorCodeConstants.java │ │ │ ├── EasyPlusHttpHeaders.java │ │ │ ├── EasyPlusOAuth2EndpointUtils.java │ │ │ ├── EasyPlusOrderConstants.java │ │ │ ├── SecurityExceptionUtils.java │ │ │ ├── SerializableObjectConverter.java │ │ │ └── TimestampUtil.java │ │ └── domain │ │ └── traditionaloauth │ │ ├── bo │ │ └── BasicTokenResolver.java │ │ ├── dto │ │ └── SpringSecurityTraditionalOauthDTO.java │ │ └── service │ │ └── TraditionalOauthService.java │ └── test │ └── java │ └── io │ └── github │ └── patternhelloworld │ └── securityhelper │ └── oauth2 │ └── api │ └── domain │ └── traditionaloauth │ └── service │ └── TraditionalOauthServiceTest.java ├── mysql ├── schema.sql └── schema2.sql ├── postman ├── sc-oauth2-pji.postman_collection.json └── sc-oauth2-pji.postman_environment.json └── reference └── docs ├── img.png ├── img1.png ├── img3.png └── img4.png /client/.docker/entrypoint/run-app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Graceful Shutdown 4 | #trap exitHandler EXIT 5 | #exitHandler() { 6 | # echo "[INSIDE APP CONTAINER][WARN] Server will be shutdown soon." 1>&2 7 | # kill -TERM $(jps | grep jar | awk '{print $1}') 8 | #} 9 | 10 | if [ -z "$1" ]; then 11 | echo "[INSIDE APP CONTAINER][ERROR] No project root path parameter found for the 'run-app.sh'" 12 | exit 1 13 | fi 14 | if [ -z "$2" ]; then 15 | echo "[INSIDE APP CONTAINER][ERROR] No file root path parameter found for the 'run-app.sh'" 16 | exit 1 17 | fi 18 | if [ -z "$3" ]; then 19 | echo "[INSIDE APP CONTAINER][ERROR] No Xms parameter found for the 'run-app.sh'" 20 | exit 1 21 | fi 22 | if [ -z "$4" ]; then 23 | echo "[INSIDE APP CONTAINER][ERROR] No Xmx parameter found for the 'run-app.sh'" 24 | exit 1 25 | fi 26 | 27 | echo "[INSIDE APP CONTAINER][NOTICE] Run : java -Xms${3}m -Xmx${4}m -XX:+PrintGCDetails -Xloggc:${2}/logs/auth-gc.log -Dspring.config.location=file:${1}/src/main/resources/application.properties -Dlogging.config=file:${1}/src/main/resources/logback-spring.xml -jar /app.jar > ${2}/logs/auth-start.log 2>&1 &" 28 | java -XX:+PrintGCDetails -Xms${3}m -Xmx${4}m -Xloggc:${2}/logs/auth-gc.log -Dspring.config.location=file:${1}/src/main/resources/application.properties -Dlogging.config=file:${1}/src/main/resources/logback-spring.xml -jar /app.jar > ${2}/logs/auth-start.log 2>&1 & 29 | -------------------------------------------------------------------------------- /client/.docker/ssl/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patternhelloworld/spring-oauth2-easyplus/1626d56b176212712df72e7b1d4ef389b709a504/client/.docker/ssl/.gitkeep -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | /.docker/ssl/* 2 | !/.docker/ssl/.gitkeep 3 | 4 | /target/ 5 | 6 | # docker 7 | /.docker/env/* 8 | !/.docker/env/.gitkeep 9 | /.docker/ssh/* 10 | !/.docker/ssh/.gitkeep 11 | 12 | ### STS ### 13 | /.apt_generated 14 | /.classpath 15 | /.factorypath 16 | /.project 17 | /.settings 18 | /.springBeans 19 | /.sts4-cache 20 | 21 | # Mac 22 | /.DS_Store 23 | 24 | 25 | ### IntelliJ IDEA ### 26 | /.idea 27 | /*.iws 28 | /*.iml 29 | /*.ipr 30 | 31 | # logs 32 | /logs/* 33 | !/logs/.gitkeep 34 | 35 | # files 36 | /files/* 37 | !/files/.gitkeep 38 | 39 | # .properties 40 | #/src/main/resources/application.properties 41 | 42 | # logback 43 | #/src/main/resources/logback-spring.xml 44 | 45 | 46 | -------------------------------------------------------------------------------- /client/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patternhelloworld/spring-oauth2-easyplus/1626d56b176212712df72e7b1d4ef389b709a504/client/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /client/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.6.3-jdk-8-slim AS build 2 | 3 | ARG PROJECT_ROOT_IN_CONTAINER 4 | 5 | # In the ./src/main/resources folder, 1) application.properties, 2) logback-spring.xml and 3) [the filename of 'server.ssl.key-store' in your properties] should be located. 6 | COPY ./ $PROJECT_ROOT_IN_CONTAINER 7 | USER root 8 | WORKDIR $PROJECT_ROOT_IN_CONTAINER 9 | 10 | RUN --mount=type=cache,target=/root/.m2 mvn -f $PROJECT_ROOT_IN_CONTAINER/pom.xml clean install 11 | 12 | FROM openjdk:17-alpine 13 | 14 | ARG PROJECT_ROOT_IN_CONTAINER 15 | ARG FILE_STORAGE_ROOT_IN_CONTAINER 16 | ARG JVM_XMS 17 | ARG JVM_XMX 18 | 19 | COPY --from=build $PROJECT_ROOT_IN_CONTAINER/ $PROJECT_ROOT_IN_CONTAINER 20 | 21 | USER root 22 | WORKDIR $PROJECT_ROOT_IN_CONTAINER 23 | 24 | RUN cp $PROJECT_ROOT_IN_CONTAINER/target/*.jar /app.jar 25 | 26 | RUN ln -s $PROJECT_ROOT_IN_CONTAINER/.docker/entrypoint/run-app.sh /run-app.sh 27 | 28 | RUN apk --no-cache add curl bash fontconfig ttf-dejavu 29 | 30 | ENV PROJECT_ROOT_IN_CONTAINER=$PROJECT_ROOT_IN_CONTAINER 31 | ENV FILE_STORAGE_ROOT_IN_CONTAINER=$FILE_STORAGE_ROOT_IN_CONTAINER 32 | ENV JVM_XMS=$JVM_XMS 33 | ENV JVM_XMX=$JVM_XMX 34 | ENV RESOURCES_TYPE=$RESOURCES_TYPE 35 | 36 | ENTRYPOINT sh /run-app.sh $PROJECT_ROOT_IN_CONTAINER $FILE_STORAGE_ROOT_IN_CONTAINER $JVM_XMS $JVM_XMX && /bin/sh -------------------------------------------------------------------------------- /client/deploy.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set local_maven_repo="C:\Users\Andrew Kang\.m2\repository\com\patternhelloworld\securityhelper\oauth2\spring-oauth2-easyplus" 3 | mvnw.cmd -DaltDeploymentRepository=snapshot-repo::default::file://%local_maven_repo%/snapshots clean deploy -------------------------------------------------------------------------------- /client/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | local_maven_repo='/mnt/c/Users/Andrew\sKang/.m2/repository/com/patternhelloworld/securityhelper/oauth2/spring-oauth2-easyplus' 3 | mvn -DaltDeploymentRepository=snapshot-repo::default::file://${local_maven_repo}/snapshots clean deploy 4 | 5 | -------------------------------------------------------------------------------- /client/lombok.config: -------------------------------------------------------------------------------- 1 | lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Value 2 | lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier -------------------------------------------------------------------------------- /client/src/docs/asciidoc/api-app.adoc: -------------------------------------------------------------------------------- 1 | = Spring Oauth2 EasyPlus 2 | :doctype: book 3 | :icons: font 4 | :source-highlighter: highlightjs 5 | :toc: left 6 | :toclevels: 4 7 | :sectnums: 8 | :sectlinks: 9 | :sectanchors: 10 | 11 | == Notice 12 | - ``/api/v1/traditional-oauth/token`` has the same function as ``/oauth2/token``, which is included in Spring Security, which can be more regarded as secure. 13 | 14 | == Authentication 15 | 16 | 17 | === Access Token 18 | ==== Request 19 | ===== Payload 20 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-access-token/http-request.adoc[] 21 | ====== Header 22 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-access-token/request-headers.adoc[] 23 | ====== Parameters 24 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-access-token/form-parameters.adoc[] 25 | ====== Body 26 | 'application/x-www-form-urlencoded' 27 | 28 | ==== Response 29 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-access-token/response-body.adoc[] 30 | 31 | 32 | === Refresh Token 33 | 34 | ==== Request 35 | ===== Payload 36 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-refresh-token/http-request.adoc[] 37 | ====== Header 38 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-refresh-token/request-headers.adoc[] 39 | ====== Parameters 40 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-refresh-token/form-parameters.adoc[] 41 | ====== Body 42 | 'application/x-www-form-urlencoded' 43 | 44 | ==== Response 45 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-refresh-token/response-body.adoc[] 46 | 47 | 48 | === Logout 49 | 50 | ==== Request 51 | ===== Payload 52 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-customer-logout/http-request.adoc[] 53 | ====== Header 54 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-customer-logout/request-headers.adoc[] 55 | ====== Parameters 56 | 57 | X 58 | 59 | ====== Body 60 | 61 | X 62 | 63 | ==== Response 64 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-customer-logout/response-body.adoc[] 65 | include::{snippets}/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-customer-logout/response-fields.adoc[] -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/SpringSecurityOauth2PasswordJpaImplApplication.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | import javax.annotation.PostConstruct; 7 | import java.util.TimeZone; 8 | 9 | 10 | @SpringBootApplication(scanBasePackages = {"com.patternhelloworld.securityhelper.oauth2.client", "io.github.patternhelloworld.securityhelper.oauth2.api"}) 11 | public class SpringSecurityOauth2PasswordJpaImplApplication { 12 | 13 | @PostConstruct 14 | void init() { 15 | TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); 16 | } 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(SpringSecurityOauth2PasswordJpaImplApplication.class, args); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/database/CommonDataSourceConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.database; 2 | 3 | import com.zaxxer.hikari.HikariDataSource; 4 | import jakarta.persistence.EntityManagerFactory; 5 | import org.springframework.beans.factory.annotation.Qualifier; 6 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Primary; 12 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 13 | import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; 14 | import org.springframework.orm.jpa.JpaTransactionManager; 15 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 16 | import org.springframework.transaction.PlatformTransactionManager; 17 | 18 | import javax.sql.DataSource; 19 | 20 | 21 | @Configuration 22 | @EnableJpaRepositories( 23 | basePackages = {"com.patternhelloworld.securityhelper.oauth2.client.domain", 24 | "com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl", 25 | "io.github.patternhelloworld.securityhelper.oauth2.api.config.security"}, 26 | entityManagerFactoryRef = "commonEntityManagerFactory", 27 | transactionManagerRef= "commonTransactionManager" 28 | ) 29 | public class CommonDataSourceConfiguration { 30 | 31 | @Bean 32 | @Primary 33 | @ConfigurationProperties("spring.datasource.hikari.patternhelloworld") 34 | public DataSourceProperties commonDataSourceProperties() { 35 | return new DataSourceProperties(); 36 | } 37 | 38 | @Bean(name="commonDataSource") 39 | @Primary 40 | @ConfigurationProperties("spring.datasource.hikari.patternhelloworld.configuration") 41 | public DataSource commonDataSource() { 42 | return new LazyConnectionDataSourceProxy(commonDataSourceProperties().initializeDataSourceBuilder() 43 | .type(HikariDataSource.class).build()); 44 | } 45 | 46 | @Primary 47 | @Bean(name = "commonEntityManagerFactory") 48 | public LocalContainerEntityManagerFactoryBean commonEntityManagerFactory(EntityManagerFactoryBuilder builder) { 49 | return builder 50 | .dataSource(commonDataSource()) 51 | .packages("com.patternhelloworld.securityhelper.oauth2.client.domain", 52 | "io.github.patternhelloworld.securityhelper.oauth2.api.config.security") 53 | .persistenceUnit("commonEntityManager") 54 | .build(); 55 | } 56 | 57 | @Primary 58 | @Bean(name = "commonTransactionManager") 59 | public PlatformTransactionManager commonTransactionManager(@Qualifier("commonEntityManagerFactory") EntityManagerFactory entityManagerFactory) { 60 | return new JpaTransactionManager(entityManagerFactory); 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/database/CommonQuerydslRepositorySupport.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.database; 2 | 3 | import jakarta.persistence.EntityManager; 4 | import jakarta.persistence.PersistenceContext; 5 | import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; 6 | import org.springframework.stereotype.Repository; 7 | 8 | 9 | @Repository 10 | public abstract class CommonQuerydslRepositorySupport extends QuerydslRepositorySupport { 11 | 12 | /** 13 | * Creates a new {@link QuerydslRepositorySupport} instance for the given domain type. 14 | * 15 | * @param domainClass must not be {@literal null}. 16 | */ 17 | public CommonQuerydslRepositorySupport(Class domainClass) { 18 | super(domainClass); 19 | } 20 | 21 | @Override 22 | @PersistenceContext(unitName = "commonEntityManager") 23 | public void setEntityManager(EntityManager entityManager) { 24 | super.setEntityManager(entityManager); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/database/QueryDslConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.database; 2 | 3 | 4 | import com.querydsl.jpa.impl.JPAQueryFactory; 5 | import jakarta.persistence.EntityManager; 6 | import jakarta.persistence.PersistenceContext; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | public class QueryDslConfig { 12 | 13 | @PersistenceContext(unitName = "commonEntityManager") 14 | private EntityManager commonEntityManager; 15 | 16 | @Bean 17 | public JPAQueryFactory authJpaQueryFactory() { 18 | return new JPAQueryFactory(commonEntityManager); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/database/SelectablePersistenceConst.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.database; 2 | 3 | public enum SelectablePersistenceConst { 4 | 5 | MYSQL_8("dialect.database.config.com.patternhelloworld.securityhelper.oauth2.client.CustomMySQL8Dialect"), 6 | MSSQL("dialect.database.config.com.patternhelloworld.securityhelper.oauth2.client.CustomSQLServerDialect"); 7 | 8 | private final String value; 9 | 10 | SelectablePersistenceConst(String value) { 11 | this.value = value; 12 | } 13 | 14 | public String getValue() { 15 | return value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/database/dialect/CustomMySQL8Dialect.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.database.dialect; 2 | 3 | 4 | import org.hibernate.boot.model.FunctionContributions; 5 | import org.hibernate.dialect.MySQLDialect; 6 | import org.hibernate.dialect.function.CommonFunctionFactory; 7 | 8 | 9 | public class CustomMySQL8Dialect extends MySQLDialect { 10 | 11 | @Override 12 | public void initializeFunctionRegistry(FunctionContributions functionContributions) { 13 | super.initializeFunctionRegistry(functionContributions); 14 | 15 | CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions); 16 | functionFactory.listagg_groupConcat(); 17 | } 18 | 19 | // 'group_concat' & 'date_format' exist, so they don't need to be implemented 20 | 21 | } 22 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/database/dialect/CustomSQLServerDialect.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.database.dialect; 2 | 3 | 4 | import org.hibernate.boot.model.FunctionContributions; 5 | import org.hibernate.dialect.SQLServerDialect; 6 | import org.hibernate.query.sqm.function.FunctionKind; 7 | import org.hibernate.query.sqm.function.SqmFunctionRegistry; 8 | import org.hibernate.query.sqm.produce.function.PatternFunctionDescriptorBuilder; 9 | import org.hibernate.type.spi.TypeConfiguration; 10 | 11 | public class CustomSQLServerDialect extends SQLServerDialect { 12 | 13 | @Override 14 | public void initializeFunctionRegistry(FunctionContributions functionContributions) { 15 | super.initializeFunctionRegistry(functionContributions); 16 | SqmFunctionRegistry registry = functionContributions.getFunctionRegistry(); 17 | TypeConfiguration types = functionContributions.getTypeConfiguration(); 18 | 19 | new PatternFunctionDescriptorBuilder(registry, "FORMAT", FunctionKind.NORMAL, "FORMAT(?1, ?2)") 20 | .setExactArgumentCount(2) 21 | .setInvariantType(types.getBasicTypeForJavaType(String.class)) 22 | .register(); 23 | 24 | // 'string_agg' exists, so it doesn't need to be implemented 25 | } 26 | 27 | 28 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/logger/module/ExcelAsyncUploadErrorLogConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.logger.module; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | 8 | 9 | @Slf4j 10 | public class ExcelAsyncUploadErrorLogConfig { 11 | 12 | private static final Logger logger = LoggerFactory.getLogger(ExcelAsyncUploadErrorLogConfig.class); 13 | 14 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/logger/module/NonStopErrorLogConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.logger.module; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | 8 | @Slf4j 9 | public class NonStopErrorLogConfig { 10 | 11 | private static final Logger logger = LoggerFactory.getLogger(NonStopErrorLogConfig.class); 12 | 13 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/logger/module/ResponseErrorLogConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.logger.module; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.config.response.error.GlobalExceptionHandler; 4 | import com.patternhelloworld.securityhelper.oauth2.client.config.logger.common.CommonLoggingRequest; 5 | 6 | 7 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.SecurityEasyPlusErrorResponsePayload; 8 | import org.aspectj.lang.JoinPoint; 9 | import org.aspectj.lang.annotation.AfterReturning; 10 | import org.aspectj.lang.annotation.Aspect; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.stereotype.Component; 15 | 16 | 17 | @Aspect 18 | @Component 19 | public class ResponseErrorLogConfig { 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(ResponseErrorLogConfig.class); 22 | 23 | 24 | @AfterReturning(pointcut = ("within(com.patternhelloworld.securityhelper.oauth2.client.config.response.error..*) || within(io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.handler..*)"), 25 | returning = "returnValue") 26 | public void endpointAfterExceptionReturning(JoinPoint p, Object returnValue) { 27 | 28 | String loggedText = "\n[After Throwing Thread] : " + Thread.currentThread().getId() + "\n"; 29 | 30 | // 4. Error logging 31 | try { 32 | if (p.getTarget().getClass().equals(GlobalExceptionHandler.class)) { 33 | 34 | SecurityEasyPlusErrorResponsePayload errorResponsePayload = (SecurityEasyPlusErrorResponsePayload) ((ResponseEntity) returnValue).getBody(); 35 | loggedText += String.format("[After - Error Response]\n message : %s || \n userMessage : %s || \n cause : %s || \n stackTrace : %s", 36 | errorResponsePayload != null ? errorResponsePayload.getMessage() : "No error message", 37 | errorResponsePayload != null ? errorResponsePayload.getUserMessage() : "No error userMessage", 38 | errorResponsePayload != null ? errorResponsePayload.getCause() : "No error detail cause", 39 | errorResponsePayload != null ? errorResponsePayload.getStackTrace() : "No error detail stack trace"); 40 | } 41 | } catch (Exception ex4) { 42 | 43 | loggedText += "\n[Error during the errorLogging] : " + ex4.getMessage(); 44 | } 45 | 46 | try { 47 | loggedText += "\n[Location] : " + p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName(); 48 | } catch (Exception ex5) { 49 | loggedText += "\n[Error during the finalStage] : " + ex5.getMessage(); 50 | } 51 | 52 | CommonLoggingRequest commonLoggingRequest = new CommonLoggingRequest(); 53 | 54 | logger.error(commonLoggingRequest.getText() + loggedText + "|||\n"); 55 | } 56 | 57 | 58 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/logger/module/ResponseSuccessLogConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.logger.module; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.config.logger.common.CommonLoggingRequest; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.aspectj.lang.JoinPoint; 7 | import org.aspectj.lang.annotation.AfterReturning; 8 | import org.aspectj.lang.annotation.Aspect; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.stereotype.Component; 14 | 15 | 16 | @Aspect 17 | @Component 18 | public class ResponseSuccessLogConfig { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(ResponseSuccessLogConfig.class); 21 | 22 | 23 | @AfterReturning(pointcut = ("within(com.patternhelloworld.securityhelper.oauth2.client.domain..api..*)"), 24 | returning = "returnValue") 25 | public void endpointAfterReturning(JoinPoint p, Object returnValue) { 26 | 27 | boolean isErrored = false; 28 | String loggedText = "\n[After - Returning Thread] : " + Thread.currentThread().getId() + "\n"; 29 | 30 | // Response logging 31 | try { 32 | ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); 33 | 34 | if (returnValue.getClass().equals(ResponseEntity.class)) { 35 | MediaType mediaType = ((ResponseEntity) returnValue).getHeaders().getContentType(); 36 | 37 | if (mediaType != null && (mediaType.getType().equals("image") || mediaType.equals(MediaType.APPLICATION_OCTET_STREAM))) { 38 | loggedText += "[After - Response] \n" + "Image binary"; 39 | } else { 40 | loggedText += "[After - Response] \n" + mapper.writeValueAsString(returnValue); 41 | } 42 | } else { 43 | loggedText += "[After - Response] \n" + mapper.writeValueAsString(returnValue); 44 | } 45 | 46 | } catch (Exception ex3) { 47 | isErrored = true; 48 | loggedText += "[After - Error during the responseLogging] : " + ex3.getMessage(); 49 | } 50 | 51 | 52 | try { 53 | loggedText += "\n[After - Location] : " + p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName(); 54 | } catch (Exception ex5) { 55 | isErrored = true; 56 | loggedText += "\n[After - Error during the finalStage] : " + ex5.getMessage(); 57 | } 58 | 59 | CommonLoggingRequest commonLoggingRequest = new CommonLoggingRequest(); 60 | 61 | if (isErrored) { 62 | logger.error(commonLoggingRequest.getText() + loggedText + "|||\n"); 63 | } else { 64 | logger.trace(commonLoggingRequest.getText() + loggedText + "|||\n"); 65 | } 66 | 67 | 68 | } 69 | 70 | 71 | 72 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/logger/module/RestTemplateClientErrorLogConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.logger.module; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.config.logger.common.CommonLoggingRequest; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.http.HttpStatusCode; 8 | import org.springframework.http.client.ClientHttpResponse; 9 | import org.springframework.lang.NonNull; 10 | import org.springframework.web.client.ResponseErrorHandler; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.IOException; 14 | import java.io.InputStreamReader; 15 | 16 | @Slf4j 17 | public class RestTemplateClientErrorLogConfig implements ResponseErrorHandler { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(RestTemplateClientLogConfig.class); 20 | 21 | @Override 22 | public boolean hasError(@NonNull final ClientHttpResponse response) throws IOException { 23 | final HttpStatusCode statusCode = response.getStatusCode(); 24 | return !statusCode.is2xxSuccessful(); 25 | } 26 | 27 | @Override 28 | public void handleError(@NonNull final ClientHttpResponse response) throws IOException { 29 | 30 | CommonLoggingRequest commonLoggingRequest = new CommonLoggingRequest(); 31 | String loggedText = commonLoggingRequest.getText(); 32 | 33 | final String error = getErrorAsString(response); 34 | 35 | loggedText += "\n======Error Response from Server====== (Thread ID : " + Thread.currentThread().getId() + ")\n"; 36 | loggedText += "Headers: " + response.getHeaders() + "\n"; 37 | loggedText += "Response Status : " + response.getRawStatusCode() + "\n"; 38 | loggedText += "Response body: " + error + "\n"; 39 | loggedText += "======Error Response from Server======\n"; 40 | 41 | logger.error(loggedText); 42 | } 43 | 44 | private String getErrorAsString(@NonNull final ClientHttpResponse response) throws IOException { 45 | try (BufferedReader br = new BufferedReader(new InputStreamReader(response.getBody()))) { 46 | return br.readLine(); 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/logger/module/RestTemplateClientLogConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.logger.module; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.config.logger.common.CommonLoggingRequest; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.http.HttpRequest; 7 | import org.springframework.http.client.ClientHttpRequestExecution; 8 | import org.springframework.http.client.ClientHttpRequestInterceptor; 9 | import org.springframework.http.client.ClientHttpResponse; 10 | import org.springframework.lang.NonNull; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.IOException; 14 | import java.io.InputStreamReader; 15 | import java.nio.charset.StandardCharsets; 16 | 17 | 18 | public class RestTemplateClientLogConfig implements ClientHttpRequestInterceptor { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(RestTemplateClientLogConfig.class); 21 | 22 | @NonNull 23 | @Override 24 | public ClientHttpResponse intercept(@NonNull final HttpRequest request, 25 | @NonNull final byte[] body, final @NonNull ClientHttpRequestExecution execution) 26 | throws IOException { 27 | 28 | loggingRequest(request, body); 29 | 30 | ClientHttpResponse response = execution.execute(request, body); 31 | 32 | try { 33 | loggingResponse(response); 34 | }catch (Exception e){ 35 | logger.error(e.getMessage()); 36 | } 37 | 38 | 39 | return response; 40 | } 41 | 42 | private void loggingRequest(final HttpRequest request, byte[] body) { 43 | 44 | CommonLoggingRequest commonLoggingRequest = new CommonLoggingRequest(); 45 | String loggedText = commonLoggingRequest.getText(); 46 | 47 | loggedText += "\n======Request to Server====== (Thread ID : " + Thread.currentThread().getId() + ")\n"; 48 | loggedText += "Headers: " + request.getHeaders() + "\n"; 49 | loggedText += "Request Method: " + request.getMethod() + "\n"; 50 | loggedText += "Request URI: " + request.getURI() + "\n"; 51 | loggedText += "Request body: " + 52 | (body.length == 0 ? "No body value" : new String(body, StandardCharsets.UTF_8)) + "\n"; 53 | loggedText += "======Request to Server======\n"; 54 | 55 | logger.trace(loggedText); 56 | } 57 | 58 | private void loggingResponse(ClientHttpResponse response) throws IOException { 59 | 60 | CommonLoggingRequest commonLoggingRequest = new CommonLoggingRequest(); 61 | String loggedText = commonLoggingRequest.getText(); 62 | 63 | final String body = getBody(response); 64 | 65 | loggedText += "\n======Response from Server====== (Thread ID : " + Thread.currentThread().getId() + ")\n"; 66 | loggedText += "Headers: " + response.getHeaders() + "\n"; 67 | loggedText += "Response Status : " + response.getRawStatusCode() + "\n"; 68 | loggedText += "Response body: " + body + "\n"; 69 | loggedText += "======Response from Server======\n"; 70 | 71 | logger.trace(loggedText); 72 | } 73 | 74 | 75 | private String getBody(@NonNull final ClientHttpResponse response) throws IOException { 76 | try (BufferedReader br = new BufferedReader(new InputStreamReader(response.getBody()))) { 77 | return br.readLine(); 78 | } 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/logger/module/SchedulingTaskErrorLogConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.logger.module; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | 8 | @Slf4j 9 | public class SchedulingTaskErrorLogConfig { 10 | //private static final Logger logger = LoggerFactory.getLogger(SchedulingTaskErrorLogConfig.class); 11 | 12 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/ErrorMessagesContainedException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.EasyPlusErrorMessages; 4 | 5 | public abstract class ErrorMessagesContainedException extends RuntimeException { 6 | 7 | protected EasyPlusErrorMessages easyPlusErrorMessages; 8 | 9 | public ErrorMessagesContainedException(){ 10 | 11 | } 12 | public ErrorMessagesContainedException(String message){ 13 | super(message); 14 | } 15 | public ErrorMessagesContainedException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | public ErrorMessagesContainedException(EasyPlusErrorMessages easyPlusErrorMessages){ 19 | this.easyPlusErrorMessages = easyPlusErrorMessages; 20 | } 21 | public EasyPlusErrorMessages getErrorMessages() { 22 | return easyPlusErrorMessages; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/auth/AlreadySocialRegisteredException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.auth; 2 | 3 | public class AlreadySocialRegisteredException extends RuntimeException { 4 | public AlreadySocialRegisteredException(String message) { 5 | super(message); 6 | } 7 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/auth/CustomAuthGuardException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.auth; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthorizationException; 4 | 5 | public class CustomAuthGuardException extends EasyPlusOauth2AuthorizationException { 6 | 7 | public CustomAuthGuardException(String message) { 8 | super(message); 9 | } 10 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/auth/NoSocialRegisteredException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.auth; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | // UNAUTHORIZED : 401 7 | // FORBIDDEN : 403 8 | @ResponseStatus(value = HttpStatus.UNAUTHORIZED) 9 | public class NoSocialRegisteredException extends RuntimeException { 10 | public NoSocialRegisteredException(String message) { 11 | super(message); 12 | } 13 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/auth/OtpValueUnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.auth; 2 | 3 | 4 | import org.springframework.security.core.AuthenticationException; 5 | 6 | public class OtpValueUnauthorizedException extends AuthenticationException { 7 | public OtpValueUnauthorizedException(String message) { 8 | super(message); 9 | } 10 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/auth/SocialEmailNotProvidedException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.auth; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | 7 | @ResponseStatus(value = HttpStatus.UNAUTHORIZED) 8 | public class SocialEmailNotProvidedException extends RuntimeException { 9 | public SocialEmailNotProvidedException(String message) { 10 | super(message); 11 | } 12 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/auth/SocialUnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.auth; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | // UNAUTHORIZED : 401 7 | // FORBIDDEN : 403 8 | @ResponseStatus(value = HttpStatus.UNAUTHORIZED) 9 | public class SocialUnauthorizedException extends RuntimeException { 10 | public SocialUnauthorizedException(String message) { 11 | super(message); 12 | } 13 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/auth/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.auth; 2 | 3 | 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.security.access.AccessDeniedException; 6 | import org.springframework.web.bind.annotation.ResponseStatus; 7 | 8 | @ResponseStatus(value = HttpStatus.FORBIDDEN) 9 | public class UnauthorizedException extends AccessDeniedException { 10 | public UnauthorizedException(String message) { 11 | super(message); 12 | } 13 | public UnauthorizedException() { 14 | super(null); 15 | } 16 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/auth/UserDeletedException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.auth; 2 | 3 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 4 | 5 | 6 | public class UserDeletedException extends UsernameNotFoundException { 7 | public UserDeletedException(String message) { 8 | super(message); 9 | } 10 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/auth/UserRestoredException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.auth; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.security.access.AccessDeniedException; 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | 8 | @ResponseStatus(value = HttpStatus.FORBIDDEN) 9 | public class UserRestoredException extends AccessDeniedException { 10 | public UserRestoredException(String message) { 11 | super(message); 12 | } 13 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/data/AlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.data; 2 | 3 | public class AlreadyExistsException extends RuntimeException { 4 | public AlreadyExistsException(String message) { 5 | super(message); 6 | } 7 | public AlreadyExistsException(String message, Throwable cause) { 8 | super(message, cause); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/data/AlreadyInProgressException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.data; 2 | 3 | 4 | public class AlreadyInProgressException extends RuntimeException { 5 | public AlreadyInProgressException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/data/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.data; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.EasyPlusErrorMessages; 4 | import com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.ErrorMessagesContainedException; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.web.bind.annotation.ResponseStatus; 7 | 8 | @ResponseStatus(value = HttpStatus.NOT_FOUND) 9 | public class ResourceNotFoundException extends ErrorMessagesContainedException { 10 | public ResourceNotFoundException() { 11 | } 12 | 13 | public ResourceNotFoundException(String message) { 14 | super(message); 15 | } 16 | 17 | public ResourceNotFoundException(String message, Throwable cause) { 18 | super(message, cause); 19 | } 20 | 21 | public ResourceNotFoundException(EasyPlusErrorMessages easyPlusErrorMessages) { 22 | super(easyPlusErrorMessages); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/payload/SearchFilterException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.payload; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(value = HttpStatus.BAD_REQUEST) 8 | public class SearchFilterException extends JsonProcessingException{ 9 | public SearchFilterException(String message){ 10 | 11 | super(message); 12 | } 13 | public SearchFilterException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/push/PushException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.push; 2 | 3 | public class PushException extends RuntimeException { 4 | public PushException(String message) { 5 | super(message); 6 | } 7 | public PushException(String message, Throwable cause) { 8 | super(message, cause); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/exception/util/EncodingProcessException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.util; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ExceptionMessageInterface; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) 8 | public class EncodingProcessException extends RuntimeException { 9 | 10 | private ExceptionMessageInterface exceptionMessage; 11 | private String value; 12 | private String message; 13 | 14 | public ExceptionMessageInterface getExceptionMessage() { 15 | return exceptionMessage; 16 | } 17 | 18 | public String getValue() { 19 | return value; 20 | } 21 | 22 | public EncodingProcessException(ExceptionMessageInterface exceptionMessage) { 23 | super(exceptionMessage.getMessage()); 24 | this.exceptionMessage = exceptionMessage; 25 | this.value = ""; 26 | } 27 | 28 | public EncodingProcessException(ExceptionMessageInterface exceptionMessage, String value) { 29 | super(exceptionMessage.getMessage()); 30 | this.exceptionMessage = exceptionMessage; 31 | this.value = value; 32 | } 33 | 34 | public EncodingProcessException(String message) { 35 | super(message); 36 | this.message = message; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/response/error/message/GeneralErrorMessage.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.response.error.message; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ExceptionMessageInterface; 4 | import lombok.Getter; 5 | import org.springframework.http.HttpStatus; 6 | 7 | @Getter 8 | public enum GeneralErrorMessage implements ExceptionMessageInterface { 9 | 10 | UNHANDLED_ERROR("G_000", "An unhandled error has occurred.", "We apologize for the inconvenience. If the problem persists upon retry, please contact the administrator. Log checking is required.", HttpStatus.INTERNAL_SERVER_ERROR), 11 | 12 | NULL_VALUE_FOUND("G_001", "A null value was detected.", "We apologize for the inconvenience. If the problem persists upon retry, please contact the administrator. Log checking is required.", HttpStatus.BAD_REQUEST), 13 | DUPLICATE_VALUE_FOUND("G_002", "A duplicate value was detected.", "A duplicate value was detected.", HttpStatus.CONFLICT), 14 | INPUT_VALUE_INVALID("G_003", "The input value is invalid.", "The input value is invalid.", HttpStatus.BAD_REQUEST), 15 | 16 | ACCOUNT_NOT_FOUND("S_000", "The member could not be found.", "The member could not be found.", HttpStatus.NOT_FOUND), 17 | EMAIL_DUPLICATION("S_001", "The email is duplicated.", "The email is duplicated.", HttpStatus.CONFLICT), 18 | PASSWORD_FAILED_EXCEEDED("S_002", "Password failure attempts have been exceeded.", "Password failure attempts have been exceeded.", HttpStatus.BAD_REQUEST); 19 | 20 | 21 | private final String code; 22 | private final String message; 23 | private final String userMessage; 24 | private final HttpStatus status; 25 | 26 | GeneralErrorMessage(String code, String message, String userMessage, HttpStatus status) { 27 | this.code = code; 28 | this.message = message; 29 | this.userMessage = userMessage; 30 | this.status = status; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/aop/CustomSecurityPointCutImpl.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.aop; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.aop.SecurityPointCut; 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity.EasyPlusAuthorization; 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity.EasyPlusClient; 6 | import jakarta.annotation.Nullable; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import org.springframework.stereotype.Service; 10 | 11 | /* 12 | * 13 | * The functionality is already implemented in the library's 14 | * 'aop.security.config.io.github.patternhelloworld.securityhelper.oauth2.api.DefaultSecurityPointCut'. 15 | * 16 | * Create this class only if you need a custom implementation that differs from the default. 17 | */ 18 | @Service 19 | @RequiredArgsConstructor 20 | public class CustomSecurityPointCutImpl implements SecurityPointCut { 21 | @Override 22 | public @Nullable T afterTokensSaved(@Nullable EasyPlusAuthorization easyPlusAuthorization, @Nullable EasyPlusClient easyPlusClient) { 23 | // Implement what you need right after tokens are persisted. 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/cors/CorsFilter.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.cors; 2 | 3 | import jakarta.servlet.*; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | 7 | import java.io.IOException; 8 | 9 | 10 | public class CorsFilter implements Filter { 11 | 12 | @Override 13 | public void init(FilterConfig fc) throws ServletException { 14 | } 15 | 16 | @Override 17 | public void doFilter(ServletRequest req, ServletResponse resp, 18 | FilterChain chain) throws IOException, ServletException { 19 | HttpServletResponse response = (HttpServletResponse) resp; 20 | HttpServletRequest request = (HttpServletRequest) req; 21 | 22 | response.setHeader("Access-Control-Allow-Origin", "*"); 23 | response.setHeader("Access-Control-Allow-Methods", "PUT, PATCH,POST,GET,OPTIONS,DELETE"); 24 | response.setHeader("Access-Control-Max-Age", "3600"); 25 | response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN"); 26 | 27 | if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { 28 | response.setStatus(HttpServletResponse.SC_OK); 29 | } else { 30 | chain.doFilter(req, resp); 31 | } 32 | 33 | } 34 | 35 | @Override 36 | public void destroy() { 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/cors/CorsFilterConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.cors; 2 | 3 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.core.Ordered; 7 | 8 | @Configuration 9 | public class CorsFilterConfig { 10 | 11 | @Bean 12 | public FilterRegistrationBean corsFilterRegistration() { 13 | FilterRegistrationBean registration = new FilterRegistrationBean<>(); 14 | registration.setFilter(new CorsFilter()); 15 | registration.addUrlPatterns("/*"); 16 | registration.setOrder(Ordered.HIGHEST_PRECEDENCE); 17 | return registration; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/guard/AccessTokenUserInfoConverter.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.core.EasyPlusUserInfo; 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage; 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; 6 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.EasyPlusErrorMessages; 7 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthenticationException; 8 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl; 9 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.userdetail.ConditionalDetailsService; 10 | 11 | 12 | import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; 13 | import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal; 14 | 15 | public class AccessTokenUserInfoConverter { 16 | 17 | public static EasyPlusUserInfo from(Object principal, 18 | ConditionalDetailsService conditionalDetailsService, 19 | OAuth2AuthorizationServiceImpl authorizationService, ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService) { 20 | 21 | EasyPlusUserInfo easyPlusUserInfo; 22 | if (principal instanceof EasyPlusUserInfo) { 23 | return ((EasyPlusUserInfo) principal); 24 | } else if (principal instanceof OAuth2IntrospectionAuthenticatedPrincipal) { 25 | String userName = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getUsername(); 26 | String clientId = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getClientId(); 27 | String appToken = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getAttribute("App-Token"); 28 | 29 | OAuth2Authorization oAuth2Authorization = authorizationService.findByUserNameAndClientIdAndAppToken(userName, clientId, appToken); 30 | if (oAuth2Authorization == null) { 31 | throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_FAILURE)); 32 | } 33 | 34 | return (EasyPlusUserInfo) conditionalDetailsService.loadUserByUsername(userName, clientId); 35 | }else { 36 | throw new EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages.builder().message("Wrong principal : " + principal.toString()).userMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_TOKEN_ERROR.getMessage()).build()); 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/guard/CustomAuthenticationPrincipal.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.PARAMETER) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface CustomAuthenticationPrincipal { 11 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/guard/CustomizedUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.admin.entity.Admin; 4 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Customer; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | 11 | @Getter 12 | @Setter 13 | public class CustomizedUserInfo implements Serializable { 14 | 15 | public enum UserType { 16 | ADMIN("client_admin"), 17 | CUSTOMER("client_customer"); 18 | 19 | private final String value; 20 | 21 | UserType(String value) { 22 | this.value = value; 23 | } 24 | 25 | public String getValue() { 26 | return value; 27 | } 28 | } 29 | 30 | private UserType userType; 31 | 32 | private Long id; 33 | private String username; 34 | private String name; 35 | 36 | private LocalDateTime deletedAt; 37 | 38 | public CustomizedUserInfo(Customer customer) { 39 | 40 | this.userType = UserType.CUSTOMER; 41 | 42 | this.id = customer.getId(); 43 | this.username = customer.getIdName(); 44 | this.name = customer.getName(); 45 | 46 | this.deletedAt = customer.getDeletedAt(); 47 | 48 | } 49 | 50 | public CustomizedUserInfo(Admin admin) { 51 | 52 | this.userType = UserType.ADMIN; 53 | 54 | this.id = admin.getId(); 55 | this.username = admin.getIdName(); 56 | 57 | this.deletedAt = admin.getDeletedAt(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/guard/UserCustomerOnly.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.METHOD) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface UserCustomerOnly { 11 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/guard/UserCustomerOnlyImpl.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.core.EasyPlusUserInfo; 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthorizationException; 6 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl; 7 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.userdetail.ConditionalDetailsService; 8 | import lombok.RequiredArgsConstructor; 9 | import org.aspectj.lang.ProceedingJoinPoint; 10 | import org.aspectj.lang.annotation.Around; 11 | import org.aspectj.lang.annotation.Aspect; 12 | import org.springframework.security.core.context.SecurityContextHolder; 13 | import org.springframework.stereotype.Component; 14 | 15 | @Aspect 16 | @Component 17 | @RequiredArgsConstructor 18 | public class UserCustomerOnlyImpl { 19 | 20 | private final OAuth2AuthorizationServiceImpl authorizationService; 21 | private final ConditionalDetailsService conditionalDetailsService; 22 | private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService; 23 | 24 | @Around("@annotation(com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard.UserCustomerOnly)") 25 | public Object check(ProceedingJoinPoint joinPoint) throws Throwable { 26 | 27 | Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 28 | EasyPlusUserInfo easyPlusUserInfo = AccessTokenUserInfoConverter.from(principal, conditionalDetailsService, authorizationService, iSecurityUserExceptionMessageService); 29 | 30 | if(easyPlusUserInfo != null && ((CustomizedUserInfo) easyPlusUserInfo.getCustomizedUserInfo()).getUserType() != CustomizedUserInfo.UserType.CUSTOMER){ 31 | // Authorization 32 | throw new EasyPlusOauth2AuthorizationException("ID \"" + easyPlusUserInfo.getUsername() + "\" : Not in Customer Group"); 33 | } 34 | 35 | return joinPoint.proceed(); 36 | } 37 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/guard/resolver/AccessTokenUserInfoArgumentResolver.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard.resolver; 2 | 3 | 4 | import com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard.CustomAuthenticationPrincipal; 5 | import com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard.AccessTokenUserInfoConverter; 6 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; 7 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl; 8 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.userdetail.ConditionalDetailsService; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.core.MethodParameter; 11 | import org.springframework.security.core.context.SecurityContextHolder; 12 | 13 | import org.springframework.stereotype.Component; 14 | import org.springframework.web.bind.support.WebDataBinderFactory; 15 | import org.springframework.web.context.request.NativeWebRequest; 16 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 17 | import org.springframework.web.method.support.ModelAndViewContainer; 18 | 19 | @Component 20 | @RequiredArgsConstructor 21 | public class AccessTokenUserInfoArgumentResolver implements HandlerMethodArgumentResolver { 22 | 23 | private final OAuth2AuthorizationServiceImpl authorizationService; 24 | private final ConditionalDetailsService conditionalDetailsService; 25 | private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService; 26 | 27 | @Override 28 | public boolean supportsParameter(MethodParameter parameter) { 29 | return parameter.hasParameterAnnotation(CustomAuthenticationPrincipal.class); 30 | } 31 | 32 | @Override 33 | public Object resolveArgument(MethodParameter parameter, 34 | ModelAndViewContainer mavContainer, 35 | NativeWebRequest webRequest, 36 | WebDataBinderFactory binderFactory) throws Exception { 37 | 38 | Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 39 | 40 | return AccessTokenUserInfoConverter.from(principal, conditionalDetailsService, authorizationService, iSecurityUserExceptionMessageService); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/message/CustomSecurityMessageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.message; 2 | 3 | 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage; 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | /* 9 | * 10 | * The functionality is already implemented in the library's 11 | * 'message.security.config.io.github.patternhelloworld.securityhelper.oauth2.api.DefaultSecurityMessageServiceImpl'. 12 | * 13 | * Create this class only if you need a custom implementation that differs from the default. 14 | */ 15 | @Configuration 16 | public class CustomSecurityMessageServiceImpl implements ISecurityUserExceptionMessageService { 17 | 18 | @Override 19 | public String getUserMessage(DefaultSecurityUserExceptionMessage defaultSecurityUserExceptionMessage) { 20 | try { 21 | CustomSecurityUserExceptionMessage customMessage = CustomSecurityUserExceptionMessage.valueOf(defaultSecurityUserExceptionMessage.name()); 22 | return customMessage.getMessage(); 23 | } catch (IllegalArgumentException e) { 24 | return defaultSecurityUserExceptionMessage.getMessage(); 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/message/CustomSecurityUserExceptionMessage.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.message; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ExceptionMessageInterface; 4 | 5 | public enum CustomSecurityUserExceptionMessage implements ExceptionMessageInterface { 6 | 7 | AUTHENTICATION_LOGIN_FAILURE("1Authentication information is not valid. Please check and try again."), 8 | AUTHENTICATION_LOGIN_ERROR("1An error occurred during authentication. If the problem persists, please contact customer service."), 9 | AUTHENTICATION_TOKEN_FAILURE("1The authentication token has expired. Please log in again."), 10 | AUTHENTICATION_TOKEN_ERROR("1There was a problem verifying the authentication token. Please log in again."), 11 | AUTHORIZATION_FAILURE("1You do not have access permissions. Please request this from the administrator."), 12 | AUTHORIZATION_ERROR("1An error occurred with access permissions. If the problem persists, please contact customer service."), 13 | 14 | // ID PASSWORD 15 | AUTHENTICATION_ID_NO_EXISTS("1The specified ID does not exist."), 16 | AUTHENTICATION_WRONG_ID_PASSWORD("1User information could not be verified. Please check your ID or password. If the problem persists, please contact customer service."), 17 | AUTHENTICATION_PASSWORD_FAILED_EXCEEDED("1The number of password attempts has been exceeded."), 18 | 19 | // Wrong Authorization Code 20 | AUTHENTICATION_INVALID_RESPONSE_TYPE("1The specified Response Type is invalid."), 21 | AUTHENTICATION_INVALID_AUTHORIZATION_CODE("1The specified Authorization Code is invalid."), 22 | AUTHENTICATION_EXPIRED_AUTHORIZATION_CODE("1The specified Authorization Code has been expired."), 23 | AUTHENTICATION_INVALID_REDIRECT_URI("1The specified Redirect URI is invalid."), 24 | AUTHENTICATION_SCOPES_NOT_APPROVED("1The specified Scopes are not approved."), 25 | // CLIENT ID, SECRET 26 | AUTHENTICATION_WRONG_CLIENT_ID_SECRET("1Client information is not verified."), 27 | 28 | // GRANT TYPE 29 | AUTHENTICATION_WRONG_GRANT_TYPE("1Wrong Grant Type detected."), 30 | AUTHENTICATION_WRONG_COMBINATION_OF_GRANT_TYPE_RESPONSE_TYPE("1Grant Type doesn't match response type."), 31 | 32 | // OAuth2 : Authorization Code 33 | AUTHENTICATION_AUTHORIZATION_CODE_REQUEST_WRONG_METHOD("1Wrong Authorization Code request."), 34 | AUTHENTICATION_CLIENT_ID_MISSING("1Client ID is missing."), 35 | AUTHENTICATION_REDIRECT_URI_MISSING("1Redirect URI is missing."), 36 | AUTHENTICATION_STATE_MISSING("1State is missing."), 37 | AUTHENTICATION_REGISTERED_CLIENT_NOT_FOUND("1Registered client is missing or invalid"), 38 | AUTHENTICATION_AUTHORIZATION_CODE_MISSING("1Authorization Code is missing."); 39 | 40 | private String message; 41 | 42 | @Override 43 | public String getMessage() { 44 | return message; 45 | } 46 | 47 | CustomSecurityUserExceptionMessage(String message) { 48 | this.message = message; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/response/CustomAuthenticationEntryPointImpl.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.response; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.security.core.AuthenticationException; 9 | import org.springframework.security.web.AuthenticationEntryPoint; 10 | import org.springframework.web.servlet.HandlerExceptionResolver; 11 | 12 | import java.io.IOException; 13 | 14 | /* 15 | * 16 | * The functionality is already implemented in the library's 17 | * 'authentication.resource.response.security.config.io.github.patternhelloworld.securityhelper.oauth2.api.DefaultAuthenticationEntryPoint'. 18 | * 19 | * Create this class only if you need a custom implementation that differs from the default. 20 | */ 21 | @Configuration 22 | @RequiredArgsConstructor 23 | public class CustomAuthenticationEntryPointImpl implements AuthenticationEntryPoint { 24 | 25 | @Qualifier("handlerExceptionResolver") 26 | private final HandlerExceptionResolver resolver; 27 | 28 | @Override 29 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException { 30 | resolver.resolveException(request, response, null, ex); 31 | } 32 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/response/CustomWebAuthenticationSuccessHandlerImpl.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.response; 2 | 3 | 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; 5 | import jakarta.servlet.ServletException; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import jakarta.servlet.http.HttpServletResponse; 8 | import lombok.RequiredArgsConstructor; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Qualifier; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.context.annotation.Primary; 14 | import org.springframework.security.core.Authentication; 15 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken; 16 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken; 17 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 18 | 19 | import java.io.IOException; 20 | 21 | @Primary 22 | @Qualifier("webAuthenticationSuccessHandler") 23 | @Configuration 24 | @RequiredArgsConstructor 25 | public class CustomWebAuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler { 26 | 27 | private static final Logger logger = LoggerFactory.getLogger(CustomWebAuthenticationSuccessHandlerImpl.class); 28 | 29 | private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService; 30 | 31 | @Override 32 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { 33 | if(authentication instanceof OAuth2AuthorizationCodeRequestAuthenticationToken) { 34 | request.getRequestDispatcher("/login").forward(request, response); 35 | }else if(authentication instanceof OAuth2AuthorizationCodeAuthenticationToken) { 36 | OAuth2AuthorizationCodeAuthenticationToken oAuth2AuthorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) authentication; 37 | 38 | String redirectUri = oAuth2AuthorizationCodeAuthenticationToken.getRedirectUri(); 39 | String authorizationCode = oAuth2AuthorizationCodeAuthenticationToken.getCode(); 40 | String state = oAuth2AuthorizationCodeAuthenticationToken.getAdditionalParameters().get("state").toString(); 41 | 42 | response.sendRedirect(redirectUri+"?code="+authorizationCode+"&state="+state); 43 | }else{ 44 | logger.error("Wrong Authentication Type : {}", authentication.getClass()); 45 | request.getRequestDispatcher("/login").forward(request, response); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/serivce/permission/CustomAuthorityService.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.serivce.permission; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthenticationException; 4 | import org.springframework.security.core.Authentication; 5 | import org.springframework.security.core.context.SecurityContextHolder; 6 | import org.springframework.stereotype.Service; 7 | 8 | @Service 9 | public class CustomAuthorityService { 10 | 11 | public boolean hasAnyUserRole() { 12 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 13 | if(authentication == null){ 14 | throw new EasyPlusOauth2AuthenticationException(); 15 | } 16 | if (authentication.getAuthorities() == null) { 17 | return false; 18 | } 19 | return authentication.getAuthorities().stream() 20 | .anyMatch(authority -> authority.getAuthority().endsWith("_USER")); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/serivce/userdetail/CustomUserDetailsServiceFactory.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.serivce.userdetail; 2 | 3 | 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.userdetail.UserDetailsServiceFactory; 5 | import com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard.CustomizedUserInfo; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | @Service 15 | public class CustomUserDetailsServiceFactory implements UserDetailsServiceFactory { 16 | 17 | private final Map userDetailsServiceMap; 18 | 19 | @Autowired 20 | public CustomUserDetailsServiceFactory(List userDetailsServices) { 21 | userDetailsServiceMap = new HashMap<>(); 22 | for (UserDetailsService userDetailsService : userDetailsServices) { 23 | if (userDetailsService instanceof AdminDetailsService) { 24 | userDetailsServiceMap.put(CustomizedUserInfo.UserType.ADMIN.getValue(), userDetailsService); 25 | } else if (userDetailsService instanceof CustomerDetailsService) { 26 | userDetailsServiceMap.put(CustomizedUserInfo.UserType.CUSTOMER.getValue(), userDetailsService); 27 | } 28 | } 29 | } 30 | 31 | @Override 32 | public UserDetailsService getUserDetailsService(String clientId) { 33 | return userDetailsServiceMap.get(clientId); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/web/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.web; 2 | 3 | 4 | import com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.guard.resolver.AccessTokenUserInfoArgumentResolver; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 8 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 9 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 10 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 11 | 12 | import java.util.List; 13 | 14 | @RequiredArgsConstructor 15 | @Configuration 16 | @EnableWebMvc 17 | public class WebMvcConfig implements WebMvcConfigurer { 18 | 19 | private final AccessTokenUserInfoArgumentResolver accessTokenUserInfoArgumentResolver; 20 | 21 | 22 | @Override 23 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 24 | registry.addResourceHandler("/docs/**").addResourceLocations("classpath:/static/docs/"); 25 | } 26 | 27 | @Override 28 | public void addArgumentResolvers(List resolvers){ 29 | resolvers.add(accessTokenUserInfoArgumentResolver); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/dao/AdminRepository.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.dao; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.admin.entity.Admin; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.querydsl.QuerydslPredicateExecutor; 6 | 7 | import java.util.Optional; 8 | 9 | 10 | public interface AdminRepository extends JpaRepository, QuerydslPredicateExecutor { 11 | Optional findByIdName(String idName); 12 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/dao/AdminRepositorySupport.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.dao; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.config.database.CommonQuerydslRepositorySupport; 4 | import com.patternhelloworld.securityhelper.oauth2.client.config.response.error.exception.data.ResourceNotFoundException; 5 | import com.patternhelloworld.securityhelper.oauth2.client.domain.admin.entity.Admin; 6 | import com.querydsl.jpa.impl.JPAQueryFactory; 7 | import jakarta.persistence.EntityManager; 8 | import jakarta.persistence.PersistenceContext; 9 | import org.springframework.beans.factory.annotation.Qualifier; 10 | import org.springframework.stereotype.Repository; 11 | 12 | @Repository 13 | public class AdminRepositorySupport extends CommonQuerydslRepositorySupport { 14 | 15 | private final JPAQueryFactory jpaQueryFactory; 16 | 17 | private final AdminRepository adminRepository; 18 | 19 | private EntityManager entityManager; 20 | 21 | public AdminRepositorySupport(AdminRepository adminRepository, @Qualifier("authJpaQueryFactory") JPAQueryFactory jpaQueryFactory) { 22 | 23 | super(Admin.class); 24 | this.adminRepository = adminRepository; 25 | this.jpaQueryFactory = jpaQueryFactory; 26 | } 27 | 28 | @Override 29 | @PersistenceContext(unitName = "commonEntityManager") 30 | public void setEntityManager(EntityManager entityManager) { 31 | super.setEntityManager(entityManager); 32 | this.entityManager = entityManager; 33 | } 34 | 35 | 36 | public Admin findById(Long id) throws ResourceNotFoundException { 37 | return adminRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("findById - Admin not found for this id :: " + id)); 38 | } 39 | 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/dao/AdminRoleRepository.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.dao; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.admin.entity.Admin; 4 | import com.patternhelloworld.securityhelper.oauth2.client.domain.admin.entity.AdminRole; 5 | import com.patternhelloworld.securityhelper.oauth2.client.domain.role.entity.Role; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Modifying; 8 | import org.springframework.data.jpa.repository.Query; 9 | import org.springframework.stereotype.Repository; 10 | 11 | import java.util.List; 12 | import java.util.Optional; 13 | 14 | @Repository 15 | public interface AdminRoleRepository extends JpaRepository { 16 | Optional findByAdminAndRole(Admin admin, Role role); 17 | 18 | @Modifying 19 | @Query("DELETE FROM AdminRole a WHERE a.admin = :admin") 20 | void deleteByAdmin(Admin admin); 21 | 22 | 23 | Optional> findByAdmin(Admin admin); 24 | } 25 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/dto/AdminCreate.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.dto; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class AdminCreate { 9 | public String idName; 10 | public String name; 11 | } 12 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/dto/AdminSearchFilter.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | @JsonIgnoreProperties(ignoreUnknown = true) 10 | public class AdminSearchFilter { 11 | 12 | private String idName; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/entity/Admin.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.*; 5 | import org.hibernate.annotations.CreationTimestamp; 6 | import org.hibernate.annotations.UpdateTimestamp; 7 | 8 | import java.sql.Timestamp; 9 | import java.time.LocalDateTime; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | @Entity 14 | @Table(name="admin") 15 | @Getter 16 | @Setter 17 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 18 | @Builder 19 | @AllArgsConstructor 20 | public class Admin 21 | { 22 | @Id 23 | @GeneratedValue(strategy= GenerationType.IDENTITY) 24 | private Long id; 25 | 26 | @Column(name="id_name") 27 | private String idName; 28 | 29 | @Column(name="description") 30 | private String description; 31 | 32 | @Embedded 33 | private Password password; 34 | 35 | @OneToMany(mappedBy = "admin") 36 | private final List adminRoles = new ArrayList<>(); 37 | 38 | @Column(name="created_at", updatable = false) 39 | @CreationTimestamp 40 | private Timestamp createdAt; 41 | 42 | @Column(name="updated_at") 43 | @UpdateTimestamp 44 | private Timestamp updatedAt; 45 | 46 | @Column(name="deleted_at") 47 | private LocalDateTime deletedAt; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/entity/AdminRole.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.entity; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.role.entity.Role; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.persistence.*; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | import org.hibernate.annotations.CreationTimestamp; 9 | import org.hibernate.annotations.UpdateTimestamp; 10 | 11 | import java.sql.Timestamp; 12 | 13 | 14 | @Getter 15 | @Setter 16 | @Entity 17 | @Table(name ="admin_role") 18 | public class AdminRole { 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private Long id; 22 | 23 | @ManyToOne 24 | @JoinColumn(name = "admin_id") 25 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 26 | private Admin admin; 27 | 28 | @ManyToOne 29 | @JoinColumn(name = "role_id") 30 | private Role role; 31 | 32 | @Column(name="created_at", updatable = false) 33 | @CreationTimestamp 34 | private Timestamp createdAt; 35 | 36 | @Column(name="updated_at") 37 | @UpdateTimestamp 38 | private Timestamp updatedAt; 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/entity/Password.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.entity; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.admin.exception.PasswordFailedExceededOauth2AuthenticationException; 4 | import jakarta.persistence.Column; 5 | import jakarta.persistence.Embeddable; 6 | import lombok.AccessLevel; 7 | import lombok.Builder; 8 | import lombok.Getter; 9 | import lombok.NoArgsConstructor; 10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | 15 | @Embeddable 16 | @Getter 17 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 18 | public class Password { 19 | 20 | @Column(name = "password") 21 | private String value; 22 | 23 | @Column(name = "password_expiration_date") 24 | private LocalDateTime expirationDate; 25 | 26 | @Column(name = "password_failed_count") 27 | private int failedCount; 28 | 29 | @Column(name = "password_ttl") 30 | private long ttl; 31 | 32 | @Builder 33 | public Password(final String value) { 34 | this.ttl = 1209_604; // 1209_604 is 14 days 35 | this.value = encodePassword(value); 36 | this.expirationDate = extendExpirationDate(); 37 | } 38 | 39 | public boolean isMatched(final String rawPassword) { 40 | if (failedCount >= 5) 41 | throw new PasswordFailedExceededOauth2AuthenticationException(); 42 | 43 | final boolean matches = isMatches(rawPassword); 44 | updateFailedCount(matches); 45 | return matches; 46 | } 47 | 48 | public void changePassword(final String newPassword, final String oldPassword) { 49 | if (isMatched(oldPassword)) { 50 | value = encodePassword(newPassword); 51 | extendExpirationDate(); 52 | } 53 | } 54 | 55 | 56 | public boolean isExpiration() { 57 | return LocalDateTime.now().isAfter(expirationDate); 58 | } 59 | 60 | private LocalDateTime extendExpirationDate() { 61 | return LocalDateTime.now().plusSeconds(ttl); 62 | } 63 | 64 | private String encodePassword(final String password) { 65 | 66 | return new BCryptPasswordEncoder().encode(password); 67 | } 68 | 69 | private void updateFailedCount(boolean matches) { 70 | if (matches) 71 | resetFailedCount(); 72 | else 73 | increaseFailCount(); 74 | } 75 | 76 | private void resetFailedCount() { 77 | this.failedCount = 0; 78 | } 79 | 80 | private void increaseFailCount() { 81 | this.failedCount++; 82 | } 83 | 84 | private boolean isMatches(String rawPassword) { 85 | return new BCryptPasswordEncoder().matches(rawPassword, this.value); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/exception/AccountNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.exception; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class AccountNotFoundException extends RuntimeException { 7 | 8 | private long id; 9 | private String idName; 10 | 11 | public AccountNotFoundException(long id) { 12 | this.id = id; 13 | } 14 | 15 | public AccountNotFoundException(String idName) { 16 | this.idName = idName; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/exception/IdNameDuplicationException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.exception; 2 | 3 | 4 | import lombok.Getter; 5 | 6 | @Getter 7 | public class IdNameDuplicationException extends RuntimeException { 8 | 9 | private String idName; 10 | private String field; 11 | 12 | public IdNameDuplicationException(String idName) { 13 | this.field = "idName"; 14 | this.idName = idName; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/admin/exception/PasswordFailedExceededOauth2AuthenticationException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.admin.exception; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.EasyPlusErrorMessages; 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthenticationException; 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage; 6 | 7 | public class PasswordFailedExceededOauth2AuthenticationException extends EasyPlusOauth2AuthenticationException { 8 | public PasswordFailedExceededOauth2AuthenticationException() { 9 | super(DefaultSecurityUserExceptionMessage.AUTHENTICATION_PASSWORD_FAILED_EXCEEDED.getMessage()); 10 | } 11 | 12 | public PasswordFailedExceededOauth2AuthenticationException(String message) { 13 | super(message); 14 | } 15 | 16 | public PasswordFailedExceededOauth2AuthenticationException(EasyPlusErrorMessages easyPlusErrorMessages) { 17 | super(easyPlusErrorMessages); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/common/api/BaseApi.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.common.api; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.security.access.prepost.PreAuthorize; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | public class BaseApi { 10 | 11 | @Value("${spring.profiles.active}") 12 | private String activeProfile; 13 | 14 | @PreAuthorize("isAuthenticated()") 15 | @GetMapping("/systemProfile") 16 | public String checkAuthenticated () { 17 | return activeProfile; 18 | } 19 | 20 | @PreAuthorize("@customAuthorityService.hasAnyUserRole()") 21 | @GetMapping("/systemProfile2") 22 | public String checkPermissions () { 23 | return activeProfile; 24 | } 25 | 26 | 27 | 28 | @GetMapping("/") 29 | public String home () { 30 | return "home"; 31 | } 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/common/dto/DateRangeFilter.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.common.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class DateRangeFilter { 7 | private String column; 8 | private String startDate; 9 | private String endDate; 10 | } 11 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/common/dto/SorterValueFilter.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.common.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class SorterValueFilter { 7 | private String column; 8 | private Boolean asc; 9 | } 10 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/common/web/AuthorizationCodeRequestWeb.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.common.web; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.ui.Model; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | 8 | @Controller 9 | public class AuthorizationCodeRequestWeb { 10 | @GetMapping("/login") 11 | public String loginPage(HttpServletRequest request, Model model) { 12 | Object errorMessages = request.getAttribute("errorMessages"); 13 | if (errorMessages != null) { 14 | model.addAttribute("errorMessages", errorMessages); 15 | } 16 | return "login"; 17 | } 18 | 19 | @GetMapping("/consent") 20 | public String consentPage(HttpServletRequest request, Model model) { 21 | Object errorMessages = request.getAttribute("errorMessages"); 22 | if (errorMessages != null) { 23 | model.addAttribute("errorMessages", errorMessages); 24 | } 25 | return "consent"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/dao/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.dao; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Customer; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.querydsl.QuerydslPredicateExecutor; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | 11 | public interface CustomerRepository extends JpaRepository, QuerydslPredicateExecutor { 12 | 13 | Optional findByIdName(String idName); 14 | 15 | Optional> findByNameAndHp(String name, String hp); 16 | 17 | Boolean existsByIdName(String idName); 18 | Boolean existsByHp(String hp); 19 | 20 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/dao/CustomerRoleRepository.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.dao; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.CustomerRole; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | 7 | public interface CustomerRoleRepository extends JpaRepository { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/dto/AdminType.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.dto; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Customer; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | public class AdminType { 10 | 11 | private Boolean isSuperAdmin; 12 | private Boolean isAdmin; 13 | private Customer customer; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/dto/CustomerReqDTO.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.dto; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Customer; 4 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Password; 5 | import com.patternhelloworld.securityhelper.oauth2.client.util.CustomUtils; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.NotNull; 8 | import jakarta.validation.constraints.Past; 9 | import lombok.AccessLevel; 10 | import lombok.AllArgsConstructor; 11 | import lombok.Getter; 12 | import lombok.NoArgsConstructor; 13 | 14 | import java.time.LocalDate; 15 | 16 | public class CustomerReqDTO { 17 | 18 | @Getter 19 | public static class CreateSocialNew { 20 | @NotBlank 21 | private String idName; 22 | } 23 | 24 | @Getter 25 | public static class Create { 26 | public String appToken; 27 | 28 | @NotBlank 29 | public String name; 30 | 31 | @NotNull 32 | @Past 33 | private LocalDate birthday; 34 | 35 | @NotBlank 36 | private String sex; 37 | 38 | @NotBlank 39 | private String hp; 40 | 41 | 42 | private String idName; 43 | 44 | 45 | @NotBlank 46 | private String password; 47 | private String email; 48 | 49 | @NotBlank 50 | private String ci; 51 | 52 | public Customer toEntity() { 53 | Customer.CustomerBuilder builder = Customer.builder() 54 | .name(this.name) 55 | .birthday(this.birthday) 56 | .sex(this.sex) 57 | .hp(CustomUtils.removeSpecialCharacters(this.hp)) 58 | .idName(this.idName) 59 | .password(Password.builder().value(this.password).build()) 60 | .email(this.email); 61 | return builder.build(); 62 | } 63 | } 64 | 65 | 66 | @Getter 67 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 68 | @AllArgsConstructor 69 | public static class Update { 70 | 71 | @NotBlank 72 | private String idName; 73 | @NotBlank 74 | public String name; 75 | @NotBlank 76 | public String hp; 77 | 78 | public String email; 79 | 80 | } 81 | 82 | @Getter 83 | public static class UpdatePasswordAndEmail { 84 | @NotBlank 85 | private String password; 86 | public String email; 87 | } 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/dto/CustomerResDTO.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Customer; 5 | import lombok.Getter; 6 | 7 | import java.sql.Timestamp; 8 | 9 | public class CustomerResDTO { 10 | 11 | @Getter 12 | public static class Id { 13 | private Long id; 14 | 15 | public Id(Long id) { 16 | this.id = id; 17 | } 18 | public Id(Customer customer) { 19 | this.id = customer.getId(); 20 | } 21 | } 22 | 23 | @Getter 24 | public static class IdAdminId { 25 | private Long id; 26 | private Long adminId; 27 | 28 | public IdAdminId(Long id, Long adminId) { 29 | this.id = id; 30 | this.adminId = adminId; 31 | } 32 | } 33 | 34 | 35 | 36 | 37 | @Getter 38 | public static class IdNameWithAccessTokenRemainingSeconds { 39 | 40 | private Long id; 41 | private String idName; 42 | private String name; 43 | private String email; 44 | private Integer accessTokenRemainingSeconds; 45 | 46 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") 47 | private Timestamp createdAt; 48 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") 49 | private Timestamp updatedAt; 50 | 51 | public IdNameWithAccessTokenRemainingSeconds(Customer customer, Integer accessTokenRemainingSeconds) { 52 | this.id = customer.getId(); 53 | this.idName = customer.getIdName(); 54 | this.name = customer.getName(); 55 | this.email = customer.getEmail(); 56 | this.accessTokenRemainingSeconds = accessTokenRemainingSeconds; 57 | this.createdAt = customer.getCreatedAt(); 58 | this.updatedAt = customer.getUpdatedAt(); 59 | } 60 | } 61 | 62 | 63 | } 64 | 65 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/entity/Customer.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.patternhelloworld.securityhelper.oauth2.client.config.response.error.CustomExceptionUtils; 5 | import com.patternhelloworld.securityhelper.oauth2.client.domain.admin.entity.Admin; 6 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.dto.CustomerReqDTO; 7 | import jakarta.persistence.*; 8 | import lombok.*; 9 | import org.hibernate.annotations.CreationTimestamp; 10 | import org.hibernate.annotations.DynamicUpdate; 11 | import org.hibernate.annotations.UpdateTimestamp; 12 | import org.springframework.format.annotation.DateTimeFormat; 13 | 14 | import java.sql.Timestamp; 15 | import java.time.LocalDate; 16 | import java.time.LocalDateTime; 17 | import java.time.format.DateTimeFormatter; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | @Entity 22 | @Table(name="customer") 23 | @Getter 24 | @Setter 25 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 26 | @Builder 27 | @AllArgsConstructor 28 | @DynamicUpdate 29 | public class Customer 30 | { 31 | @Id 32 | @GeneratedValue(strategy= GenerationType.IDENTITY) 33 | private Long id; 34 | 35 | @Column(name="id_name") 36 | private String idName; 37 | @Column(name="deleted_id_name") 38 | private String deletedIdName; 39 | 40 | private String email; 41 | @Embedded 42 | private Password password; 43 | private String name; 44 | private String hp; 45 | 46 | @DateTimeFormat(pattern = "yyyy-MM-dd") 47 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") 48 | private LocalDate birthday; 49 | @Column(length = 1) 50 | private String sex; 51 | 52 | 53 | @OneToMany(mappedBy = "customer") 54 | private final List customerRoles = new ArrayList<>(); 55 | 56 | 57 | @Column(name="created_at", updatable = false) 58 | @CreationTimestamp 59 | private Timestamp createdAt; 60 | 61 | @Column(name="updated_at") 62 | @UpdateTimestamp 63 | private Timestamp updatedAt; 64 | 65 | 66 | @Column(name="deleted_at") 67 | private LocalDateTime deletedAt; 68 | 69 | @ManyToOne(fetch = FetchType.LAZY) 70 | @JoinColumn(name = "delete_admin_id") 71 | private Admin deleteAdmin; 72 | 73 | @Column(name="delete_admin_id", insertable = false, updatable = false) 74 | private Long deleteAdminId; 75 | 76 | 77 | public void updateCustomer(CustomerReqDTO.Update dto) { 78 | this.idName = dto.getIdName(); 79 | this.name = dto.getName(); 80 | this.hp = dto.getHp(); 81 | this.email = dto.getEmail(); 82 | } 83 | 84 | 85 | public void updatePasswordAndEmail(CustomerReqDTO.UpdatePasswordAndEmail dto) { 86 | this.password = Password.builder().value(dto.getPassword()).build(); 87 | this.email = dto.getEmail(); 88 | } 89 | 90 | public String getOneWeekAfterDeletedAsString() { 91 | 92 | try { 93 | 94 | LocalDateTime oneWeekAfter = this.deletedAt.plusWeeks(1); 95 | 96 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 97 | 98 | return oneWeekAfter.format(formatter); 99 | 100 | }catch (Exception e){ 101 | CustomExceptionUtils.createNonStoppableErrorMessage(e.getMessage(), e); 102 | return null; 103 | } 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/entity/CustomerRole.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.role.entity.Role; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.persistence.*; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | 9 | 10 | @Getter 11 | @Setter 12 | @Entity 13 | @Table(name ="customer_role") 14 | public class CustomerRole { 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | private Long id; 18 | 19 | @ManyToOne 20 | @JoinColumn(name = "customer_id") 21 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 22 | private Customer customer; 23 | 24 | @ManyToOne 25 | @JoinColumn(name = "role_id") 26 | private Role role; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/entity/Email.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import jakarta.persistence.Column; 5 | import jakarta.persistence.Embeddable; 6 | import lombok.AccessLevel; 7 | import lombok.Builder; 8 | import lombok.Getter; 9 | import lombok.NoArgsConstructor; 10 | 11 | 12 | @Embeddable 13 | @Getter 14 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 15 | @JsonIgnoreProperties({"host", "id"}) 16 | public class Email { 17 | 18 | @jakarta.validation.constraints.Email 19 | @Column(name = "email", nullable = false, unique = true) 20 | private String value; 21 | 22 | @Builder 23 | public Email(String value) { 24 | this.value = value; 25 | } 26 | 27 | public static Email of(String email) { 28 | return new Email(email); 29 | } 30 | 31 | public String getHost() { 32 | int index = value.indexOf("@"); 33 | return value.substring(index); 34 | } 35 | 36 | public String getId() { 37 | int index = value.indexOf("@"); 38 | return value.substring(0, index); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/entity/Password.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.exception.PasswordFailedExceededException; 4 | import jakarta.persistence.Column; 5 | import jakarta.persistence.Embeddable; 6 | import lombok.AccessLevel; 7 | import lombok.Builder; 8 | import lombok.Getter; 9 | import lombok.NoArgsConstructor; 10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | 15 | @Embeddable 16 | @Getter 17 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 18 | public class Password { 19 | 20 | @Column(name = "password") 21 | private String value; 22 | 23 | @Column(name = "password_changed_at") 24 | private LocalDateTime passwordChangedAt; 25 | 26 | @Column(name = "password_failed_count") 27 | private int failedCount; 28 | 29 | 30 | @Builder 31 | public Password(final String value) { 32 | this.value = encodePassword(value); 33 | this.passwordChangedAt = LocalDateTime.now(); 34 | } 35 | 36 | public boolean isMatched(final String rawPassword) { 37 | if (failedCount >= 5) 38 | throw new PasswordFailedExceededException(); 39 | 40 | final boolean matches = isMatches(rawPassword); 41 | updateFailedCount(matches); 42 | return matches; 43 | } 44 | 45 | private String encodePassword(final String password) { 46 | 47 | return new BCryptPasswordEncoder().encode(password); 48 | } 49 | 50 | private void updateFailedCount(boolean matches) { 51 | if (matches) 52 | resetFailedCount(); 53 | else 54 | increaseFailCount(); 55 | } 56 | 57 | private void resetFailedCount() { 58 | this.failedCount = 0; 59 | } 60 | 61 | private void increaseFailCount() { 62 | this.failedCount++; 63 | } 64 | 65 | private boolean isMatches(String rawPassword) { 66 | return new BCryptPasswordEncoder().matches(rawPassword, this.value); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/exception/AccountNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.exception; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Email; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | public class AccountNotFoundException extends RuntimeException { 8 | 9 | private long id; 10 | private Email email; 11 | 12 | public AccountNotFoundException(long id) { 13 | this.id = id; 14 | } 15 | 16 | public AccountNotFoundException(Email email) { 17 | this.email = email; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/exception/EmailDuplicationException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.exception; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Email; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | public class EmailDuplicationException extends RuntimeException { 8 | 9 | private Email email; 10 | private String field; 11 | 12 | public EmailDuplicationException(Email email) { 13 | this.field = "email"; 14 | this.email = email; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/exception/PasswordFailedExceededException.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.exception; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.config.response.error.message.GeneralErrorMessage; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | public class PasswordFailedExceededException extends RuntimeException { 8 | 9 | private GeneralErrorMessage generalErrorMessage; 10 | 11 | public PasswordFailedExceededException() { 12 | this.generalErrorMessage = GeneralErrorMessage.PASSWORD_FAILED_EXCEEDED; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/validator/Password.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.validator; 2 | 3 | 4 | import jakarta.validation.Constraint; 5 | import jakarta.validation.Payload; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.Target; 10 | 11 | import static java.lang.annotation.ElementType.*; 12 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 13 | 14 | @Documented 15 | @Constraint(validatedBy = PasswordValidator.class) 16 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) 17 | @Retention(RUNTIME) 18 | public @interface Password { 19 | 20 | String message() default "Password is not allow"; 21 | 22 | Class[] groups() default {}; 23 | 24 | Class[] payload() default {}; 25 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/customer/validator/PasswordValidator.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.customer.validator; 2 | 3 | import jakarta.validation.ConstraintValidator; 4 | import jakarta.validation.ConstraintValidatorContext; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.text.MessageFormat; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | @Component 12 | public class PasswordValidator implements ConstraintValidator { 13 | 14 | private static final int MIN_SIZE = 9; 15 | private static final int MAX_SIZE = 20; 16 | private static final String regexPassword = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[$@!%*#?&])[A-Za-z[0-9]$@!%*#?&]{" + MIN_SIZE 17 | + "," + MAX_SIZE + "}$"; 18 | private static final String regexConsecutiveNumber = "([0-9])\\1"; 19 | 20 | @Override 21 | public void initialize(Password constraintAnnotation) { 22 | } 23 | 24 | @Override 25 | public boolean isValid(String password, ConstraintValidatorContext context) { 26 | boolean isValidPassword = password.matches(regexPassword) && !findConsecutiveNumber(password); 27 | if (!isValidPassword) { 28 | context.disableDefaultConstraintViolation(); 29 | context.buildConstraintViolationWithTemplate( 30 | MessageFormat.format("Please enter a password that includes numbers, letters, and special characters of at least {0} characters and no more than {1} characters, and is not a sequence of consecutive numbers.", MIN_SIZE, MAX_SIZE)) 31 | .addConstraintViolation(); 32 | } 33 | return isValidPassword; 34 | } 35 | 36 | private boolean findConsecutiveNumber(String password){ 37 | Pattern pattern = Pattern.compile(regexConsecutiveNumber); 38 | Matcher matcher = pattern.matcher(password); 39 | return matcher.find(); 40 | } 41 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/role/api/RoleApi.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.role.api; 2 | 3 | import org.springframework.web.bind.annotation.*; 4 | 5 | 6 | @RestController 7 | @RequestMapping("/api/v1") 8 | 9 | public class RoleApi { 10 | 11 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/role/dao/RoleRepository.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.role.dao; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.role.entity.Role; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | 7 | public interface RoleRepository extends JpaRepository { 8 | 9 | } -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/role/entity/Role.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.role.entity; 2 | 3 | import jakarta.persistence.*; 4 | import jakarta.validation.constraints.NotEmpty; 5 | import lombok.AccessLevel; 6 | import lombok.Builder; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | 10 | 11 | @Entity 12 | @Getter 13 | @Table(name = "role") 14 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 15 | public class Role { 16 | 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | 21 | @Column(nullable = false, unique = true) 22 | @NotEmpty 23 | private String name; 24 | 25 | private String description; 26 | 27 | @Builder 28 | public Role(Long id, String name, String description) { 29 | this.id = id; 30 | this.name = name; 31 | this.description = description; 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/domain/traditionaloauth/api/v1/TraditionalOauthApi.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.domain.traditionaloauth.api.v1; 2 | 3 | 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthenticationException; 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage; 6 | 7 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; 8 | import io.github.patternhelloworld.securityhelper.oauth2.api.domain.traditionaloauth.dto.SpringSecurityTraditionalOauthDTO; 9 | import io.github.patternhelloworld.securityhelper.oauth2.api.domain.traditionaloauth.service.TraditionalOauthService; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | @RestController 14 | @RequiredArgsConstructor 15 | public class TraditionalOauthApi { 16 | 17 | private final TraditionalOauthService traditionalOauthService; 18 | private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService; 19 | 20 | @PostMapping("/api/v1/traditional-oauth/token") 21 | public SpringSecurityTraditionalOauthDTO.TokenResponse createAccessToken( 22 | @ModelAttribute SpringSecurityTraditionalOauthDTO.TokenRequest tokenRequest, 23 | @RequestHeader("Authorization") String authorizationHeader){ 24 | switch(tokenRequest.getGrant_type()) { 25 | case "password": 26 | return traditionalOauthService.createAccessToken(tokenRequest, authorizationHeader); 27 | case "refresh_token": 28 | return traditionalOauthService.refreshAccessToken(tokenRequest, authorizationHeader); 29 | default: 30 | throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_WRONG_GRANT_TYPE)); 31 | } 32 | } 33 | 34 | /* @PostMapping("/api/v1/traditional-oauth/authorization-code") 35 | public SpringSecurityTraditionalOauthDTO.AuthorizationCodeResponse createAuthorizationCode( 36 | @ModelAttribute SpringSecurityTraditionalOauthDTO.AuthorizationCodeRequest authorizationCodeRequest, 37 | @RequestHeader("Authorization") String authorizationHeader){ 38 | 39 | // authorization_code 생성 40 | return traditionalOauthService.createAuthorizationCode(authorizationCodeRequest, authorizationHeader); 41 | }*/ 42 | 43 | } 44 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/util/AES256.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util; 2 | 3 | import javax.crypto.BadPaddingException; 4 | import javax.crypto.Cipher; 5 | import javax.crypto.IllegalBlockSizeException; 6 | import javax.crypto.NoSuchPaddingException; 7 | import javax.crypto.spec.IvParameterSpec; 8 | import javax.crypto.spec.SecretKeySpec; 9 | import java.io.UnsupportedEncodingException; 10 | import java.security.InvalidAlgorithmParameterException; 11 | import java.security.InvalidKeyException; 12 | import java.security.NoSuchAlgorithmException; 13 | import java.util.Base64; 14 | 15 | public class AES256 { 16 | 17 | private final static String alg = "AES/CBC/PKCS5Padding"; 18 | private final static String key = "oLwZAyXT9KwaFpTVtsMAVK4qUR6ptjox"; 19 | private final static String iv = key.substring(0, 16); // 16byte 20 | 21 | public static String encrypt(String text) throws NoSuchPaddingException, NoSuchAlgorithmException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException { 22 | Cipher cipher = Cipher.getInstance(alg); 23 | SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); 24 | IvParameterSpec ivParamSpec = new IvParameterSpec(iv.getBytes()); 25 | cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParamSpec); 26 | 27 | byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8")); 28 | return Base64.getEncoder().encodeToString(encrypted); 29 | } 30 | 31 | public static String decrypt(String text) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { 32 | Cipher cipher = Cipher.getInstance(alg); 33 | SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); 34 | IvParameterSpec ivParamSpec = new IvParameterSpec(iv.getBytes()); 35 | cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParamSpec); 36 | 37 | byte[] decodedBytes = Base64.getDecoder().decode(text); 38 | byte[] decrypted = cipher.doFinal(decodedBytes); 39 | return new String(decrypted, "UTF-8"); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/util/BitConverter.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util; 2 | 3 | import java.nio.ByteOrder; 4 | 5 | public class BitConverter { 6 | public static byte[] getBytes(boolean x){ 7 | return new byte[] {(byte) (x ? 1 : 0)}; 8 | } 9 | 10 | public static byte[] getBytes(char c){ 11 | return new byte[] {(byte) (c & 0xff), (byte)(c >> 8 & 0xff)}; 12 | } 13 | 14 | public static byte[] getBytes(double x){ 15 | return getBytes(Double.doubleToRawLongBits(x)); 16 | } 17 | 18 | public static byte[] getBytes(short x){ 19 | return new byte[]{(byte) (x >>> 8), (byte) x}; 20 | } 21 | 22 | public static byte[] getBytes(int x){ 23 | return new byte[]{(byte) (x >>> 24), (byte) (x >>> 16), (byte) (x >>> 8), (byte)x}; 24 | } 25 | 26 | public static byte[] getBytes(long x){ 27 | return new byte[]{(byte) (x >>> 56), (byte) (x >>> 48), (byte) (x >>> 40), (byte) (x >>> 32), 28 | (byte) (x >>> 24), (byte) (x >>> 16), (byte) (x >>> 8), (byte)x}; 29 | } 30 | public static byte[] getBytes(float x){ 31 | return getBytes(Float.floatToRawIntBits(x)); 32 | } 33 | 34 | public static boolean IsLittleEndian(){ 35 | return ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/util/CommonConstant.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util; 2 | 3 | public class CommonConstant { 4 | public static final String COMMON_PAGE_NUM = "1"; 5 | public static final String COMMON_PAGE_SIZE = "10"; 6 | 7 | public static final String COMMON_PAGE_SIZE_DEFAULT_MAX = "1000"; 8 | 9 | public static final String SUPER_ADMIN_ROLE_NAME = "SUPER_ADMIN"; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/util/PaginationUtil.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util; 2 | 3 | import com.mysema.commons.lang.Assert; 4 | import com.querydsl.jpa.JPQLQuery; 5 | import com.querydsl.jpa.sql.JPASQLQuery; 6 | import org.springframework.data.domain.Page; 7 | import org.springframework.data.domain.PageImpl; 8 | import org.springframework.data.domain.PageRequest; 9 | import org.springframework.data.jpa.repository.support.Querydsl; 10 | 11 | public class PaginationUtil { 12 | 13 | private final Querydsl querydsl; 14 | 15 | public PaginationUtil(Querydsl querydsl) { 16 | this.querydsl = querydsl; 17 | } 18 | 19 | public PaginationUtil() { 20 | this.querydsl = null; 21 | } 22 | 23 | public Page applyPagination(JPASQLQuery query, int pageNum, int pageSize, boolean skipPagination) { 24 | if (skipPagination) { 25 | pageNum = Integer.parseInt(CommonConstant.COMMON_PAGE_NUM); 26 | pageSize = Integer.parseInt(CommonConstant.COMMON_PAGE_SIZE_DEFAULT_MAX); 27 | } 28 | 29 | long totalElements = query.fetch().size(); 30 | PageRequest pageRequest = PageRequest.of(pageNum - 1, pageSize); 31 | 32 | Assert.notNull(pageRequest, "Pageable must not be null!"); 33 | Assert.notNull(query, "JPASQLQuery must not be null!"); 34 | 35 | if(pageRequest.isPaged()) { 36 | query.offset(pageRequest.getOffset()); 37 | query.limit(pageRequest.getPageSize()); 38 | } 39 | 40 | return new PageImpl<>(query.fetch(), pageRequest, totalElements); 41 | } 42 | 43 | public Page applyPagination(JPQLQuery query, int pageNum, int pageSize, boolean skipPagination) { 44 | if (skipPagination) { 45 | pageNum = Integer.parseInt(CommonConstant.COMMON_PAGE_NUM); 46 | pageSize = Integer.parseInt(CommonConstant.COMMON_PAGE_SIZE_DEFAULT_MAX); 47 | } 48 | 49 | long totalElements = query.fetchCount(); 50 | PageRequest pageRequest = PageRequest.of(pageNum - 1, pageSize); 51 | 52 | return new PageImpl<>(querydsl.applyPagination(pageRequest, query).fetch(), pageRequest, totalElements); 53 | } 54 | 55 | public Page applyPagination(JPQLQuery query, int pageNum, int pageSize, boolean skipPagination, boolean skipCalculateTotalElements) { 56 | if (skipPagination) { 57 | pageNum = Integer.parseInt(CommonConstant.COMMON_PAGE_NUM); 58 | pageSize = Integer.parseInt(CommonConstant.COMMON_PAGE_SIZE_DEFAULT_MAX); 59 | } 60 | long totalElements; 61 | if(skipCalculateTotalElements){ 62 | totalElements = 10000; 63 | }else{ 64 | totalElements = query.fetchCount(); 65 | } 66 | 67 | PageRequest pageRequest = PageRequest.of(pageNum - 1, pageSize); 68 | 69 | return new PageImpl<>(querydsl.applyPagination(pageRequest, query).fetch(), pageRequest, totalElements); 70 | } 71 | 72 | public Page applyPagination(JPQLQuery query, int pageNum, int pageSize, boolean skipPagination, Long totalElements) { 73 | if (skipPagination) { 74 | pageNum = Integer.parseInt(CommonConstant.COMMON_PAGE_NUM); 75 | pageSize = Integer.parseInt(CommonConstant.COMMON_PAGE_SIZE_DEFAULT_MAX); 76 | } 77 | 78 | PageRequest pageRequest = PageRequest.of(pageNum - 1, pageSize); 79 | 80 | return new PageImpl<>(querydsl.applyPagination(pageRequest, query).fetch(), pageRequest, totalElements); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/util/PathUtils.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.util.AntPathMatcher; 5 | 6 | import java.util.List; 7 | import java.util.concurrent.atomic.AtomicReference; 8 | 9 | @Component 10 | public class PathUtils { 11 | 12 | public static boolean matches(List patterns, String url) { 13 | AntPathMatcher pathMatcher = new AntPathMatcher(); 14 | AtomicReference result = new AtomicReference<>(false); 15 | patterns.forEach(pattern -> { 16 | if (pathMatcher.match(pattern, url)) { 17 | result.set(true); 18 | } 19 | }); 20 | return result.get(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/util/ReflectionUtils.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | public class ReflectionUtils { 6 | 7 | public static String getFieldValue(Object object, String fieldName) { 8 | try { 9 | Field field = object.getClass().getDeclaredField(fieldName); 10 | field.setAccessible(true); 11 | Object value = field.get(object); 12 | return value != null ? value.toString() : null; 13 | } catch (NoSuchFieldException | IllegalAccessException e) { 14 | // Handle or log the exception as needed 15 | return null; 16 | } 17 | } 18 | public static void setFieldValue(Object object, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { 19 | 20 | Field field = object.getClass().getDeclaredField(fieldName); 21 | field.setAccessible(true); 22 | field.set(object, value); 23 | 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/util/ValidatorUtils.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | @Component 9 | public class ValidatorUtils { 10 | private static final int MIN_SIZE = 9; 11 | private static final int MAX_SIZE = 20; 12 | private static final String regexPassword = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[$@!%*#?&])[A-Za-z[0-9]$@!%*#?&]{" + MIN_SIZE 13 | + "," + MAX_SIZE + "}$"; 14 | private static final String regexConsecutiveNumber = "([0-9])\\1"; 15 | 16 | public boolean isValid(String password) { 17 | return password.matches(regexPassword) && !findConsecutiveNumber(password) && !findContinuousPwd(password) ; 18 | } 19 | 20 | private boolean findConsecutiveNumber(String password){ 21 | Pattern pattern = Pattern.compile(regexConsecutiveNumber); 22 | Matcher matcher = pattern.matcher(password); 23 | return matcher.find(); 24 | } 25 | 26 | public boolean findContinuousPwd(String pwd) { 27 | int o = 0; 28 | int d = 0; 29 | int p = 0; 30 | int n = 0; 31 | int limit = 3; 32 | 33 | for(int i=0; i 0 && (p = o - tempVal) > -2 && (n = p == d ? n + 1 :0) > limit -3) { 36 | return true; 37 | } 38 | d = p; 39 | o = tempVal; 40 | } 41 | return false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /client/src/main/resources/templates/css/signin.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | display: flex; 8 | align-items: start; 9 | padding-top: 100px; 10 | background-color: #f5f5f5; 11 | } 12 | 13 | .form-signin { 14 | max-width: 330px; 15 | padding: 15px; 16 | } 17 | 18 | .form-signin .form-floating:focus-within { 19 | z-index: 2; 20 | } 21 | 22 | .form-signin input[type="username"] { 23 | margin-bottom: -1px; 24 | border-bottom-right-radius: 0; 25 | border-bottom-left-radius: 0; 26 | } 27 | 28 | .form-signin input[type="password"] { 29 | margin-bottom: 10px; 30 | border-top-left-radius: 0; 31 | border-top-right-radius: 0; 32 | } 33 | -------------------------------------------------------------------------------- /client/src/main/resources/templates/device-activate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spring Authorization Server sample 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Device Activation

14 |

Enter the activation code to authorize the device.

15 |
16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 |
28 | Devices 29 |
30 |
31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /client/src/main/resources/templates/device-activated.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spring Authorization Server sample 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Success!

14 |

15 | You have successfully activated your device.
16 | Please return to your device to continue. 17 |

18 |
19 |
20 | Devices 21 |
22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /client/src/main/resources/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Error 7 | 56 | 57 | 58 |
59 |
60 | An error occurred 61 |
62 |
    63 |
  • 64 |
65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /client/src/test/java/com/patternhelloworld/securityhelper/oauth2/client/SpringSecurityOauth2PasswordJpaImplApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client; 2 | 3 | import org.assertj.core.util.Arrays; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.boot.test.web.client.TestRestTemplate; 9 | import org.springframework.test.context.junit.jupiter.SpringExtension; 10 | 11 | @ExtendWith(SpringExtension.class) 12 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 13 | public class SpringSecurityOauth2PasswordJpaImplApplicationTests { 14 | 15 | @Autowired 16 | private TestRestTemplate restTemplate; 17 | 18 | @Test 19 | public void contextLoads() { 20 | 21 | } 22 | 23 | private Boolean checkProfileValidation(String profile) { 24 | String[] profiles = new String[] {"local", "alpha", "production"}; 25 | return Arrays.asList(profiles).contains(profile); 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /client/src/test/java/com/patternhelloworld/securityhelper/oauth2/client/unit/customer/CustomerRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.unit.customer; 2 | 3 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.dao.CustomerRepository; 4 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Customer; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.junit.jupiter.SpringExtension; 10 | 11 | import java.util.Optional; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | 16 | //@DataJpaTest 17 | @ExtendWith(SpringExtension.class) 18 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 19 | public class CustomerRepositoryTest { 20 | 21 | @Autowired 22 | private CustomerRepository customerRepository; 23 | 24 | @Test 25 | public void findByIdName_test() { 26 | final String idName = "cicd@test.com"; 27 | final Customer customer = customerRepository.findByIdName(idName).get(); 28 | assertThat(customer.getIdName()).isEqualTo(idName); 29 | } 30 | 31 | @Test 32 | public void findByIdName_notFound_test() { 33 | final String nonexistentEmail = "nonexistent@test.com"; 34 | final Optional optionalCustomer = customerRepository.findByIdName(nonexistentEmail); 35 | assertThat(optionalCustomer.isPresent()).isFalse(); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /client/src/test/java/com/patternhelloworld/securityhelper/oauth2/client/util/NameGenerator.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util; 2 | 3 | import java.util.Random; 4 | 5 | public class NameGenerator { 6 | private static final String[] FIRST_NAMES = { 7 | "James", "Mary", "John", "Patricia", "Robert", "Jennifer", 8 | "Michael", "Linda", "William", "Elizabeth", "David", "Barbara", 9 | "Richard", "Susan", "Joseph", "Jessica", "Thomas", "Sarah", "Charles", "Karen" 10 | }; 11 | 12 | private static final String[] LAST_NAMES = { 13 | "Smith", "Johnson", "Williams", "Jones", "Brown", "Davis", 14 | "Miller", "Wilson", "Moore", "Taylor", "Anderson", "Thomas", 15 | "Jackson", "White", "Harris", "Martin", "Thompson", "Garcia", "Martinez", "Robinson" 16 | }; 17 | 18 | private static final Random RANDOM = new Random(); 19 | 20 | public static String generateRandomName() { 21 | String firstName = FIRST_NAMES[RANDOM.nextInt(FIRST_NAMES.length)]; 22 | String lastName = LAST_NAMES[RANDOM.nextInt(LAST_NAMES.length)]; 23 | return firstName + " " + lastName; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/test/java/com/patternhelloworld/securityhelper/oauth2/client/util/TestUtil.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 5 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 6 | import org.junit.jupiter.api.Assertions; 7 | 8 | import java.io.InputStream; 9 | import java.time.LocalDate; 10 | import java.time.format.DateTimeFormatter; 11 | import java.util.Properties; 12 | 13 | public class TestUtil { 14 | 15 | public static String asJsonString(final Object obj) { 16 | try { 17 | ObjectMapper mapper = new ObjectMapper(); 18 | JavaTimeModule module = new JavaTimeModule(); 19 | module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); 20 | mapper.registerModule(module); 21 | 22 | return mapper.writeValueAsString(obj); 23 | } catch (Exception e) { 24 | throw new RuntimeException(e); 25 | } 26 | } 27 | 28 | public static Properties loadPropertiesFromFile(String fileName) { 29 | Properties prop = new Properties(); 30 | try { 31 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); 32 | InputStream stream = loader.getResourceAsStream(fileName); 33 | prop.load(stream); 34 | stream.close(); 35 | } catch (Exception e) { 36 | String msg = String.format("Failed to load file '%s' - %s - %s", fileName, e.getClass().getName(), 37 | e.getMessage()); 38 | Assertions.fail(msg); 39 | } 40 | return prop; 41 | } 42 | 43 | public static String loadCertainPropertyValue(String key) { 44 | return loadPropertiesFromFile("application.properties").getProperty(key); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /client/src/test/java/com/patternhelloworld/securityhelper/oauth2/client/util/auth/AbstractRestDocsTests.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util.auth; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.extension.ExtendWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.restdocs.RestDocsAutoConfiguration; 7 | import org.springframework.context.annotation.Import; 8 | import org.springframework.restdocs.RestDocumentationContextProvider; 9 | import org.springframework.restdocs.RestDocumentationExtension; 10 | import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.result.MockMvcResultHandlers; 13 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 14 | import org.springframework.web.context.WebApplicationContext; 15 | import org.springframework.web.filter.CharacterEncodingFilter; 16 | 17 | import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; 18 | 19 | @Import(RestDocsAutoConfiguration.class) 20 | @ExtendWith(RestDocumentationExtension.class) 21 | public abstract class AbstractRestDocsTests { 22 | 23 | @Autowired 24 | protected RestDocumentationResultHandler restDocs; 25 | 26 | @Autowired 27 | protected MockMvc mockMvc; 28 | 29 | @BeforeEach 30 | void setUp( 31 | final WebApplicationContext context, 32 | final RestDocumentationContextProvider restDocumentation) { 33 | this.mockMvc = MockMvcBuilders.webAppContextSetup(context) 34 | .apply(documentationConfiguration(restDocumentation)) 35 | .alwaysDo(MockMvcResultHandlers.print()) 36 | .alwaysDo(restDocs) 37 | .addFilters(new CharacterEncodingFilter("UTF-8", true)) 38 | .build(); 39 | } 40 | } -------------------------------------------------------------------------------- /client/src/test/java/com/patternhelloworld/securityhelper/oauth2/client/util/auth/IntegrationMockAuth.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util.auth; 2 | 3 | import org.springframework.boot.test.web.client.TestRestTemplate; 4 | import org.springframework.test.web.servlet.MockMvc; 5 | 6 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 7 | 8 | 9 | public class IntegrationMockAuth extends AbstractMockAuth { 10 | 11 | public IntegrationMockAuth(TestRestTemplate testRestTemplate){ 12 | this.testRestTemplate = testRestTemplate; 13 | } 14 | public IntegrationMockAuth(MockMvc mockMvc){ 15 | this.mockMvc = mockMvc; 16 | } 17 | 18 | @Override 19 | public String mockAccessToken(String clientName, String clientPassword, String username, String password) throws Exception { 20 | return super.mockAccessToken(clientName, clientPassword, username, password); 21 | } 22 | 23 | @Override 24 | public String mockAccessTokenOnPersistence(String authUrl, String clientName, String clientPassword, String username, String password) throws Exception { 25 | return super.mockAccessTokenOnPersistence(authUrl, clientName, clientPassword, username, password); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/test/java/com/patternhelloworld/securityhelper/oauth2/client/util/auth/MockAuth.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util.auth; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.core.EasyPlusUserInfo; 4 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Customer; 5 | 6 | public interface MockAuth { 7 | 8 | /** 9 | * Mock @AuthenticationPrincipal 10 | */ 11 | EasyPlusUserInfo mockAuthenticationPrincipal(Customer customer); 12 | 13 | /** 14 | * Mock Customer 15 | */ 16 | Customer mockCustomerObject() throws Exception; 17 | 18 | /** 19 | * Mock AccessToken 20 | */ 21 | String mockAccessToken(String clientName, String clientPassword, String username, String password) throws Exception; 22 | 23 | /** 24 | * Mock AccessToken on entity (select from DB) 25 | */ 26 | String mockAccessTokenOnPersistence(String authUrl, String clientName, String clientPassword, String username, String password) throws Exception; 27 | } 28 | -------------------------------------------------------------------------------- /client/src/test/java/com/patternhelloworld/securityhelper/oauth2/client/util/auth/UnitMockAuth.java: -------------------------------------------------------------------------------- 1 | package com.patternhelloworld.securityhelper.oauth2.client.util.auth; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.core.EasyPlusUserInfo; 4 | import com.patternhelloworld.securityhelper.oauth2.client.domain.customer.entity.Customer; 5 | 6 | public class UnitMockAuth extends AbstractMockAuth { 7 | 8 | 9 | 10 | public UnitMockAuth(){ 11 | 12 | } 13 | 14 | @Override 15 | public EasyPlusUserInfo mockAuthenticationPrincipal(Customer customer) { 16 | return super.mockAuthenticationPrincipal(customer); 17 | } 18 | 19 | @Override 20 | public Customer mockCustomerObject() { 21 | return super.mockCustomerObject(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | /.docker/ssl/.gitkeep 2 | !/client/.docker/ssl/.gitkeep 3 | 4 | /target/ 5 | /client/target/ 6 | 7 | # docker 8 | /.docker/env/* 9 | !/.docker/env/.gitkeep 10 | /.docker/ssh/* 11 | !/.docker/ssh/.gitkeep 12 | 13 | ### STS ### 14 | /.apt_generated 15 | /.classpath 16 | /.factorypath 17 | /.project 18 | /.settings 19 | /.springBeans 20 | /.sts4-cache 21 | 22 | /client/.apt_generated 23 | /client/.classpath 24 | /client/.factorypath 25 | /client/.project 26 | /client/.settings 27 | /client/.springBeans 28 | /client/.sts4-cache 29 | 30 | # Mac 31 | /.DS_Store 32 | /client/.DS_Store 33 | 34 | ### IntelliJ IDEA ### 35 | /.idea 36 | /*.iws 37 | /*.iml 38 | /*.ipr 39 | 40 | /client/.idea 41 | /client/*.iws 42 | /client/*.iml 43 | /client/*.ipr 44 | 45 | # logs 46 | /logs/* 47 | !/logs/.gitkeep 48 | 49 | /client/logs/* 50 | !/client/logs/.gitkeep 51 | 52 | # files 53 | /files/* 54 | !/files/.gitkeep 55 | 56 | -------------------------------------------------------------------------------- /lib/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patternhelloworld/spring-oauth2-easyplus/1626d56b176212712df72e7b1d4ef389b709a504/lib/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /lib/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /lib/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.6.3-openjdk-17-slim AS build 2 | 3 | ARG PROJECT_ROOT_IN_CONTAINER 4 | 5 | # In the ./src/main/resources folder, 1) application.properties, 2) logback-spring.xml and 3) [the filename of 'server.ssl.key-store' in your properties] should be located. 6 | COPY .. $PROJECT_ROOT_IN_CONTAINER 7 | USER root 8 | WORKDIR $PROJECT_ROOT_IN_CONTAINER 9 | 10 | RUN --mount=type=cache,target=/root/.m2 mvn -f $PROJECT_ROOT_IN_CONTAINER/pom.xml clean install 11 | 12 | FROM openjdk:17-alpine 13 | 14 | ARG PROJECT_ROOT_IN_CONTAINER 15 | ARG FILE_STORAGE_ROOT_IN_CONTAINER 16 | ARG JVM_XMS 17 | ARG JVM_XMX 18 | 19 | COPY --from=build $PROJECT_ROOT_IN_CONTAINER/ $PROJECT_ROOT_IN_CONTAINER 20 | 21 | USER root 22 | WORKDIR $PROJECT_ROOT_IN_CONTAINER 23 | 24 | RUN cp $PROJECT_ROOT_IN_CONTAINER/target/*.jar /app.jar 25 | 26 | RUN ln -s $PROJECT_ROOT_IN_CONTAINER/.docker/entrypoint/run-app.sh /run-app.sh 27 | 28 | RUN apk --no-cache add curl bash fontconfig ttf-dejavu 29 | 30 | ENV PROJECT_ROOT_IN_CONTAINER=$PROJECT_ROOT_IN_CONTAINER 31 | ENV FILE_STORAGE_ROOT_IN_CONTAINER=$FILE_STORAGE_ROOT_IN_CONTAINER 32 | ENV JVM_XMS=$JVM_XMS 33 | ENV JVM_XMX=$JVM_XMX 34 | ENV RESOURCES_TYPE=$RESOURCES_TYPE 35 | 36 | ENTRYPOINT sh /run-app.sh $PROJECT_ROOT_IN_CONTAINER $FILE_STORAGE_ROOT_IN_CONTAINER $JVM_XMS $JVM_XMX && /bin/sh -------------------------------------------------------------------------------- /lib/deploy.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set local_maven_repo="C:\Users\Andrew Kang\.m2\repository\com\patternhelloworld\securityhelper\oauth2\spring-oauth2-easyplus" 3 | mvnw.cmd -DaltDeploymentRepository=snapshot-repo::default::file://%local_maven_repo%/snapshots clean deploy -------------------------------------------------------------------------------- /lib/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | local_maven_repo='/mnt/c/Users/Andrew\sKang/.m2/repository/com/patternhelloworld/securityhelper/oauth2/spring-oauth2-easyplus' 3 | mvn -DaltDeploymentRepository=snapshot-repo::default::file://${local_maven_repo}/snapshots clean deploy 4 | 5 | -------------------------------------------------------------------------------- /lib/files/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patternhelloworld/spring-oauth2-easyplus/1626d56b176212712df72e7b1d4ef389b709a504/lib/files/.gitkeep -------------------------------------------------------------------------------- /lib/logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patternhelloworld/spring-oauth2-easyplus/1626d56b176212712df72e7b1d4ef389b709a504/lib/logs/.gitkeep -------------------------------------------------------------------------------- /lib/lombok.config: -------------------------------------------------------------------------------- 1 | lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Value 2 | lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier -------------------------------------------------------------------------------- /lib/src/docs/asciidoc/api-app.adoc: -------------------------------------------------------------------------------- 1 | = Spring Oauth2 EasyPlus 2 | :doctype: book 3 | :icons: font 4 | :source-highlighter: highlightjs 5 | :toc: left 6 | :toclevels: 4 7 | :sectnums: 8 | :sectlinks: 9 | :sectanchors: 10 | 11 | == Notice 12 | - ``/api/v1/traditional-oauth/token`` has the same function as ``/oauth2/token``, which is included in Spring Security, which can be more regarded as secure. 13 | 14 | == Authentication 15 | 16 | 17 | === Access Token 18 | ==== Request 19 | ===== Payload 20 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-access-token/http-request.adoc[] 21 | ====== Header 22 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-access-token/request-headers.adoc[] 23 | ====== Parameters 24 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-access-token/form-parameters.adoc[] 25 | ====== Body 26 | 'application/x-www-form-urlencoded' 27 | 28 | ==== Response 29 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-access-token/response-body.adoc[] 30 | 31 | 32 | === Refresh Token 33 | 34 | ==== Request 35 | ===== Payload 36 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-refresh-token/http-request.adoc[] 37 | ====== Header 38 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-refresh-token/request-headers.adoc[] 39 | ====== Parameters 40 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-refresh-token/form-parameters.adoc[] 41 | ====== Body 42 | 'application/x-www-form-urlencoded' 43 | 44 | ==== Response 45 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-refresh-token/response-body.adoc[] 46 | 47 | 48 | === Logout 49 | 50 | ==== Request 51 | ===== Payload 52 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-customer-logout/http-request.adoc[] 53 | ====== Header 54 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-customer-logout/request-headers.adoc[] 55 | ====== Parameters 56 | 57 | X 58 | 59 | ====== Body 60 | 61 | X 62 | 63 | ==== Response 64 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-customer-logout/response-body.adoc[] 65 | include::../../../../client/target/generated-snippets/token-integration-test/test_-same-app-tokens-use-same-access-token_exposed/oauth-customer-logout/response-fields.adoc[] -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/logger/EasyPlusSecurityLogConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.logger; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | @Slf4j 8 | public final class EasyPlusSecurityLogConfig { 9 | 10 | private static final Logger logger = LoggerFactory.getLogger(EasyPlusSecurityLogConfig.class); 11 | 12 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/aop/DefaultSecurityPointCut.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.aop; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity.EasyPlusAuthorization; 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity.EasyPlusClient; 5 | import jakarta.annotation.Nullable; 6 | 7 | public class DefaultSecurityPointCut implements SecurityPointCut { 8 | @Override 9 | public @Nullable T afterTokensSaved(@Nullable EasyPlusAuthorization easyPlusAuthorization, @Nullable EasyPlusClient easyPlusClient) { 10 | return null; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/aop/SecurityPointCut.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.aop; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity.EasyPlusAuthorization; 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity.EasyPlusClient; 5 | import jakarta.annotation.Nullable; 6 | 7 | public interface SecurityPointCut { 8 | @Nullable T afterTokensSaved(@Nullable EasyPlusAuthorization easyPlusAuthorization, @Nullable EasyPlusClient easyPlusClient); 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/converter/auth/endpoint/TokenRequestAfterClientBasicSecretAuthenticatedConverter.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.converter.auth.endpoint; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.util.EasyPlusOAuth2EndpointUtils; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.oauth2.core.ClientAuthenticationMethod; 8 | import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; 9 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; 10 | import org.springframework.security.web.authentication.AuthenticationConverter; 11 | 12 | import java.util.Map; 13 | 14 | @RequiredArgsConstructor 15 | public final class TokenRequestAfterClientBasicSecretAuthenticatedConverter implements AuthenticationConverter { 16 | 17 | @Override 18 | public Authentication convert(HttpServletRequest request) { 19 | 20 | Map allParameters = EasyPlusOAuth2EndpointUtils.getApiParametersContainingEasyPlusHeaders(request); 21 | 22 | // The client_id has already been parsed by ClientSecretBasicAuthenticationConverter. 23 | // Therefore, there is no need to validate the client_id again at this point. 24 | String clientId = allParameters.get("client_id").toString(); 25 | 26 | // All token requests are "CLIENT_SECRET_BASIC" 27 | ClientAuthenticationMethod clientAuthenticationMethod = ClientAuthenticationMethod.CLIENT_SECRET_BASIC; 28 | Object credentials = null; 29 | 30 | OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode() 31 | .clientId(clientId) 32 | .authorizationUri(request.getRequestURL().toString()) 33 | .additionalParameters(allParameters) 34 | .build(); 35 | 36 | allParameters.put(OAuth2AuthorizationRequest.class.getName(), authorizationRequest); 37 | 38 | return new OAuth2ClientAuthenticationToken( 39 | clientId, 40 | clientAuthenticationMethod, 41 | credentials, 42 | allParameters 43 | ); 44 | } 45 | 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/core/EasyPlusUserInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.core; 2 | 3 | import org.springframework.security.core.GrantedAuthority; 4 | import org.springframework.security.core.userdetails.User; 5 | import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; 6 | 7 | import java.util.Collection; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.stream.Collectors; 12 | 13 | /** 14 | * The {@code EasyPlusUserInfo} class extends {@link User} and implements {@link OAuth2AuthenticatedPrincipal}. 15 | * This class is designed to serve as both a {@link org.springframework.security.core.userdetails.UserDetails} 16 | * and an {@link OAuth2AuthenticatedPrincipal}. 17 | * 18 | * @param the type of customizable user information to be associated with the authenticated user. 19 | * 20 | * Although this is located in the core folder, its usage is optional and will not impact functionality if omitted. 21 | */ 22 | public class EasyPlusUserInfo extends User implements OAuth2AuthenticatedPrincipal { 23 | 24 | private T customizedUserInfo; 25 | public T getCustomizedUserInfo() { 26 | return customizedUserInfo; 27 | } 28 | public void setCustomizedUserInfo(T customizedUserInfo) { 29 | this.customizedUserInfo = customizedUserInfo; 30 | } 31 | 32 | public EasyPlusUserInfo(String username, String password, Collection authorities) { 33 | super(username, password, authorities); 34 | } 35 | 36 | public EasyPlusUserInfo(String username, String password, boolean enabled, boolean accountNonExpired, 37 | boolean credentialsNonExpired, boolean accountNonLocked, 38 | Collection authorities) { 39 | super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); 40 | } 41 | 42 | @Override 43 | public Map getAttributes() { 44 | Map attributes = new HashMap<>(); 45 | List authorities = this.getAuthorities().stream() 46 | .map(GrantedAuthority::getAuthority) 47 | .collect(Collectors.toList()); 48 | attributes.put("authorities", authorities); 49 | 50 | return attributes; 51 | } 52 | 53 | @Override 54 | public String getName() { 55 | return this.getUsername(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/dao/EasyPlusAuthorizationConsentRepository.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.dao; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity.EasyPlusAuthorizationConsent; 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 EasyPlusAuthorizationConsentRepository extends JpaRepository { 11 | Optional findByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName); 12 | void deleteByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName); 13 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/dao/EasyPlusClientRepository.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.dao; 2 | 3 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity.EasyPlusClient; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface EasyPlusClientRepository extends JpaRepository { 9 | /* 10 | * [1] From "https://github.com/spring-projects/spring-authorization-server/tree/main/docs/src/main/java/sample/jpa" 11 | * */ 12 | Optional findByClientId(String clientId); 13 | 14 | /* 15 | * [2] From "EasyPlus" 16 | * : Spring Security 5 -> 6 17 | * */ 18 | 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/entity/EasyPlusAuthorizationConsent.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity; 2 | 3 | import jakarta.persistence.*; 4 | 5 | import java.io.Serializable; 6 | import java.util.Objects; 7 | 8 | @Entity 9 | @Table(name = "`authorization_consent`") 10 | @IdClass(EasyPlusAuthorizationConsent.AuthorizationConsentId.class) 11 | public class EasyPlusAuthorizationConsent { 12 | @Id 13 | @Column(name = "registered_client_id") 14 | private String registeredClientId; 15 | @Id 16 | @Column(name = "principal_name") 17 | private String principalName; 18 | @Column(name = "authorities", length = 1000) 19 | private String authorities; 20 | 21 | public String getRegisteredClientId() { 22 | return registeredClientId; 23 | } 24 | 25 | public void setRegisteredClientId(String registeredClientId) { 26 | this.registeredClientId = registeredClientId; 27 | } 28 | 29 | public String getPrincipalName() { 30 | return principalName; 31 | } 32 | 33 | public void setPrincipalName(String principalName) { 34 | this.principalName = principalName; 35 | } 36 | 37 | public String getAuthorities() { 38 | return authorities; 39 | } 40 | 41 | public void setAuthorities(String authorities) { 42 | this.authorities = authorities; 43 | } 44 | 45 | public static class AuthorizationConsentId implements Serializable { 46 | private String registeredClientId; 47 | private String principalName; 48 | 49 | public String getRegisteredClientId() { 50 | return registeredClientId; 51 | } 52 | 53 | public void setRegisteredClientId(String registeredClientId) { 54 | this.registeredClientId = registeredClientId; 55 | } 56 | 57 | public String getPrincipalName() { 58 | return principalName; 59 | } 60 | 61 | public void setPrincipalName(String principalName) { 62 | this.principalName = principalName; 63 | } 64 | 65 | @Override 66 | public boolean equals(Object o) { 67 | if (this == o) return true; 68 | if (o == null || getClass() != o.getClass()) return false; 69 | AuthorizationConsentId that = (AuthorizationConsentId) o; 70 | return registeredClientId.equals(that.registeredClientId) && principalName.equals(that.principalName); 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | return Objects.hash(registeredClientId, principalName); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/entity/EasyPlusClient.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.entity; 2 | 3 | import jakarta.persistence.Column; 4 | import jakarta.persistence.Entity; 5 | import jakarta.persistence.Id; 6 | import jakarta.persistence.Table; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | 10 | import java.time.Instant; 11 | 12 | @Entity 13 | @Table(name = "oauth2_registered_client") 14 | @Getter 15 | @Setter 16 | public class EasyPlusClient { 17 | 18 | // UUID.randomUUID().toString() (refer to 'Spring-Authorization-Server') 19 | @Id 20 | @Column(name = "id", length = 100) 21 | private String id; 22 | 23 | @Column(name = "client_id", length = 100, nullable = false) 24 | private String clientId; 25 | 26 | @Column(name = "client_id_issued_at", nullable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") 27 | private Instant clientIdIssuedAt; 28 | 29 | @Column(name = "client_secret", length = 200) 30 | private String clientSecret; 31 | 32 | @Column(name = "client_secret_expires_at") 33 | private Instant clientSecretExpiresAt; 34 | 35 | @Column(name = "client_name", length = 200, nullable = false) 36 | private String clientName; 37 | 38 | @Column(name = "client_authentication_methods", length = 1000, nullable = false) 39 | private String clientAuthenticationMethods; 40 | 41 | @Column(name = "authorization_grant_types", length = 1000, nullable = false) 42 | private String authorizationGrantTypes; 43 | 44 | @Column(name = "redirect_uris", length = 1000) 45 | private String redirectUris; 46 | 47 | @Column(name = "post_logout_redirect_uris", length = 1000) 48 | private String postLogoutRedirectUris; 49 | 50 | @Column(name = "scopes", length = 1000, nullable = false) 51 | private String scopes; 52 | 53 | @Column(name = "client_settings", length = 2000, nullable = false) 54 | private String clientSettings; 55 | 56 | @Column(name = "token_settings", length = 2000, nullable = false) 57 | private String tokenSettings; 58 | 59 | @Override 60 | public String toString() { 61 | return "EasyPlusClient{" + 62 | "id='" + id + '\'' + 63 | ", clientId='" + clientId + '\'' + 64 | ", clientIdIssuedAt=" + clientIdIssuedAt + 65 | ", clientSecret='" + clientSecret + '\'' + 66 | ", clientSecretExpiresAt=" + clientSecretExpiresAt + 67 | ", clientName='" + clientName + '\'' + 68 | ", clientAuthenticationMethods='" + clientAuthenticationMethods + '\'' + 69 | ", authorizationGrantTypes='" + authorizationGrantTypes + '\'' + 70 | ", redirectUris='" + redirectUris + '\'' + 71 | ", postLogoutRedirectUris='" + postLogoutRedirectUris + '\'' + 72 | ", scopes='" + scopes + '\'' + 73 | ", clientSettings='" + clientSettings + '\'' + 74 | ", tokenSettings='" + tokenSettings + '\'' + 75 | '}'; 76 | } 77 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/enums/MobileOSType.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.enums; 2 | 3 | public enum MobileOSType { 4 | WINDOWS_PHONE(1), 5 | ANDROID(2), 6 | IOS(3), 7 | UNKNOWN(4); 8 | 9 | private final int value; 10 | 11 | MobileOSType(int value) { 12 | this.value = value; 13 | } 14 | 15 | public int getValue() { 16 | return value; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/message/DefaultSecurityMessageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message; 2 | 3 | public class DefaultSecurityMessageServiceImpl implements ISecurityUserExceptionMessageService { 4 | 5 | @Override 6 | public String getUserMessage(DefaultSecurityUserExceptionMessage defaultSecurityUserExceptionMessage) { 7 | return defaultSecurityUserExceptionMessage.getMessage(); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/message/DefaultSecurityUserExceptionMessage.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message; 2 | 3 | 4 | public enum DefaultSecurityUserExceptionMessage implements ExceptionMessageInterface { 5 | 6 | AUTHENTICATION_LOGIN_FAILURE("Authentication information is not valid. Please check and try again."), 7 | AUTHENTICATION_LOGIN_ERROR("An error occurred during authentication. If the problem persists, please contact customer service."), 8 | AUTHENTICATION_TOKEN_FAILURE("The authentication token has expired. Please log in again."), 9 | AUTHENTICATION_TOKEN_ERROR("There was a problem verifying the authentication token. Please log in again."), 10 | AUTHORIZATION_FAILURE("You do not have access permissions. Please request this from the administrator."), 11 | AUTHORIZATION_ERROR("An error occurred with access permissions. If the problem persists, please contact customer service."), 12 | 13 | // ID PASSWORD 14 | AUTHENTICATION_ID_NO_EXISTS("The specified ID does not exist."), 15 | AUTHENTICATION_WRONG_ID_PASSWORD("User information could not be verified. Please check your ID or password. If the problem persists, please contact customer service."), 16 | AUTHENTICATION_PASSWORD_FAILED_EXCEEDED("The number of password attempts has been exceeded."), 17 | 18 | // Wrong Authorization Code 19 | AUTHENTICATION_INVALID_RESPONSE_TYPE("The specified Response Type is invalid."), 20 | AUTHENTICATION_INVALID_AUTHORIZATION_CODE("The specified Authorization Code is invalid."), 21 | AUTHENTICATION_EXPIRED_AUTHORIZATION_CODE("The specified Authorization Code has been expired."), 22 | AUTHENTICATION_INVALID_REDIRECT_URI("The specified Redirect URI is invalid."), 23 | AUTHENTICATION_SCOPES_NOT_APPROVED("The specified Scopes are not approved."), 24 | // CLIENT ID, SECRET 25 | AUTHENTICATION_WRONG_CLIENT_ID_SECRET("Client information is not verified."), 26 | 27 | // GRANT TYPE 28 | AUTHENTICATION_WRONG_GRANT_TYPE("Wrong Grant Type detected."), 29 | AUTHENTICATION_WRONG_COMBINATION_OF_GRANT_TYPE_RESPONSE_TYPE("Grant Type doesn't match response type."), 30 | 31 | // OAuth2 : Authorization Code 32 | AUTHENTICATION_AUTHORIZATION_CODE_REQUEST_WRONG_METHOD("Wrong Authorization Code request."), 33 | AUTHENTICATION_CLIENT_ID_MISSING("Client ID is missing."), 34 | AUTHENTICATION_REDIRECT_URI_MISSING("Redirect URI is missing."), 35 | AUTHENTICATION_STATE_MISSING("State is missing."), 36 | AUTHENTICATION_REGISTERED_CLIENT_NOT_FOUND("Registered client is missing or invalid"), 37 | AUTHENTICATION_AUTHORIZATION_CODE_MISSING("Authorization Code is missing."); 38 | 39 | private String message; 40 | 41 | @Override 42 | public String getMessage() { 43 | return message; 44 | } 45 | 46 | DefaultSecurityUserExceptionMessage(String message) { 47 | this.message = message; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/message/ExceptionMessageInterface.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message; 2 | 3 | public interface ExceptionMessageInterface { 4 | String getMessage(); 5 | } 6 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/message/ISecurityUserExceptionMessageService.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message; 2 | 3 | public interface ISecurityUserExceptionMessageService { 4 | String getUserMessage(DefaultSecurityUserExceptionMessage defaultSecurityUserExceptionMessage); 5 | } 6 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/provider/auth/endpoint/authorization/CodeAuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.provider.auth.endpoint.authorization; 2 | 3 | import org.springframework.security.authentication.AuthenticationProvider; 4 | import org.springframework.security.core.Authentication; 5 | import org.springframework.security.core.AuthenticationException; 6 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken; 7 | 8 | 9 | public final class CodeAuthenticationProvider implements AuthenticationProvider { 10 | @Override 11 | public Authentication authenticate(Authentication authentication) throws AuthenticationException { 12 | authentication.setAuthenticated(true); 13 | return authentication; 14 | } 15 | @Override 16 | public boolean supports(Class authentication) { 17 | return OAuth2AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/provider/auth/endpoint/authorization/CodeRequestAuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.provider.auth.endpoint.authorization; 2 | 3 | import org.springframework.security.authentication.AuthenticationProvider; 4 | import org.springframework.security.core.Authentication; 5 | import org.springframework.security.core.AuthenticationException; 6 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken; 7 | 8 | public final class CodeRequestAuthenticationProvider implements AuthenticationProvider { 9 | 10 | @Override 11 | public Authentication authenticate(Authentication authentication) throws AuthenticationException { 12 | authentication.setAuthenticated(true); 13 | return authentication; 14 | } 15 | 16 | @Override 17 | public boolean supports(Class authentication) { 18 | return OAuth2AuthorizationCodeRequestAuthenticationToken.class.isAssignableFrom(authentication); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/response/auth/authentication/DefaultWebAuthenticationSuccessHandlerImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.auth.authentication; 2 | 3 | 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; 5 | import jakarta.servlet.ServletException; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import jakarta.servlet.http.HttpServletResponse; 8 | import lombok.RequiredArgsConstructor; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.security.core.Authentication; 12 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken; 13 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken; 14 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 15 | 16 | import java.io.IOException; 17 | 18 | @RequiredArgsConstructor 19 | public class DefaultWebAuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler { 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(DefaultWebAuthenticationSuccessHandlerImpl.class); 22 | 23 | private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService; 24 | 25 | @Override 26 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { 27 | if(authentication instanceof OAuth2AuthorizationCodeRequestAuthenticationToken) { 28 | request.getRequestDispatcher("/login").forward(request, response); 29 | }else if(authentication instanceof OAuth2AuthorizationCodeAuthenticationToken) { 30 | OAuth2AuthorizationCodeAuthenticationToken oAuth2AuthorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) authentication; 31 | 32 | String redirectUri = oAuth2AuthorizationCodeAuthenticationToken.getRedirectUri(); 33 | String authorizationCode = oAuth2AuthorizationCodeAuthenticationToken.getCode(); 34 | String state = oAuth2AuthorizationCodeAuthenticationToken.getAdditionalParameters().get("state").toString(); 35 | 36 | response.sendRedirect(redirectUri+"?code="+authorizationCode+"&state="+state); 37 | }else{ 38 | logger.error("Wrong Authentication Type : {}", authentication.getClass()); 39 | request.getRequestDispatcher("/login").forward(request, response); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/response/error/dto/EasyPlusErrorMessages.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto; 2 | 3 | import lombok.*; 4 | import org.springframework.security.core.userdetails.UserDetails; 5 | 6 | import java.util.Map; 7 | 8 | @Getter 9 | @Setter 10 | @ToString 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class EasyPlusErrorMessages { 15 | 16 | private String message; 17 | private String userMessage; 18 | private Map userValidationMessage; 19 | private UserDetails userDetails; 20 | private String errorCode; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/response/error/dto/ISecurityErrorMessages.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto; 2 | 3 | import java.util.Map; 4 | 5 | public interface ISecurityErrorMessages { 6 | // Logged but NOT sent to clients 7 | String getMessage(); 8 | // Logged and sent to clients 9 | String getUserMessage(); 10 | // Logged and sent to clients in the format of an array of "field":"message" pairs. 11 | Map getUserValidationMessage(); 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/response/error/dto/SecurityEasyPlusErrorResponsePayload.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.util.TimestampUtil; 6 | import lombok.ToString; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import java.util.Date; 10 | import java.util.Map; 11 | 12 | @ToString 13 | public class SecurityEasyPlusErrorResponsePayload { 14 | private Date timestamp; 15 | 16 | // Never to be returned to clients, but must be logged. 17 | //@JsonIgnore 18 | private String message; 19 | private String details; 20 | private String userMessage; 21 | private Map userValidationMessage; 22 | 23 | @JsonIgnore 24 | private String stackTrace; 25 | @JsonIgnore 26 | private String cause; 27 | 28 | 29 | public SecurityEasyPlusErrorResponsePayload(EasyPlusErrorMessages easyPlusErrorMessages, Exception e, String details, String stackTrace, String userMessage, Map userValidationMessage) { 30 | this.timestamp = TimestampUtil.getPayloadTimestamp(); 31 | this.message = !StringUtils.isEmpty(easyPlusErrorMessages.getMessage()) ? easyPlusErrorMessages.getMessage() : e.getMessage() ; 32 | this.details = details; 33 | this.userMessage = !StringUtils.isEmpty(easyPlusErrorMessages.getUserMessage()) ? easyPlusErrorMessages.getUserMessage() : userMessage; 34 | this.stackTrace = stackTrace; 35 | this.userValidationMessage = easyPlusErrorMessages.getUserValidationMessage() != null && !easyPlusErrorMessages.getUserValidationMessage().isEmpty() ? easyPlusErrorMessages.getUserValidationMessage() : userValidationMessage; 36 | } 37 | 38 | public SecurityEasyPlusErrorResponsePayload(String message, String details, String userMessage, String stackTrace) { 39 | this.timestamp = TimestampUtil.getPayloadTimestamp(); 40 | this.message = message; 41 | this.details = details; 42 | this.userMessage = userMessage; 43 | this.stackTrace = stackTrace; 44 | } 45 | 46 | public SecurityEasyPlusErrorResponsePayload(String message, String details, String userMessage, String stackTrace, String cause) { 47 | this.timestamp = TimestampUtil.getPayloadTimestamp(); 48 | this.message = message; 49 | this.details = details; 50 | this.userMessage = userMessage; 51 | this.stackTrace = stackTrace; 52 | this.cause = cause; 53 | } 54 | 55 | public SecurityEasyPlusErrorResponsePayload(String message, String details, String userMessage, Map userValidationMessage, 56 | String stackTrace, String cause) { 57 | 58 | this.timestamp = TimestampUtil.getPayloadTimestamp(); 59 | this.message = message; 60 | this.details = details; 61 | this.userMessage = userMessage; 62 | this.userValidationMessage = userValidationMessage; 63 | this.stackTrace = stackTrace; 64 | this.cause = cause; 65 | } 66 | 67 | public Date getTimestamp() { 68 | return timestamp; 69 | } 70 | 71 | public String getMessage() { 72 | return message; 73 | } 74 | 75 | public String getDetails() { 76 | return details; 77 | } 78 | 79 | public String getUserMessage() { 80 | return userMessage; 81 | } 82 | 83 | public String getStackTrace() { 84 | return stackTrace; 85 | } 86 | 87 | public String getCause() { 88 | return cause; 89 | } 90 | 91 | public Map getUserValidationMessage() { 92 | return userValidationMessage; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/response/error/exception/EasyPlusOauth2AuthenticationException.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception; 2 | 3 | 4 | 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.EasyPlusErrorMessages; 6 | import org.springframework.security.oauth2.core.OAuth2AuthenticationException; 7 | import org.springframework.security.oauth2.core.OAuth2Error; 8 | 9 | /* 10 | * Only OAuth2AuthenticationException is allowed to be tossed according to "spring-authorization-server". 11 | * */ 12 | public class EasyPlusOauth2AuthenticationException extends OAuth2AuthenticationException { 13 | protected EasyPlusErrorMessages easyPlusErrorMessages; 14 | 15 | public EasyPlusOauth2AuthenticationException(){ 16 | super("default"); 17 | } 18 | public EasyPlusOauth2AuthenticationException(String message){ 19 | super(message); 20 | easyPlusErrorMessages = EasyPlusErrorMessages.builder().userMessage(message).message(message).build(); 21 | } 22 | 23 | public EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages easyPlusErrorMessages){ 24 | super(new OAuth2Error(easyPlusErrorMessages.getErrorCode() == null ? "default" : easyPlusErrorMessages.getErrorCode()), easyPlusErrorMessages.getMessage() == null ? "default" : easyPlusErrorMessages.getMessage()); 25 | this.easyPlusErrorMessages = easyPlusErrorMessages; 26 | } 27 | 28 | public EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages easyPlusErrorMessages, Throwable cause) { 29 | super(new OAuth2Error(easyPlusErrorMessages.getErrorCode() == null ? "default" : easyPlusErrorMessages.getErrorCode()), 30 | easyPlusErrorMessages.getMessage() == null ? "default" : easyPlusErrorMessages.getMessage(), cause); 31 | this.easyPlusErrorMessages = easyPlusErrorMessages; 32 | } 33 | 34 | public EasyPlusErrorMessages getErrorMessages() { 35 | return easyPlusErrorMessages; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/response/error/exception/EasyPlusOauth2AuthorizationException.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception; 2 | 3 | 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.security.access.AccessDeniedException; 6 | import org.springframework.web.bind.annotation.ResponseStatus; 7 | 8 | @ResponseStatus(value = HttpStatus.FORBIDDEN) 9 | public class EasyPlusOauth2AuthorizationException extends AccessDeniedException { 10 | public EasyPlusOauth2AuthorizationException(String message) { 11 | super(message); 12 | } 13 | public EasyPlusOauth2AuthorizationException() { 14 | super(null); 15 | } 16 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/response/error/util/ExceptionEasyPlusUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.util; 2 | 3 | import org.apache.commons.lang3.exception.ExceptionUtils; 4 | 5 | public class ExceptionEasyPlusUtils { 6 | 7 | public static String getAllCausesWithStartMessage(Throwable e, String causes) { 8 | if (e.getCause() == null) return causes; 9 | causes += e.getCause() + " / "; 10 | return getAllCausesWithStartMessage(e.getCause(), causes); 11 | } 12 | 13 | public static String getAllCauses(Throwable e) { 14 | String causes = ""; 15 | return getAllCausesWithStartMessage(e, causes); 16 | } 17 | 18 | public static String getAllStackTraces(Throwable e) { 19 | return ExceptionUtils.getStackTrace(e); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/response/resource/authentication/DefaultAuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.resource.authentication; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.security.core.AuthenticationException; 7 | import org.springframework.security.web.AuthenticationEntryPoint; 8 | import org.springframework.web.servlet.HandlerExceptionResolver; 9 | 10 | import java.io.IOException; 11 | 12 | @RequiredArgsConstructor 13 | public class DefaultAuthenticationEntryPoint implements AuthenticationEntryPoint { 14 | 15 | private final HandlerExceptionResolver resolver; 16 | 17 | @Override 18 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException { 19 | resolver.resolveException(request, response, null, ex); 20 | } 21 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/serivce/CommonOAuth2AuthorizationSaver.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce; 2 | 3 | import jakarta.validation.constraints.NotNull; 4 | import org.springframework.security.core.userdetails.UserDetails; 5 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 6 | import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; 7 | 8 | import java.util.Map; 9 | 10 | /* 11 | * Create = Build + Persist 12 | * */ 13 | public interface CommonOAuth2AuthorizationSaver { 14 | 15 | @NotNull OAuth2Authorization save(UserDetails userDetails, AuthorizationGrantType authorizationGrantType, 16 | String clientId, Map additionalParameters); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/serivce/DefaultOauth2AuthenticationHashCheckService.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce; 2 | 3 | 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.EasyPlusErrorMessages; 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthenticationException; 6 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage; 7 | 8 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; 9 | import jakarta.annotation.Nullable; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.security.core.userdetails.UserDetails; 12 | import org.springframework.security.crypto.password.PasswordEncoder; 13 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 14 | import org.springframework.stereotype.Service; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class DefaultOauth2AuthenticationHashCheckService implements IOauth2AuthenticationHashCheckService { 19 | 20 | private final PasswordEncoder passwordEncoder; 21 | private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService; 22 | 23 | public void validateUsernamePassword(String inputPassword, @Nullable UserDetails userDetails){ 24 | if (userDetails == null) { 25 | throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_ID_NO_EXISTS)); 26 | } 27 | if (!passwordEncoder.matches(inputPassword, userDetails.getPassword())) { 28 | throw new EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages.builder() 29 | .userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_FAILURE)).message(DefaultSecurityUserExceptionMessage.AUTHENTICATION_WRONG_ID_PASSWORD.getMessage() + " (inputPassword : " + inputPassword + ", input username : " + userDetails.getUsername() + ")").build()); 30 | } 31 | } 32 | 33 | public void validateClientCredentials(String inputClientSecret, RegisteredClient registeredClient){ 34 | if (registeredClient == null) { 35 | throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_WRONG_CLIENT_ID_SECRET)); 36 | } 37 | if (!passwordEncoder.matches(inputClientSecret, registeredClient.getClientSecret())) { 38 | throw new EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages.builder() 39 | .userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_FAILURE)).message(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_WRONG_CLIENT_ID_SECRET) + " (inputClientSecret : " + inputClientSecret+ ")").build()); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/serivce/IOauth2AuthenticationHashCheckService.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce; 2 | 3 | import jakarta.annotation.Nullable; 4 | import org.springframework.security.core.userdetails.UserDetails; 5 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 6 | 7 | public interface IOauth2AuthenticationHashCheckService { 8 | void validateUsernamePassword(String inputPassword, @Nullable UserDetails userDetails); 9 | void validateClientCredentials(String inputClientSecret, RegisteredClient registeredClient); 10 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/serivce/authentication/OAuth2AuthorizationBuildingService.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.authentication; 2 | 3 | import org.springframework.security.core.userdetails.UserDetails; 4 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 5 | import org.springframework.security.oauth2.core.OAuth2RefreshToken; 6 | import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; 7 | 8 | import java.util.Map; 9 | 10 | /* 11 | * Create = Build + Persist 12 | * */ 13 | public interface OAuth2AuthorizationBuildingService { 14 | 15 | OAuth2Authorization build(UserDetails userDetails, AuthorizationGrantType grantType, String clientId, 16 | Map additionalParameters, OAuth2RefreshToken shouldBePreservedRefreshToken); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/serivce/userdetail/ConditionalDetailsService.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.userdetail; 2 | 3 | 4 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthenticationException; 5 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage; 6 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.EasyPlusErrorMessages; 7 | import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; 8 | import lombok.AllArgsConstructor; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.core.userdetails.UserDetailsService; 11 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 12 | import org.springframework.stereotype.Service; 13 | 14 | @Service 15 | @AllArgsConstructor 16 | public class ConditionalDetailsService { 17 | 18 | private final UserDetailsServiceFactory userDetailsServiceFactory; 19 | private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService; 20 | 21 | public UserDetails loadUserByUsername(String username, String clientId) throws UsernameNotFoundException, EasyPlusOauth2AuthenticationException { 22 | 23 | UserDetailsService userDetailsService = userDetailsServiceFactory.getUserDetailsService(clientId); 24 | if (userDetailsService != null) { 25 | return userDetailsService.loadUserByUsername(username); 26 | } 27 | throw new EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages.builder() 28 | .message("Unable to distinguish whether the user is an Admin or a Customer. (username : " + username + " / client_id: " + clientId + ")") 29 | .userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_ERROR)) 30 | .build()); 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/serivce/userdetail/UserDetailsServiceFactory.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.userdetail; 2 | 3 | import org.springframework.security.core.userdetails.UserDetailsService; 4 | 5 | public interface UserDetailsServiceFactory { 6 | UserDetailsService getUserDetailsService(String clientId); 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/server/EasyPlusJwtConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.server; 2 | 3 | import com.nimbusds.jose.JWSAlgorithm; 4 | import com.nimbusds.jose.JWSHeader; 5 | import com.nimbusds.jose.crypto.MACSigner; 6 | import com.nimbusds.jwt.JWTClaimsSet; 7 | import com.nimbusds.jwt.SignedJWT; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.security.oauth2.jwt.Jwt; 14 | import org.springframework.security.oauth2.jwt.JwtDecoder; 15 | import org.springframework.security.oauth2.jwt.JwtEncoder; 16 | import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; 17 | 18 | import javax.crypto.spec.SecretKeySpec; 19 | import java.time.Instant; 20 | import java.util.Base64; 21 | import java.util.Date; 22 | 23 | @Configuration 24 | public class EasyPlusJwtConfig { 25 | 26 | private static final Logger logger = LoggerFactory.getLogger(EasyPlusJwtConfig.class); 27 | 28 | @Value("${patternhelloworld.securityhelper.jwt.secret:5pAq6zRyX8bC3dV2wS7gN1mK9jF0hL4tUoP6iBvE3nG8xZaQrY7cW2fA}") 29 | private String jwtSecret; 30 | 31 | @Value("${patternhelloworld.securityhelper.jwt.algorithm:HmacSHA256}") 32 | private String algorithm; 33 | 34 | @Bean 35 | public JwtDecoder jwtDecoder() { 36 | byte[] keyBytes = Base64.getDecoder().decode(jwtSecret); 37 | SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, algorithm); 38 | return NimbusJwtDecoder.withSecretKey(secretKeySpec).build(); 39 | } 40 | 41 | @Bean 42 | public JwtEncoder jwtEncoder() { 43 | return parameters -> { 44 | byte[] secretKeyBytes = Base64.getDecoder().decode(jwtSecret); 45 | SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, algorithm); 46 | 47 | try { 48 | MACSigner signer = new MACSigner(secretKeySpec); 49 | 50 | JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder(); 51 | parameters.getClaims().getClaims().forEach((key, value) -> 52 | claimsSetBuilder.claim(key, value instanceof Instant ? Date.from((Instant) value) : value) 53 | ); 54 | JWTClaimsSet claimsSet = claimsSetBuilder.build(); 55 | 56 | JWSHeader header = new JWSHeader(JWSAlgorithm.HS256); 57 | 58 | SignedJWT signedJWT = new SignedJWT(header, claimsSet); 59 | signedJWT.sign(signer); 60 | 61 | return Jwt.withTokenValue(signedJWT.serialize()) 62 | .header("alg", header.getAlgorithm().getName()) 63 | .subject(claimsSet.getSubject()) 64 | .issuer(claimsSet.getIssuer()) 65 | .claims(claims -> claims.putAll(claimsSet.getClaims())) 66 | .issuedAt(claimsSet.getIssueTime().toInstant()) 67 | .expiresAt(claimsSet.getExpirationTime().toInstant()) 68 | .build(); 69 | } catch (Exception e) { 70 | throw new IllegalStateException("Error while signing the JWT", e); 71 | } 72 | }; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/token/EasyPlusGrantAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.token; 2 | 3 | import org.springframework.security.authentication.AbstractAuthenticationToken; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 7 | 8 | import java.util.Collection; 9 | import java.util.Collections; 10 | import java.util.Map; 11 | 12 | /* 13 | * EasyPlusGrantAuthenticationToken instead of OAuth2ClientAuthenticationToken 14 | * */ 15 | public class EasyPlusGrantAuthenticationToken extends AbstractAuthenticationToken { 16 | 17 | private final Object principal; 18 | private final AuthorizationGrantType grantType; 19 | private final Map additionalParameters; 20 | 21 | // Constructor 22 | public EasyPlusGrantAuthenticationToken(AuthorizationGrantType grantType, Object principal, Map additionalParameters) { 23 | super(Collections.emptyList()); 24 | this.grantType = grantType; 25 | this.principal = principal; 26 | this.additionalParameters = additionalParameters; 27 | setAuthenticated(false); // This should always be false initially 28 | } 29 | 30 | @Override 31 | public Collection getAuthorities() { 32 | if(principal instanceof UserDetails){ 33 | return (Collection) ((UserDetails) principal).getAuthorities(); 34 | }else{ 35 | Object authoritiesObj = this.getAdditionalParameters().get("authorities"); 36 | if (authoritiesObj instanceof Collection) { 37 | Collection rawCollection = (Collection) authoritiesObj; 38 | for (Object obj : rawCollection) { 39 | if (!(obj instanceof GrantedAuthority)) { 40 | // throw new ClassCastException("Element is not a GrantedAuthority"); 41 | return Collections.emptyList(); 42 | } 43 | } 44 | return (Collection) rawCollection; 45 | } else { 46 | return Collections.emptyList(); 47 | } 48 | } 49 | 50 | } 51 | 52 | @Override 53 | public Object getCredentials() { 54 | return null; // Typically, credentials are not needed/used after authentication 55 | } 56 | 57 | @Override 58 | public Object getDetails() { 59 | return this.additionalParameters; // Details about the authentication request 60 | } 61 | 62 | @Override 63 | public Object getPrincipal() { 64 | return this.principal; // The authenticated user or client 65 | } 66 | 67 | @Override 68 | public boolean isAuthenticated() { 69 | return super.isAuthenticated(); // The authentication state 70 | } 71 | 72 | @Override 73 | public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { 74 | super.setAuthenticated(isAuthenticated); // Set the authentication state 75 | } 76 | 77 | public AuthorizationGrantType getGrantType() { 78 | return grantType; 79 | } 80 | 81 | public Map getAdditionalParameters() { 82 | return additionalParameters; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/token/generator/CustomAccessTokenCustomizer.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.token.generator; 2 | 3 | import org.springframework.security.core.userdetails.UserDetails; 4 | import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; 5 | import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext; 6 | import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsSet; 7 | import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class CustomAccessTokenCustomizer implements OAuth2TokenCustomizer { 13 | 14 | private final String clientId; 15 | private final UserDetails userDetails; 16 | 17 | public CustomAccessTokenCustomizer(String clientId, UserDetails userDetails) { 18 | this.clientId = clientId; 19 | this.userDetails = userDetails; 20 | } 21 | 22 | @Override 23 | public void customize(JwtEncodingContext context) { 24 | if (context == null) { 25 | throw new IllegalArgumentException("JwtEncodingContext cannot be null"); 26 | } 27 | context.getClaims().claim("client_id", clientId); 28 | context.getClaims().claim("username", this.userDetails.getUsername()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/token/generator/CustomAuthenticationKeyGenerator.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.token.generator; 2 | 3 | import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.math.BigInteger; 7 | import java.security.MessageDigest; 8 | import java.security.NoSuchAlgorithmException; 9 | import java.util.LinkedHashMap; 10 | import java.util.Map; 11 | 12 | public class CustomAuthenticationKeyGenerator { 13 | 14 | /* 15 | * KEY : username + client_id + app_token 16 | * */ 17 | public static String hashUniqueCompositeColumnsToAuthenticationId(OAuth2Authorization authorization, String appToken) { 18 | Map values = new LinkedHashMap(); 19 | 20 | if (authorization.getRegisteredClientId() != null) { 21 | values.put("username", authorization.getPrincipalName()); 22 | } 23 | 24 | values.put("client_id", authorization.getRegisteredClientId()); 25 | /* if (authorizationRequest.getScope() != null) { 26 | values.put("scope", OAuth2Utils.formatParameterList(new TreeSet(authorizationRequest.getScope()))); 27 | }*/ 28 | values.put("app_token", appToken); 29 | 30 | return generateKey(values); 31 | } 32 | 33 | protected static String generateKey(Map values) { 34 | try { 35 | MessageDigest digest = MessageDigest.getInstance("MD5"); 36 | byte[] bytes = digest.digest(values.toString().getBytes("UTF-8")); 37 | return String.format("%032x", new BigInteger(1, bytes)); 38 | } catch (NoSuchAlgorithmException var4) { 39 | throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK).", var4); 40 | } catch (UnsupportedEncodingException var5) { 41 | throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK).", var5); 42 | } 43 | } 44 | 45 | public static String hashTokenValue(String value) { 46 | if(value == null) { 47 | return null; 48 | } else { 49 | MessageDigest digest; 50 | try { 51 | digest = MessageDigest.getInstance("MD5"); 52 | } catch (NoSuchAlgorithmException var5) { 53 | throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK)."); 54 | } 55 | 56 | try { 57 | byte[] e = digest.digest(value.getBytes("UTF-8")); 58 | return String.format("%032x", new Object[]{new BigInteger(1, e)}); 59 | } catch (UnsupportedEncodingException var4) { 60 | throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK)."); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/validator/endpoint/authorization/CodeValidationResult.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.validator.endpoint.authorization; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 6 | 7 | import java.util.Set; 8 | 9 | @Data 10 | @Builder 11 | public class CodeValidationResult { 12 | private String clientId; 13 | private String responseType; 14 | private String redirectUri; 15 | private String state; 16 | private Set scope; 17 | private RegisteredClient registeredClient; 18 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/validator/endpoint/token/CodeValidationResult.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.validator.endpoint.token; 2 | 3 | import jakarta.annotation.Nullable; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 7 | 8 | @Data 9 | @Builder 10 | public class CodeValidationResult { 11 | private String clientId; 12 | private String grantType; 13 | @Nullable 14 | private String code; // grantType : authorization_code 15 | @Nullable 16 | private String username; // grantType : password 17 | @Nullable 18 | private String password; // grantType : password 19 | @Nullable 20 | private String refreshToken; // grantType : refresh_token 21 | private RegisteredClient registeredClient; 22 | @Nullable 23 | private String responseType; 24 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/util/EasyPlusErrorCodeConstants.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.util; 2 | 3 | 4 | /* 5 | * Consider the following method in "spring-authorization-server" 6 | * 7 | * static String normalizeUserCode(String userCode) { 8 | Assert.hasText(userCode, "userCode cannot be empty"); 9 | StringBuilder sb = new StringBuilder(userCode.toUpperCase().replaceAll("[^A-Z\\d]+", "")); 10 | Assert.isTrue(sb.length() == 8, "userCode must be exactly 8 alpha/numeric characters"); 11 | sb.insert(4, '-'); 12 | return sb.toString(); 13 | } 14 | * */ 15 | public class EasyPlusErrorCodeConstants { 16 | public static final String REDIRECT_TO_LOGIN = "REDIRECTTOLOGIN"; 17 | public static final String REDIRECT_TO_CONSENT = "REDIRECTTOCONSENT"; 18 | public static final String MISSING_CLIENT_ID = "MISSINGCLIENTID"; 19 | public static final String MISSING_REDIRECT_URI = "MISSINGREDIRECTURI"; 20 | public static final String MISSING_STATE = "MISSINGSTATE"; 21 | public static final String MISSING_RESPONSE_TYPE = "MISSINGRESPONSETYPE"; 22 | public static final String WRONG_RESPONSE_TYPE = "WRONGRESPONSETYPE"; 23 | public static final String SCOPE_MISMATCH = "SCOPEMISMATCH"; 24 | 25 | public static final String MISSING_AUTHORIZATION_CODE = "MISSINGAUTHORIZATIONCODE"; 26 | public static final String MISSING_USERNAME = "MISSINGUSERNAME"; 27 | public static final String MISSING_PASSWORD = "MISSINGPASSWORD"; 28 | public static final String MISSING_REFRESH_TOKEN = "MISSINGREFRESHTOKEN"; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/util/EasyPlusHttpHeaders.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.util; 2 | 3 | import org.springframework.http.HttpHeaders; 4 | 5 | public class EasyPlusHttpHeaders extends HttpHeaders { 6 | 7 | public static final String APP_TOKEN = "App-Token"; 8 | public static final String X_Forwarded_For = "X-Forwarded-For"; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/util/EasyPlusOrderConstants.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.util; 2 | 3 | public class EasyPlusOrderConstants { 4 | public static final int SECURITY_EASY_PLUS_EXCEPTION_HANDLER_ORDER = -9977; 5 | public static final int SECURITY_EASY_PLUS_SERVER_CONFIG_ORDER = -9977; 6 | } 7 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/util/SecurityExceptionUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.springframework.dao.DataIntegrityViolationException; 5 | 6 | import java.util.function.Supplier; 7 | 8 | public class SecurityExceptionUtils { 9 | 10 | public static T retryOnDuplicateException(Supplier action, int maxRetries, Logger logger, String errorMessage) { 11 | int attempt = 0; 12 | while (attempt < maxRetries) { 13 | try { 14 | return action.get(); 15 | } catch (DataIntegrityViolationException e) { 16 | logger.error(String.format("%s... Retrying up to %d times.... (Count: %d) - %s", errorMessage, maxRetries, attempt, e.getMessage())); 17 | attempt++; 18 | if (attempt == maxRetries) { 19 | throw e; 20 | } 21 | } 22 | } 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/util/SerializableObjectConverter.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.util; 2 | 3 | 4 | import org.apache.tomcat.util.codec.binary.Base64; 5 | import org.springframework.security.oauth2.core.OAuth2AccessToken; 6 | import org.springframework.security.oauth2.core.OAuth2RefreshToken; 7 | import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; 8 | import org.springframework.util.SerializationUtils; 9 | 10 | import java.util.Arrays; 11 | 12 | 13 | public class SerializableObjectConverter { 14 | 15 | public static String serializeAuthentication(OAuth2Authorization object) { 16 | try { 17 | byte[] bytes = SerializationUtils.serialize(object); 18 | return Base64.encodeBase64String(bytes); 19 | } catch (Exception e) { 20 | e.printStackTrace(); 21 | throw e; 22 | } 23 | } 24 | 25 | public static String serializeAccessToken(OAuth2AccessToken object) { 26 | try { 27 | byte[] bytes = SerializationUtils.serialize(object); 28 | return Base64.encodeBase64String(bytes); 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | throw e; 32 | } 33 | } 34 | 35 | public static String serializeRefreshToken(OAuth2RefreshToken object) { 36 | try { 37 | byte[] bytes = SerializationUtils.serialize(object); 38 | return Base64.encodeBase64String(bytes); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | throw e; 42 | } 43 | } 44 | 45 | public static OAuth2Authorization deserializeToAuthentication(String encodedObject) { 46 | try { 47 | byte[] bytes = Base64.decodeBase64(encodedObject); 48 | return (OAuth2Authorization) SerializationUtils.deserialize(bytes); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | throw e; 52 | } 53 | } 54 | 55 | public static OAuth2AccessToken deserializeToAccessToken(String encodedObject) { 56 | try { 57 | byte[] bytes = Base64.decodeBase64(Arrays.toString(encodedObject.getBytes())); 58 | return (OAuth2AccessToken) SerializationUtils.deserialize(bytes); 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | throw e; 62 | } 63 | } 64 | 65 | public static OAuth2RefreshToken deserializeToRefreshToken(String encodedObject) { 66 | try { 67 | byte[] bytes = Base64.decodeBase64(encodedObject); 68 | return (OAuth2RefreshToken) SerializationUtils.deserialize(bytes); 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | throw e; 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/util/TimestampUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.config.util; 2 | 3 | import java.util.Date; 4 | 5 | public class TimestampUtil { 6 | public static Date getPayloadTimestamp(){ 7 | return new Date(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/domain/traditionaloauth/bo/BasicTokenResolver.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.domain.traditionaloauth.bo; 2 | 3 | import java.util.Base64; 4 | import java.util.Optional; 5 | 6 | public class BasicTokenResolver { 7 | 8 | public static Optional parse(String authHeader) { 9 | if (authHeader != null && authHeader.startsWith("Basic ")) { 10 | 11 | String base64Credentials = authHeader.substring("Basic ".length()).trim(); 12 | 13 | byte[] credDecoded = Base64.getDecoder().decode(base64Credentials); 14 | String credentials = new String(credDecoded); 15 | 16 | 17 | final String[] values = credentials.split(":", 2); 18 | if (values.length == 2) { 19 | return Optional.of(new BasicCredentials(values[0], values[1])); 20 | } 21 | } 22 | return Optional.empty(); 23 | } 24 | 25 | public static class BasicCredentials { 26 | private final String clientId; 27 | private final String clientSecret; 28 | 29 | public BasicCredentials(String clientId, String clientSecret) { 30 | this.clientId = clientId; 31 | this.clientSecret = clientSecret; 32 | } 33 | 34 | 35 | public String getClientId() { 36 | return clientId; 37 | } 38 | 39 | public String getClientSecret() { 40 | return clientSecret; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/domain/traditionaloauth/dto/SpringSecurityTraditionalOauthDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.patternhelloworld.securityhelper.oauth2.api.domain.traditionaloauth.dto; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | 9 | public class SpringSecurityTraditionalOauthDTO { 10 | 11 | @Getter 12 | @Setter 13 | public static class TokenRequest { 14 | 15 | private String username; 16 | private String password; 17 | 18 | private String refresh_token; 19 | 20 | @NotBlank 21 | private String grant_type; 22 | 23 | private String otp_value; 24 | 25 | } 26 | 27 | @Getter 28 | @Setter 29 | public static class AuthorizationCodeRequest { 30 | 31 | private String username; 32 | private String password; 33 | 34 | } 35 | 36 | 37 | @AllArgsConstructor 38 | @Getter 39 | public static class TokenResponse { 40 | private String access_token; 41 | private String token_type = "Bearer"; 42 | private String refresh_token; 43 | private int expires_in; 44 | private String scope; 45 | } 46 | 47 | 48 | public static class AuthorizationCodeResponse { 49 | private String authorization_code; 50 | 51 | public AuthorizationCodeResponse(String authorizationCode) { 52 | this.authorization_code = authorizationCode; 53 | } 54 | 55 | public String getAuthorizationCode() { 56 | return authorization_code; 57 | } 58 | 59 | public void setAuthorizationCode(String authorizationCode) { 60 | this.authorization_code = authorizationCode; 61 | } 62 | 63 | } 64 | 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /postman/sc-oauth2-pji.postman_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "cf557906-42cc-4a49-bb95-eef4e63dc97c", 3 | "name": "sc-oauth2-pji", 4 | "values": [ 5 | { 6 | "key": "HOST", 7 | "value": "localhost:8370", 8 | "type": "default", 9 | "enabled": true 10 | }, 11 | { 12 | "key": "PROTOCOL", 13 | "value": "http", 14 | "type": "default", 15 | "enabled": true 16 | } 17 | ], 18 | "_postman_variable_scope": "environment", 19 | "_postman_exported_at": "2025-01-04T02:00:27.091Z", 20 | "_postman_exported_using": "Postman/11.23.2-241218-1111" 21 | } -------------------------------------------------------------------------------- /reference/docs/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patternhelloworld/spring-oauth2-easyplus/1626d56b176212712df72e7b1d4ef389b709a504/reference/docs/img.png -------------------------------------------------------------------------------- /reference/docs/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patternhelloworld/spring-oauth2-easyplus/1626d56b176212712df72e7b1d4ef389b709a504/reference/docs/img1.png -------------------------------------------------------------------------------- /reference/docs/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patternhelloworld/spring-oauth2-easyplus/1626d56b176212712df72e7b1d4ef389b709a504/reference/docs/img3.png -------------------------------------------------------------------------------- /reference/docs/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patternhelloworld/spring-oauth2-easyplus/1626d56b176212712df72e7b1d4ef389b709a504/reference/docs/img4.png --------------------------------------------------------------------------------