Skip to content

Commit 976200e

Browse files
feature : new Oauth2 supporting module (authorization code) development started
1 parent dc75d3d commit 976200e

File tree

14 files changed

+424
-38
lines changed

14 files changed

+424
-38
lines changed

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
# Spring Security Oauth2 Password JPA Implementation
2-
3-
> App-Token based OAuth2 ROPC POC built to grow with Spring Boot and ORM
1+
# Spring Security Oauth2 JPA Implementation
2+
3+
> App-Token based OAuth2 POC built to grow with Spring Boot and ORM
4+
>
5+
## Supporting Oauth2 Type
6+
| ROPC | Authorization Code |
7+
|-----------|-------------------|
8+
| available | in development |
49

510
## Quick Start
611
```xml

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/securityimpl/web/WebMvcConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
1414

1515
@Override
1616
public void addResourceHandlers(ResourceHandlerRegistry registry) {
17-
registry.addResourceHandler("/docs/**").addResourceLocations("classpath:/static/docs/");
17+
registry.addResourceHandler("/docs/**", "/favicon.ico").addResourceLocations("classpath:/static/docs/");
1818
}
1919

2020
}

client/src/main/resources/application.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,6 @@ spring.servlet.multipart.maxRequestSize=10MB
6868
management.endpoints.web.exposure.include=*
6969
app.timezone=Asia/Seoul
7070

71+
logginglevel.org.springframework.security=trace
72+
73+
io.github.patternknife.securityhelper.oauth2.no-app-token-same-access-token=true

client/src/test/java/com/patternknife/securityhelper/oauth2/client/integration/auth/CustomerIntegrationTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public void setUp(RestDocumentationContextProvider restDocumentationContextProvi
9797

9898
}
9999

100+
100101
@Test
101102
public void test_SameAppTokensUseSameAccessToken_EXPOSED() throws Exception {
102103

mysql/schema.sql

Lines changed: 15 additions & 3 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package io.github.patternknife.securityhelper.oauth2.api.config.security.converter.auth.endpoint;
2+
3+
4+
import io.github.patternknife.securityhelper.oauth2.api.config.security.util.RequestOAuth2Distiller;
5+
import jakarta.servlet.http.HttpServletRequest;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.lang.Nullable;
8+
import org.springframework.security.core.Authentication;
9+
import org.springframework.security.core.context.SecurityContextHolder;
10+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
11+
12+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
13+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
14+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
15+
16+
17+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
18+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
19+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
20+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
21+
import org.springframework.security.web.authentication.AuthenticationConverter;
22+
import org.springframework.util.MultiValueMap;
23+
import org.springframework.util.StringUtils;
24+
25+
import java.util.*;
26+
27+
@RequiredArgsConstructor
28+
public final class AuthorizationCodeAuthenticationConverter implements AuthenticationConverter {
29+
30+
/*
31+
* `
32+
* CustomGrantAuthenticationToken <- OAuth2ClientAuthenticationToken
33+
* /oauth2/authorize?response_type=code&client_id=client_customer&redirect_uri=http://localhost:8370/callback1&scope=read%20write&state=random-state&prompt=consent&access_type=offline
34+
* */
35+
private final RegisteredClientRepository registeredClientRepository;
36+
37+
public void setClientAuthentication(String clientId) {
38+
39+
RegisteredClient registeredClient = registeredClientRepository.findByClientId(clientId);
40+
41+
if (registeredClient == null) {
42+
throw new IllegalArgumentException("Invalid client ID");
43+
}
44+
45+
OAuth2ClientAuthenticationToken clientAuthenticationToken = new OAuth2ClientAuthenticationToken(registeredClient , ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);
46+
47+
SecurityContextHolder.getContext().setAuthentication(clientAuthenticationToken);
48+
}
49+
50+
@Nullable
51+
@Override
52+
public Authentication convert(HttpServletRequest request) {
53+
MultiValueMap<String, String> parameters = RequestOAuth2Distiller.getAuthorizationCodeSecurityAdditionalParameters(request);
54+
55+
// grant_type (REQUIRED)
56+
String grantType = parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE);
57+
if (!AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(grantType)) {
58+
return null;
59+
}
60+
61+
// 클라이언트 인증 설정
62+
setClientAuthentication(parameters.getFirst(OAuth2ParameterNames.CLIENT_ID));
63+
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
64+
65+
// code (REQUIRED) - Authorization Code 요청 시에는 아직 발급되지 않음
66+
String code = parameters.getFirst(OAuth2ParameterNames.CODE);
67+
if (!StringUtils.hasText(code) || parameters.get(OAuth2ParameterNames.CODE).size() != 1) {
68+
// 예외 처리 필요 시 여기에 추가
69+
}
70+
71+
// redirect_uri (REQUIRED)
72+
String redirectUri = parameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);
73+
if (StringUtils.hasText(redirectUri) && parameters.get(OAuth2ParameterNames.REDIRECT_URI).size() != 1) {
74+
// 예외 처리 필요 시 여기에 추가
75+
}
76+
77+
// scopes
78+
Set<String> scopes = new HashSet<>(parameters.getOrDefault(OAuth2ParameterNames.SCOPE, Collections.emptyList()));
79+
80+
// 추가적인 파라미터 처리
81+
Map<String, Object> additionalParameters = new HashMap<>();
82+
parameters.forEach((key, value) -> {
83+
additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));
84+
});
85+
86+
// OAuth2AuthorizationCodeRequestAuthenticationToken 생성
87+
return new OAuth2AuthorizationCodeRequestAuthenticationToken(
88+
request.getRequestURI(), // authorizationUri
89+
parameters.getFirst(OAuth2ParameterNames.CLIENT_ID), // clientId
90+
clientPrincipal, // principal (사용자 인증 객체)
91+
redirectUri, // redirectUri
92+
parameters.getFirst(OAuth2ParameterNames.STATE), // state
93+
scopes, // 요청한 스코프
94+
additionalParameters // 추가 파라미터
95+
);
96+
}
97+
98+
99+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
import org.springframework.security.core.Authentication;
88
import org.springframework.security.core.context.SecurityContextHolder;
99
import org.springframework.security.oauth2.core.AuthorizationGrantType;
10+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
11+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
1012
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
1113
import org.springframework.security.web.authentication.AuthenticationConverter;
1214

1315
import java.util.Map;
1416

1517
@RequiredArgsConstructor
16-
public final class KnifeGrantAuthenticationConverter implements AuthenticationConverter {
18+
public final class PasswordAuthenticationConverter implements AuthenticationConverter {
1719
/*
1820
* `
1921
* CustomGrantAuthenticationToken <- OAuth2ClientAuthenticationToken
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.github.patternknife.securityhelper.oauth2.api.config.security.dao;
2+
3+
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.KnifeAuthorizationConsent;
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 KnifeAuthorizationConsentRepository extends JpaRepository<KnifeAuthorizationConsent, KnifeAuthorizationConsent.AuthorizationConsentId> {
11+
Optional<KnifeAuthorizationConsent> findByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
12+
void deleteByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package io.github.patternknife.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(KnifeAuthorizationConsent.AuthorizationConsentId.class)
11+
public class KnifeAuthorizationConsent {
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+
}

src/main/java/io/github/patternknife/securityhelper/oauth2/api/config/security/provider/auth/endpoint/KnifeOauth2AuthenticationProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
@AllArgsConstructor
3232
public final class KnifeOauth2AuthenticationProvider implements AuthenticationProvider {
3333

34-
private final CommonOAuth2AuthorizationSaver commonOAuth2AuthorizationCycle;
34+
private final CommonOAuth2AuthorizationSaver commonOAuth2AuthorizationSaver;
3535
private final ConditionalDetailsService conditionalDetailsService;
3636
private final DefaultOauth2AuthenticationHashCheckService oauth2AuthenticationHashCheckService;
3737
private final OAuth2AuthorizationServiceImpl oAuth2AuthorizationService;
@@ -67,7 +67,7 @@ public Authentication authenticate(Authentication authentication)
6767
}
6868

6969

70-
OAuth2Authorization oAuth2Authorization = commonOAuth2AuthorizationCycle.save(userDetails, ((CustomGrantAuthenticationToken) authentication).getGrantType(), clientId, ((CustomGrantAuthenticationToken) authentication).getAdditionalParameters(), null);
70+
OAuth2Authorization oAuth2Authorization = commonOAuth2AuthorizationSaver.save(userDetails, ((CustomGrantAuthenticationToken) authentication).getGrantType(), clientId, ((CustomGrantAuthenticationToken) authentication).getAdditionalParameters(), null);
7171

7272
RegisteredClient registeredClient = oAuth2ClientAuthenticationToken.getRegisteredClient();
7373

0 commit comments

Comments
 (0)