Skip to content

Commit 6435ad2

Browse files
feature : set authorization code base
1 parent 976200e commit 6435ad2

31 files changed

+986
-110
lines changed

client/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,14 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
157157
<!-- Follow ${spring-boot-dependencies.version} -->
158158
</dependency>
159159

160+
<!-- Login Page -->
161+
<dependency>
162+
<groupId>org.springframework.boot</groupId>
163+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
164+
<!-- Follow ${spring-boot-dependencies.version} -->
165+
</dependency>
166+
167+
160168

161169

162170
<!--4. TEST-->

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/logger/common/LoggingFilter.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
3434
return;
3535
}
3636

37-
HttpServletRequest requestToCache = new ContentCachingRequestWrapper(request);
37+
// HttpServletRequest requestToCache = new ContentCachingRequestWrapper(request);
3838
// HttpServletResponse responseToCache = new ContentCachingResponseWrapper(response);
3939

40-
chain.doFilter(requestToCache, servletResponse);
40+
// chain.doFilter(requestToCache, servletResponse);
4141
} else {
42-
chain.doFilter(servletRequest, servletResponse);
42+
//chain.doFilter(servletRequest, servletResponse);
4343
}
44+
chain.doFilter(servletRequest, servletResponse);
4445

4546

4647
}

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/securityimpl/message/CustomSecurityUserExceptionMessage.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public enum CustomSecurityUserExceptionMessage implements ExceptionMessageInterf
1616
AUTHENTICATION_WRONG_ID_PASSWORD("1User information could not be verified. Please check your ID or password. If the problem persists, please contact customer service."),
1717
AUTHENTICATION_PASSWORD_FAILED_EXCEEDED("1The number of password attempts has been exceeded."),
1818

19+
// Wrong Authorization Code
20+
AUTHORIZATION_CODE_NO_EXISTS("1The specified Authorization code does not exist."),
21+
1922
// CLIENT ID, SECRET
2023
AUTHENTICATION_WRONG_CLIENT_ID_SECRET("1Client information is not verified."),
2124

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/**", "/favicon.ico").addResourceLocations("classpath:/static/docs/");
17+
registry.addResourceHandler("/docs/**").addResourceLocations("classpath:/static/docs/");
1818
}
1919

2020
}

client/src/main/java/com/patternknife/securityhelper/oauth2/client/domain/common/api/BaseApi.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ public String home () {
3030
return "home";
3131
}
3232

33+
34+
3335
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.patternknife.securityhelper.oauth2.client.domain.common.web;
2+
3+
import org.springframework.stereotype.Controller;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
6+
@Controller
7+
public class LoginWeb {
8+
@GetMapping("/login")
9+
public String loginPage() {
10+
return "login";
11+
}
12+
}

client/src/main/java/com/patternknife/securityhelper/oauth2/client/domain/traditionaloauth/api/v1/TraditionalOauthApi.java

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
package com.patternknife.securityhelper.oauth2.client.domain.traditionaloauth.api.v1;
22

33

4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.nimbusds.jose.util.Base64;
47
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
58
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
69

710
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
811
import io.github.patternknife.securityhelper.oauth2.api.domain.traditionaloauth.dto.SpringSecurityTraditionalOauthDTO;
912
import io.github.patternknife.securityhelper.oauth2.api.domain.traditionaloauth.service.TraditionalOauthService;
13+
import lombok.Getter;
1014
import lombok.RequiredArgsConstructor;
15+
import lombok.ToString;
16+
import org.springframework.http.*;
17+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
18+
import org.springframework.util.LinkedMultiValueMap;
19+
import org.springframework.util.MultiValueMap;
1120
import org.springframework.web.bind.annotation.*;
21+
import org.springframework.web.client.RestTemplate;
1222

1323
@RestController
14-
@RequestMapping("/api/v1")
1524
@RequiredArgsConstructor
1625
public class TraditionalOauthApi {
1726

1827
private final TraditionalOauthService traditionalOauthService;
1928
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
2029

21-
@PostMapping("/traditional-oauth/token")
30+
@PostMapping("/api/v1/traditional-oauth/token")
2231
public SpringSecurityTraditionalOauthDTO.TokenResponse createAccessToken(
2332
@ModelAttribute SpringSecurityTraditionalOauthDTO.TokenRequest tokenRequest,
2433
@RequestHeader("Authorization") String authorizationHeader){
@@ -32,4 +41,57 @@ public SpringSecurityTraditionalOauthDTO.TokenResponse createAccessToken(
3241
}
3342
}
3443

44+
@PostMapping("/api/v1/traditional-oauth/authorization-code")
45+
public SpringSecurityTraditionalOauthDTO.AuthorizationCodeResponse createAuthorizationCode(
46+
@ModelAttribute SpringSecurityTraditionalOauthDTO.AuthorizationCodeRequest authorizationCodeRequest,
47+
@RequestHeader("Authorization") String authorizationHeader){
48+
49+
// authorization_code 생성
50+
return traditionalOauthService.createAuthorizationCode(authorizationCodeRequest, authorizationHeader);
51+
}
52+
53+
@GetMapping("/callback1")
54+
public String callback(@RequestParam String code) throws JsonProcessingException {
55+
System.out.println("code = " + code);
56+
57+
OauthTokenDto token = getToken(code);
58+
System.out.println("getToken() = " + token);
59+
60+
return token.toString();
61+
}
62+
63+
private RestTemplate restTemplate;
64+
private ObjectMapper objectMapper;
65+
66+
private OauthTokenDto getToken(String code) throws JsonProcessingException {
67+
String credentials = "testClientId:testSecret";
68+
String encodedCredentials = new String(Base64.encode(credentials.getBytes()).decode());
69+
70+
HttpHeaders headers = new HttpHeaders();
71+
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
72+
headers.add("Authorization", "Basic " + encodedCredentials);
73+
74+
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
75+
params.add("code", code);
76+
params.add("grant_type", "authorization_code");
77+
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
78+
ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8080/oauth2/token", request, String.class);
79+
if (response.getStatusCode() == HttpStatus.OK) {
80+
System.out.println("response.getBody() = " + response.getBody());
81+
OauthTokenDto oauthTokenDto = objectMapper.readValue(response.getBody(), OauthTokenDto.class);
82+
return oauthTokenDto;
83+
}
84+
return null;
85+
}
86+
87+
@Getter
88+
@ToString
89+
static class OauthTokenDto {
90+
private String access_token;
91+
private String token_type;
92+
private String refresh_token;
93+
private long expires_in;
94+
private String scope;
95+
}
96+
3597
}

client/src/main/resources/application.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,7 @@ app.timezone=Asia/Seoul
7070

7171
logginglevel.org.springframework.security=trace
7272

73-
io.github.patternknife.securityhelper.oauth2.no-app-token-same-access-token=true
73+
io.github.patternknife.securityhelper.oauth2.no-app-token-same-access-token=true
74+
75+
spring.mvc.view.prefix=/templates/
76+
spring.mvc.view.suffix=.html
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<!DOCTYPE html>
2+
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>Custom consent page - Consent required</title>
7+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
8+
<script>
9+
function cancelConsent() {
10+
document.consent_form.reset();
11+
document.consent_form.submit();
12+
}
13+
</script>
14+
</head>
15+
<body>
16+
<div class="container">
17+
<div class="row py-5">
18+
<h1 class="text-center text-primary">App permissions</h1>
19+
</div>
20+
<div class="row">
21+
<div class="col text-center">
22+
<p>
23+
The application
24+
<span class="fw-bold text-primary" th:text="${clientId}"></span>
25+
wants to access your account
26+
<span class="fw-bold" th:text="${principalName}"></span>
27+
</p>
28+
</div>
29+
</div>
30+
<div th:if="${userCode}" class="row">
31+
<div class="col text-center">
32+
<p class="alert alert-warning">
33+
You have provided the code
34+
<span class="fw-bold" th:text="${userCode}"></span>.
35+
Verify that this code matches what is shown on your device.
36+
</p>
37+
</div>
38+
</div>
39+
<div class="row pb-3">
40+
<div class="col text-center">
41+
<p>
42+
The following permissions are requested by the above app.<br/>
43+
Please review these and consent if you approve.
44+
</p>
45+
</div>
46+
</div>
47+
<div class="row">
48+
<div class="col text-center">
49+
<form name="consent_form" method="post" th:action="${requestURI}">
50+
<input type="hidden" name="client_id" th:value="${clientId}">
51+
<input type="hidden" name="state" th:value="${state}">
52+
<input th:if="${userCode}" type="hidden" name="user_code" th:value="${userCode}">
53+
54+
<div th:each="scope: ${scopes}" class="form-check py-1">
55+
<input class="form-check-input"
56+
style="float: none"
57+
type="checkbox"
58+
name="scope"
59+
th:value="${scope.scope}"
60+
th:id="${scope.scope}">
61+
<label class="form-check-label fw-bold px-2" th:for="${scope.scope}" th:text="${scope.scope}"></label>
62+
<p class="text-primary" th:text="${scope.description}"></p>
63+
</div>
64+
65+
<p th:if="${not #lists.isEmpty(previouslyApprovedScopes)}">
66+
You have already granted the following permissions to the above app:
67+
</p>
68+
<div th:each="scope: ${previouslyApprovedScopes}" class="form-check py-1">
69+
<input class="form-check-input"
70+
style="float: none"
71+
type="checkbox"
72+
th:id="${scope.scope}"
73+
disabled
74+
checked>
75+
<label class="form-check-label fw-bold px-2" th:for="${scope.scope}" th:text="${scope.scope}"></label>
76+
<p class="text-primary" th:text="${scope.description}"></p>
77+
</div>
78+
79+
<div class="pt-3">
80+
<button class="btn btn-primary btn-lg" type="submit" id="submit-consent">
81+
Submit Consent
82+
</button>
83+
</div>
84+
<div class="pt-3">
85+
<button class="btn btn-link regular" type="button" id="cancel-consent" onclick="cancelConsent();">
86+
Cancel
87+
</button>
88+
</div>
89+
</form>
90+
</div>
91+
</div>
92+
<div class="row pt-4">
93+
<div class="col text-center">
94+
<p>
95+
<small>
96+
Your consent to provide access is required.<br/>
97+
If you do not approve, click Cancel, in which case no information will be shared with the app.
98+
</small>
99+
</p>
100+
</div>
101+
</div>
102+
</div>
103+
</body>
104+
</html>

0 commit comments

Comments
 (0)