Skip to content

Commit 27eb117

Browse files
Merge branch 'v3-preparation'
2 parents 0e3a576 + 5989b75 commit 27eb117

34 files changed

+645
-605
lines changed

README.md

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
# Spring Security Oauth2 Password JPA Implementation
2-
## Overview
32

4-
* Complete separation of the library (API) and the client for testing it
3+
> One OAuth2 ROPC POC built to grow with Spring Boot and ORM
54
5+
## Quick Start
66
```xml
77
<dependency>
88
<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
99
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
10-
<version>2.8.1</version>
10+
<version>3.0.0</version>
1111
</dependency>
1212
```
13+
14+
## Overview
15+
16+
* Complete separation of the library (API) and the client for testing it
17+
1318
* 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 have `the same request & response payloads for success and errors`. (However, ``/oauth2/token`` is the standard that "spring-authorization-server" provides.)
1419
* As you are aware, the API ``/oauth2/token`` is what "spring-authorization-server" provides.
1520
* ``/api/v1/traditional-oauth/token`` is what this library implemented directly.
@@ -40,15 +45,17 @@
4045
* Authentication management based on a combination of username, client ID, and App-Token
4146
* What is an App-Token? An App-Token is a new access token generated each time the same account logs in. If the token values are the same, the same access token is shared.
4247
* Separated UserDetails implementation for Admin and Customer roles as an example. (This can be extended as desired by implementing ``UserDetailsServiceFactory``)
43-
* Provide MySQL DDL, which consists of oauth\_access\_token, oauth\_refresh\_token and oauth\_client\_details, which is tables in Security 5. As I mean to migrate current security system to Security 6, I haven't changed them to the ``authorization`` table indicated in https://github.com/spring-projects/spring-authorization-server.
48+
* For versions greater than or equal to v3, including the latest version (Spring Security 6), provide MySQL DDL, which consists of ``oauth2_authorization`` and ``oauth2_registered_client``.
49+
* For v2 (Spring Security 5), provide MySQL DDL, which consists of ``oauth_access_token, oauth_refresh_token and oauth_client_details``, which are tables in Security 5. As I meant to migrate current security system to Security 6 back then, I hadn't changed them to the ``oauth2_authorization`` table indicated in https://github.com/spring-projects/spring-authorization-server.
50+
4451
* Application of Spring Rest Docs
4552

4653
## Dependencies
4754

4855
| Category | Dependencies |
4956
|-------------------|-------------------------------------------------------------------|
5057
| Backend-Language | Java 17 |
51-
| Backend-Framework | Spring Boot 3.3.2 |
58+
| Backend-Framework | Spring Boot 3.3.2 (the latest version) |
5259
| Main Libraries | Spring Security 6.3.1, Spring Security Authorization Server 1.3.1 |
5360
| Package-Manager | Maven 3.6.3 (mvnw, Dockerfile) |
5461
| RDBMS | Mysql 8.0.17 |
@@ -149,6 +156,9 @@ public class CommonDataSourceConfiguration {
149156
- ``client.config.securityimpl.response.CustomAuthenticationSuccessHandlerImpl``
150157
- The success response payload of "/api/v1/traditional-oauth/token" is in ``api.domain.traditionaloauth.dto`` and is not yet customizable.
151158

159+
- **Customize the verification logic for UsernamePassword and Client as desired**
160+
- ``IOauth2AuthenticationHashCheckService``
161+
152162
## Running this App with Docker
153163
* Use the following module for Blue-Green deployment:
154164
* https://github.com/patternknife/docker-blue-green-runner

client/pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
77
<modelVersion>4.0.0</modelVersion>
88
<groupId>com.patternknife.securityhelper.oauth2.client</groupId>
99
<artifactId>spring-security-oauth2-password-jpa-implementation-client</artifactId>
10-
<version>2.8.1</version>
10+
<version>3.0.0</version>
1111
<packaging>jar</packaging>
1212

1313
<properties>
@@ -48,7 +48,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
4848
<dependency>
4949
<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
5050
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
51-
<version>2.8.1</version>
51+
<version>3.0.0</version>
5252
</dependency>
5353

5454
<!-- DB -->

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/logger/module/ResponseErrorLogConfig.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import com.patternknife.securityhelper.oauth2.client.config.response.error.GlobalExceptionHandler;
44
import com.patternknife.securityhelper.oauth2.client.config.logger.common.CommonLoggingRequest;
55

6-
import com.patternknife.securityhelper.oauth2.client.config.response.error.dto.CustomErrorResponsePayload;
6+
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.SecurityKnifeErrorResponsePayload;
78
import org.aspectj.lang.JoinPoint;
89
import org.aspectj.lang.annotation.AfterReturning;
910
import org.aspectj.lang.annotation.Aspect;
@@ -30,12 +31,12 @@ public void endpointAfterExceptionReturning(JoinPoint p, Object returnValue) {
3031
try {
3132
if (p.getTarget().getClass().equals(GlobalExceptionHandler.class)) {
3233

33-
CustomErrorResponsePayload customErrorResponsePayload = (CustomErrorResponsePayload) ((ResponseEntity) returnValue).getBody();
34+
SecurityKnifeErrorResponsePayload errorResponsePayload = (SecurityKnifeErrorResponsePayload) ((ResponseEntity) returnValue).getBody();
3435
loggedText += String.format("[After - Error Response]\n message : %s || \n userMessage : %s || \n cause : %s || \n stackTrace : %s",
35-
customErrorResponsePayload != null ? customErrorResponsePayload.getMessage() : "No error message",
36-
customErrorResponsePayload != null ? customErrorResponsePayload.getUserMessage() : "No error userMessage",
37-
customErrorResponsePayload != null ? customErrorResponsePayload.getCause() : "No error detail cause",
38-
customErrorResponsePayload != null ? customErrorResponsePayload.getStackTrace() : "No error detail stack trace");
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");
3940
}
4041
} catch (Exception ex4) {
4142

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/response/error/CustomExceptionUtils.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import com.patternknife.securityhelper.oauth2.client.config.logger.common.CommonLoggingRequest;
44

55
import com.patternknife.securityhelper.oauth2.client.config.logger.module.ResponseSuccessLogConfig;
6-
import com.patternknife.securityhelper.oauth2.client.config.response.error.dto.CustomErrorResponsePayload;
6+
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.SecurityKnifeErrorResponsePayload;
78
import org.apache.commons.lang3.exception.ExceptionUtils;
89
import org.slf4j.Logger;
910
import org.slf4j.LoggerFactory;
@@ -27,10 +28,10 @@ public static void createNonStoppableErrorMessage(String message) {
2728
} catch (Exception ex2) {
2829
logger.error(ex2.getMessage());
2930
} finally {
30-
CustomErrorResponsePayload customErrorResponsePayload = new CustomErrorResponsePayload(message, "Without error param " + " / Thread ID = " + Thread.currentThread().getId() + " / StackTrace",
31+
SecurityKnifeErrorResponsePayload errorResponsePayload = new SecurityKnifeErrorResponsePayload(message, "Without error param " + " / Thread ID = " + Thread.currentThread().getId() + " / StackTrace",
3132
message, "", "");
3233

33-
logger.error(" / " + customErrorResponsePayload.toString());
34+
logger.error(" / " + errorResponsePayload.toString());
3435
}
3536

3637
}
@@ -45,10 +46,10 @@ public static void createNonStoppableErrorMessage(String message, Throwable ex)
4546
} catch (Exception ex2) {
4647
logger.error(ex2.getMessage());
4748
} finally {
48-
CustomErrorResponsePayload customErrorResponsePayload = new CustomErrorResponsePayload(message, "Without error param " + " / Thread ID = " + Thread.currentThread().getId() + " / StackTrace",
49+
SecurityKnifeErrorResponsePayload errorResponsePayload = new SecurityKnifeErrorResponsePayload(message, "Without error param " + " / Thread ID = " + Thread.currentThread().getId() + " / StackTrace",
4950
message, CustomExceptionUtils.getAllStackTraces(ex), CustomExceptionUtils.getAllCauses(ex));
5051

51-
logger.error(" / " + customErrorResponsePayload.toString());
52+
logger.error(" / " + errorResponsePayload.toString());
5253
}
5354

5455
}

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/response/error/GlobalExceptionHandler.java

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package com.patternknife.securityhelper.oauth2.client.config.response.error;
22

33

4-
import com.patternknife.securityhelper.oauth2.client.config.response.error.dto.CustomErrorResponsePayload;
5-
64
import com.patternknife.securityhelper.oauth2.client.config.response.error.message.GeneralErrorMessage;
75
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.util.ExceptionKnifeUtils;
8-
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.ErrorResponsePayload;
6+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.SecurityKnifeErrorResponsePayload;
97
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
108
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
119
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
@@ -40,13 +38,13 @@ public class GlobalExceptionHandler {
4038
// 401 : Authentication
4139
@ExceptionHandler({AuthenticationException.class})
4240
public ResponseEntity<?> authenticationException(Exception ex, WebRequest request) {
43-
ErrorResponsePayload errorResponsePayload;
41+
SecurityKnifeErrorResponsePayload errorResponsePayload;
4442
if(ex instanceof KnifeOauth2AuthenticationException && ((KnifeOauth2AuthenticationException) ex).getErrorMessages() != null) {
45-
errorResponsePayload = new ErrorResponsePayload(((KnifeOauth2AuthenticationException) ex).getErrorMessages(),
43+
errorResponsePayload = new SecurityKnifeErrorResponsePayload(((KnifeOauth2AuthenticationException) ex).getErrorMessages(),
4644
ex, request.getDescription(false), ExceptionKnifeUtils.getAllStackTraces(ex),
4745
ExceptionKnifeUtils.getAllCauses(ex), null);
4846
}else {
49-
errorResponsePayload = new ErrorResponsePayload(ExceptionKnifeUtils.getAllCauses(ex), request.getDescription(false), iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_FAILURE),
47+
errorResponsePayload = new SecurityKnifeErrorResponsePayload(ExceptionKnifeUtils.getAllCauses(ex), request.getDescription(false), iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_FAILURE),
5048
ex.getMessage(), ex.getStackTrace()[0].toString());
5149
}
5250
return new ResponseEntity<>(errorResponsePayload, HttpStatus.UNAUTHORIZED);
@@ -55,17 +53,17 @@ public ResponseEntity<?> authenticationException(Exception ex, WebRequest reques
5553
// 403 : Authorization
5654
@ExceptionHandler({ AccessDeniedException.class })
5755
public ResponseEntity<?> authorizationException(Exception ex, WebRequest request) {
58-
ErrorResponsePayload errorResponsePayload = new ErrorResponsePayload(ex.getMessage() != null ? ex.getMessage() : ExceptionKnifeUtils.getAllCauses(ex), request.getDescription(false),
56+
SecurityKnifeErrorResponsePayload errorResponsePayload = new SecurityKnifeErrorResponsePayload(ex.getMessage() != null ? ex.getMessage() : ExceptionKnifeUtils.getAllCauses(ex), request.getDescription(false),
5957
ex.getMessage() == null || ex.getMessage().equals("Access Denied") ? iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHORIZATION_FAILURE) : ex.getMessage(), ex.getStackTrace()[0].toString());
6058
return new ResponseEntity<>(errorResponsePayload, HttpStatus.FORBIDDEN);
6159
}
6260

6361
// Unhandled
6462
@ExceptionHandler(Exception.class)
6563
public ResponseEntity<?> unhandledExceptionHandler(Exception ex, WebRequest request) {
66-
CustomErrorResponsePayload customErrorResponsePayload = new CustomErrorResponsePayload(ex.getMessage(), request.getDescription(false), GeneralErrorMessage.UNHANDLED_ERROR.getUserMessage(),
64+
SecurityKnifeErrorResponsePayload errorResponsePayload = new SecurityKnifeErrorResponsePayload(ex.getMessage(), request.getDescription(false), GeneralErrorMessage.UNHANDLED_ERROR.getUserMessage(),
6765
CustomExceptionUtils.getAllStackTraces(ex), CustomExceptionUtils.getAllCauses(ex));
68-
return new ResponseEntity<>(customErrorResponsePayload, HttpStatus.INTERNAL_SERVER_ERROR);
66+
return new ResponseEntity<>(errorResponsePayload, HttpStatus.INTERNAL_SERVER_ERROR);
6967
}
7068

7169
}

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/response/error/dto/CustomErrorResponsePayload.java

-96
This file was deleted.

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/securityimpl/aop/SecurityPointCutImpl.java

+3-6
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33

44
import io.github.patternknife.securityhelper.oauth2.api.config.security.aop.SecurityPointCut;
5-
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.KnifeOauthAccessToken;
6-
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.KnifeOauthRefreshToken;
7-
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.KnifeOauthClientDetail;
85

6+
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.KnifeAuthorization;
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.KnifeClient;
98
import jakarta.annotation.Nullable;
109
import lombok.RequiredArgsConstructor;
1110

@@ -14,10 +13,8 @@
1413
@Service
1514
@RequiredArgsConstructor
1615
public class SecurityPointCutImpl implements SecurityPointCut {
17-
1816
@Override
19-
public <T> @Nullable T afterTokensSaved(@Nullable KnifeOauthAccessToken knifeOauthAccessToken, @Nullable KnifeOauthRefreshToken knifeOauthRefreshToken, @Nullable KnifeOauthClientDetail knifeOauthClientDetail) {
20-
17+
public <T> @Nullable T afterTokensSaved(@Nullable KnifeAuthorization knifeAuthorization, @Nullable KnifeClient knifeClient) {
2118
// Implement what you need right after tokens are persisted.
2219
return null;
2320
}

0 commit comments

Comments
 (0)