Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

patternhelloworld/spring-oauth2-easyplus

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

App-Token based easy OAuth2 implementation built to grow with Spring Boot

Table of Contents

Quick Start

<dependency>    <groupId>io.github.patternhelloworld.securityhelper.oauth2.api</groupId>    <artifactId>spring-oauth2-easyplus</artifactId>    <version>4.4.1</version></dependency>

Features

  • Complete separation of the library and the client
    • Library : API
    • Client : DOC, Integration tester
  • Use JPA for various databases to gain full control over all tokens and permissions, unlike simple in-memory examples.
  • Extensible: Supports multiple authorization servers and resource servers with this library.
  • Hybrid Resource Servers Token Verification Methods: Support for multiple verification approaches, including API calls to the authorization server, direct database validation, and local JWT decoding.
  • Immediate Permission (Authority) Check: Not limited to verifying the token itself, but also ensuring real-time validation of any updates to permissions in the database.
  • Authentication management based on a combination of Username, client ID, and App-Token
    • What is an App-Token?
      • An App-Token is an additional token that serves as a unique identifier for each device. Unlike access tokens, it is not regenerated with each login. Instead, it uses a device-specific unique value, such as a GUID in Android, to control device-level authentication, even when the app is reinstalled. If the token values are the same, the same access token is shared.
App-Token StatusAccess Token Behavior
same for the same userAccess-Token is shared
different for the same userAccess-Token is NOT shared
  • Set this in yourapplication.properties.
    • App-Token Behavior Based onio.github.patternhelloworld.securityhelper.oauth2.no-app-token-same-access-token
no-app-token-same-access-token ValueApp-Token StatusAccess Token Sharing Behavior
trueApp-Token isnull for the same userSame user with anull App-Token shares the same access token across multiple logins.
falseApp-Token isnull for the same userEven if the App-Token isnull, the same user will receive a new access token for each login.
-App-Token is shared for the same userAccess tokens will not be shared. A new access token is generated for each unique App-Token, even for the same user.
-App-Token is NOT shared for the same userEach unique App-Token generates a new access token for the same user.
  • Separated UserDetails implementation for Admin and Customer roles as an example. (This can be extended such as Admin, Customer, Seller and Buyer... by implementingUserDetailsServiceFactory)
  • Authorization Code Flow with Optional PKCE, Authorization Consent and Single Page Application (XMLHttpRequest)
  • ROPC for scenarios where accessing a browser screen on the server is either unavailable or impractical
  • Application of Spring Rest Docs, Postman payloads provided
  • Set up the same access & refresh token APIs on both/oauth2/token and on our controller layer such as/api/v1/traditional-oauth/token, both of which function same and havethe same request & response payloads for success and errors. (However,/oauth2/token is the standard that "spring-authorization-server" provides.)
    • As you are aware, the API/oauth2/token(Recommended) is what "spring-authorization-server" provides.
      • /api/v1/traditional-oauth/token(Easily Customizable) is what this library implemented directly.

        • Success Payload
         {"access_token" :"Vd4x8D4lDg7VBFh...","token_type" :"Bearer","refresh_token" :"m3UgLrvPtXKdy7jiD...","expires_in" :3469,"scope" :"read write"  }
        • Error Payload (Customizable)
          {"timestamp":1719470948370,"message":"Couldn't find the client ID : client_admin",// Sensitive info such as being thrown from StackTraces"details":"uri=/oauth2/token","userMessage":"Authentication failed. Please check your credentials.","userValidationMessage":null  }
        • In the following error payload, the 'message' shouldn't be exposed to clients; instead, the 'userMessage' should be.
        • Definitely, you can customize the payload sent to the client by implementing AuthenticationFailureHandler.
  • See the sample foldercom.patternhelloworld.securityhelper.oauth2.client.config.securityimpl to understand how to implement the library.

Dependencies

CategoryDependencies
Backend-LanguageJava 17
Backend-FrameworkSpring Boot 3.3.2
Main LibrariesSpring Security 6.3.1, Spring Security Authorization Server 1.3.1, JPA
Package-ManagerMaven 3.6.3 (mvnw, Dockerfile)
RDBMSMysql 8.0.17

Run the App

Import the SQL file in themysql folder.

Install Maven

# Do NOT use your latest Maven version, but mvnw here or one with the same version.cd libmvnw clean installcd ..cd clientmvnw clean install# Integration tests are done here, which creates docs by Spring-Rest-Doc.
  • Run the client module by runningSpringSecurityOauth2PasswordJpaImplApplication in the client.
  • The API information is found onhttp://localhost:8370/docs/api-app.html, managed by Spring Rest Doc

img.png

  • In case you use IntelliJ, I recommend creating an empty project and importing the API (root) module and client module separately.
  • The client module definitely consumes the API module, but not vice versa.

API Guide

Registration

  • See theclient folder.
  • As the Api module consumes JPA, adding it to Beans is required.
// Add 'io.github.patternhelloworld.securityhelper.oauth2.api'@SpringBootApplication(scanBasePackages =  {"com.patternhelloworld.securityhelper.oauth2.client","io.github.patternhelloworld.securityhelper.oauth2.api"})publicclassSpringSecurityOauth2PasswordJpaImplApplication {publicstaticvoidmain(String[]args) {SpringApplication.run(SpringSecurityOauth2PasswordJpaImplApplication.class,args);    }}
@Configuration// ADD 'io.github.patternhelloworld.securityhelper.oauth2.api.config.security'@EnableJpaRepositories(basePackages = {"com.patternhelloworld.securityhelper.oauth2.client.domain","com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl","io.github.patternhelloworld.securityhelper.oauth2.api.config.security"},entityManagerFactoryRef ="commonEntityManagerFactory",transactionManagerRef="commonTransactionManager")publicclassCommonDataSourceConfiguration {// ADD 'io.github.patternhelloworld.securityhelper.oauth2.api.config.security'@Primary@Bean(name ="commonEntityManagerFactory")publicLocalContainerEntityManagerFactoryBeancommonEntityManagerFactory(EntityManagerFactoryBuilderbuilder) {returnbuilder                .dataSource(commonDataSource())                .packages("com.patternhelloworld.securityhelper.oauth2.client.domain","io.github.patternhelloworld.securityhelper.oauth2.api.config.security")                .persistenceUnit("commonEntityManager")                .build();    }}

Implementations

  • As indicated, theclient folder demonstrates how to use this library.

"Mandatory" settings

  • The only mandatory setting isclient.config.securityimpl.service.userdetail.CustomUserDetailsServiceFactory. The rest depend on your specific situation.

"Customizable" settings

  • Insert your code when events happen such as tokens created

    • SecurityPointCut
    • See the source code inclient.config.securityimpl.aop
  • Register error user messages as desired

    • ISecurityUserExceptionMessageService
    • See the source code inclient.config.securityimpl.message
  • Customize the whole error payload as desired for all cases

    • What is "all cases"?
      • Authorization Server ("/oauth2/token", "/api/v1/traditional-oauth/token") and Resource Server (Bearer token authentication : 401, authorization (permission) : 403)
    • Customize errors of the following cases
      • Login (/oauth2/token) :client.config.securityimpl.response.CustomAuthenticationFailureHandlerImpl
      • Login (/api/v1/traditional-oauth/token) :client.config.response.error.GlobalExceptionHandler.authenticationException ("/api/v1/traditional-oauth/token", Resource Server (Bearer token inspection))
      • Resource Server (Bearer token expired or with a wrong value, 401) :client.config.securityimpl.response.CustomAuthenticationEntryPointImpl
      • Resource Server (Permission, 403, @PreAuthorized on your APIs)client.config.response.error.GlobalExceptionHandler.authorizationException
  • Customize the whole success payload as desired for the only "/oauth2/token"

    • client.config.securityimpl.response.CustomAuthenticationSuccessHandlerImpl
    • The success response payload of "/api/v1/traditional-oauth/token" is inapi.domain.traditionaloauth.dto and is not yet customizable.
  • Customize the verification logic for UsernamePassword and Client as desired

    • IOauth2AuthenticationHashCheckService
  • Customize OpaqueTokenIntrospector as desired (!This is for Resource Servers)

    • client.config.securityimpl.introspector.CustomResourceServerTokenIntrospector
    • # Introspection type configuration:# - api: The Resource Server sends introspection requests to the Authorization Server.#        Benefits: High scalability and real-time authorization checks.#        Drawbacks: Increased traffic due to frequent API calls.## - database: The Resource Server and Authorization Server share the same database.#             Benefits: Minimal traffic and real-time authorization checks.#             Drawbacks: Limited scalability due to direct database dependency.## - decode: The Resource Server decodes the Access Token locally using the JWT algorithm.#           Benefits: No traffic and high scalability.#           Drawbacks: Lacks real-time authorization updates.## [WARNING] api: Certain test cases are currently failing due to issues with the specified introspection URI calls.patternhelloworld.securityhelper.oauth2.introspection.type=databasepatternhelloworld.securityhelper.oauth2.introspection.uri=http://localhost:8370/oauth2/introspectpatternhelloworld.securityhelper.oauth2.introspection.client-id=client_customerpatternhelloworld.securityhelper.oauth2.introspection.client-secret=12345

OAuth2 - ROPC

  • Refer toclient/src/docs/asciidoc/api-app.adoc

OAuth2 - Authorization Code

  • How to set it up
    1. Create your own login page with the /login route as indicated in the client project (In the future, this address will be customisable):
    @ControllerpublicclassLoginWeb {@GetMapping("/login")publicStringloginPage() {return"login";      }  }
    spring.mvc.view.prefix=/templates/spring.mvc.view.suffix=.html
    1. Check the login page at the "resources/templates/login.hml"
    2. Ensure the callback URL (http://localhost:8081/callback1) is properly set in theoauth2_registered_client table in the database.
  • How to use
    1. Open the web browser by connecting tohttp://localhost:8370/oauth2/authorize?client_id=client_customer&state=xxx&scope=read&redirect_uri=http%3A%2F%2Flocalhost%3A8081%2Fcallback1&code_challenge=HVoKJYs8JruAxs7hKcG4oLpJXCP-z1jJQtXpQte6GyA&code_challenge_method=S256, using the values from theoauth2_registered_client
      • PKCE (code_challege, code_challege_METHOD) is optional.
      • PKCE adds a Code Verifier and a Code Challenge to the flow, enhancing the Authorization Code Grant Flow by preventing the issuance of an Access Token if the Authorization Code is compromised.
    2. Login withcicd@test.com / 1234
    3. You will be redirected tohttps://localhost:8081/callback1?code=215e9539-1dcb-4843-b1ea-b2d7be0a3c44&state=xxx
      • However, ifpatternhelloworld.securityhelper.authorization-code.consent=Yis set in theapplication.properties, it will be redirected to the consent page.
    4. You can login with the API in the Postman
      • img4.png
      • code_verifier sample : EAp91aanXdoMcoOc2Il55H3UDDIV909k9olEEcl6L24J6_9X

Running this App with Docker

Contribution Guide

  • You can create a pull request directly to the main branch.
  • Integration tests in the client folder are sufficient for now, but you may add more if necessary.
  • There is a lack of unit tests, so contributions to unit test code are welcome, which will help improve the overall codebase.

[8]ページ先頭

©2009-2025 Movatter.jp