Skip to content

Commit 5f721c7

Browse files
committed
code iteration
1 parent bda7352 commit 5f721c7

29 files changed

+706
-369
lines changed

README.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
## 第二步:修改配置
88
修改application.yaml文件中的 idp 配置
99
```yaml
10+
custom:
11+
serverDomain: # 填写当前运行的域名地址
1012
idaas:
1113
oidc:
1214
clientId: # IDaaS应用中拿到的 client_id
1315
clientSecret: # IDaaS应用中拿到的 client_secret
1416
issuer: # IDaaS应用中拿到的Issuer
1517
scopes: # IDaaS应用中配置的scopes
16-
redirectUri: ${baseUri}/authentication/login #登录 Redirect URI
18+
redirectUri: ${custom.serverDomain}/authentication/login #登录 Redirect URI
1719
```
1820
application.yaml中的配置项分别对应IDaaS应用中的配置
1921
![img_3.png](src/main/resources/static/img/img_3.png)
@@ -22,7 +24,7 @@ application.yaml中的配置项分别对应IDaaS应用中的配置
2224
其中,redirectUri在当前项目中指向了/authentication/login
2325
## 第三步:在 IDaaS 中添加重定向地址redirect-uri
2426
- 因为要最终需要到LoginController接口中拿到access token令牌,故重定向地址为LoginController内的login方法所指向的uri,将其填写到IDaaS中。
25-
- {baseUri}/authentication/login
27+
- http://127.0.0.1:8082/authentication/login
2628
2729
## 第四步:添加授权
2830
### 添加授权
@@ -62,7 +64,7 @@ application.yaml中的配置项分别对应IDaaS应用中的配置
6264
```yaml
6365
idaas:
6466
oidc:
65-
openPkce: true #打开PKCE
67+
pkceRequired: true #打开PKCE
6668
codeChallengeMethod: S256 #加密算法
6769
```
6870
## 访问

src/main/java/com/aliyunidaas/sample/Application.java

-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
**/
1313
@SpringBootApplication
1414
public class Application {
15-
1615
public static void main(String[] args) {
1716
SpringApplication.run(Application.class, args);
1817
}
19-
2018
}

src/main/java/com/aliyunidaas/sample/common/EndpointContext.java

+6-33
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ public class EndpointContext {
1515

1616
private String userinfoEndpoint;
1717

18+
private String jwksUri;
19+
1820
public EndpointContext() {
1921
}
2022

@@ -42,41 +44,12 @@ public void setUserinfoEndpoint(String userinfoEndpoint) {
4244
this.userinfoEndpoint = userinfoEndpoint;
4345
}
4446

45-
public static EndpointContextBuilder getBuilder() {
46-
return EndpointContextBuilder.anEndpointContext();
47+
public String getJwksUri() {
48+
return jwksUri;
4749
}
4850

49-
public static final class EndpointContextBuilder {
50-
private String authorizationEndpoint;
51-
private String tokenEndpoint;
52-
private String userinfoEndpoint;
53-
54-
private EndpointContextBuilder() {}
55-
56-
public static EndpointContextBuilder anEndpointContext() {return new EndpointContextBuilder();}
57-
58-
public EndpointContextBuilder setAuthorizationEndpoint(String authorizationEndpoint) {
59-
this.authorizationEndpoint = authorizationEndpoint;
60-
return this;
61-
}
62-
63-
public EndpointContextBuilder setTokenEndpoint(String tokenEndpoint) {
64-
this.tokenEndpoint = tokenEndpoint;
65-
return this;
66-
}
67-
68-
public EndpointContextBuilder setUserinfoEndpoint(String userinfoEndpoint) {
69-
this.userinfoEndpoint = userinfoEndpoint;
70-
return this;
71-
}
72-
73-
public EndpointContext build() {
74-
EndpointContext endpointContext = new EndpointContext();
75-
endpointContext.setAuthorizationEndpoint(authorizationEndpoint);
76-
endpointContext.setTokenEndpoint(tokenEndpoint);
77-
endpointContext.setUserinfoEndpoint(userinfoEndpoint);
78-
return endpointContext;
79-
}
51+
public void setJwksUri(String jwksUri) {
52+
this.jwksUri = jwksUri;
8053
}
8154
}
8255

src/main/java/com/aliyunidaas/sample/common/SimpleAuthnInterceptor.java

+28-31
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
import javax.servlet.http.HttpServletRequest;
1515
import javax.servlet.http.HttpServletResponse;
1616
import java.io.IOException;
17+
import java.io.UnsupportedEncodingException;
1718
import java.net.MalformedURLException;
1819
import java.net.URL;
20+
import java.net.URLEncoder;
1921
import java.nio.charset.StandardCharsets;
2022
import java.security.MessageDigest;
2123
import java.security.NoSuchAlgorithmException;
@@ -36,33 +38,31 @@ public class SimpleAuthnInterceptor implements HandlerInterceptor {
3638

3739
private final static String ILLEGAL_ACCESS_EXCEPTION_MESSAGE = "the cookie is invalid, please clear the cookie and try again.";
3840

41+
private final static String UTF_8 = "UTF-8";
42+
3943
@Autowired
4044
private CustomOidcConfiguration customOidcConfiguration;
4145

4246
@Autowired
4347
private CacheManager cacheManager;
4448

45-
@Autowired
46-
private CommonUtil commonUtil;
47-
4849
@Override
4950
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
5051
throws IOException, NoSuchAlgorithmException, IllegalAccessException {
5152
Cookie cookie = WebUtils.getCookie(request, ParameterNameFactory.COOKIE_NAME);
5253
if (cookie == null) {
53-
String cacheKey = UUID.randomUUID().toString();
54-
String redirectUri = commonUtil.getRedirectUri(request);
55-
String loginUri = getAuthorizationEndpointLoginUri(request, cacheKey, redirectUri);
56-
if (customOidcConfiguration.isOpenPkce()) {
57-
loginUri = getPkceAuthorizationEndpointLoginUri(cacheKey, loginUri);
54+
String state = UUID.randomUUID().toString();
55+
String redirectUri = customOidcConfiguration.getRedirectUri();
56+
String iDaaSLoginUri = getIDaaSLoginUri(request, state, redirectUri);
57+
if (customOidcConfiguration.isPkceRequired()) {
58+
iDaaSLoginUri = getIDaaSLoginUri(state, iDaaSLoginUri);
5859
}
59-
response.sendRedirect(loginUri);
60+
response.sendRedirect(iDaaSLoginUri);
6061
} else {
6162
String cookieValue = cacheManager.getCache(CommonUtil.generateCacheKey(cookie.getValue(), ParameterNameFactory.COOKIE_NAME));
6263
if (StringUtils.isBlank(cookieValue) || !cookie.getValue().equals(cookieValue)) {
6364
throw new IllegalAccessException(ILLEGAL_ACCESS_EXCEPTION_MESSAGE);
6465
}
65-
6666
}
6767
return true;
6868
}
@@ -71,10 +71,11 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
7171
* Redirect the user to the authorization endpoint of the authorization server
7272
* and include some query parameters in the URL of the authorization endpoint
7373
*
74-
* @param cacheKey 用于作为缓存的key
74+
* @param state 用于作为缓存的key以及作为随机值用于跨站保护
7575
* @return 登录的重定向地址
7676
*/
77-
private String getAuthorizationEndpointLoginUri(HttpServletRequest request, String cacheKey, String redirectUri) throws MalformedURLException {
77+
private String getIDaaSLoginUri(HttpServletRequest request, String state, String redirectUri)
78+
throws MalformedURLException, UnsupportedEncodingException {
7879
EndpointContext endpointContext = cacheManager.getCache(customOidcConfiguration.getIssuer());
7980
String authorizationEndpoint = endpointContext.getAuthorizationEndpoint();
8081
final String clientId = customOidcConfiguration.getClientId();
@@ -83,40 +84,37 @@ private String getAuthorizationEndpointLoginUri(HttpServletRequest request, Stri
8384
final String queryString = request.getQueryString();
8485
final String requestUrl = request.getRequestURL().toString();
8586
final URL url = new URL(requestUrl);
86-
final String state = url.getPath() + (queryString == null ? "" : "?" + queryString) + ":" + cacheKey;
87+
String callbackUrl = url.getPath() + (queryString == null ? "" : "?" + queryString);
88+
cacheManager.setCache(CommonUtil.generateCacheKey(state, ParameterNameFactory.URI), callbackUrl);
8789
// responseType = code , It means that this is an authorization code request
88-
final String loginUrl = String.format(
89-
"%s?response_type=code&client_id=%s&redirect_uri=%s&scope=%s&state=%s",
90-
authorizationEndpoint,
91-
clientId,
92-
redirectUri,
93-
scopes,
94-
state);
95-
return loginUrl;
90+
return authorizationEndpoint +
91+
"?response_type=code"
92+
+ "&client_id=" + URLEncoder.encode(clientId, UTF_8)
93+
+ "&redirect_uri=" + URLEncoder.encode(redirectUri, UTF_8)
94+
+ "&scope=" + URLEncoder.encode(scopes, UTF_8)
95+
+ "&state=" + URLEncoder.encode(state, UTF_8);
9696
}
9797

9898
/**
9999
* Obtain the redirection URL of Authorization Code With PKCE Flow
100100
*
101101
* @param state 缓存code verifier 的key
102-
* @param loginUri 授权码情况下的重定向地址
102+
* @param iDaaSLoginUri 授权码情况下的重定向地址
103103
* @return pkce情况下登录的的重定向地址
104104
* @throws NoSuchAlgorithmException
105105
*/
106-
private String getPkceAuthorizationEndpointLoginUri(String state, String loginUri) throws NoSuchAlgorithmException {
106+
private String getIDaaSLoginUri(String state, String iDaaSLoginUri) throws NoSuchAlgorithmException, UnsupportedEncodingException {
107107
String codeVerifier = createCodeVerifier();
108108
String codeChallenge = codeVerifier;
109109
String codeChallengeMethod = customOidcConfiguration.getCodeChallengeMethod();
110110
if (codeChallengeMethod.equals(CodeChallengeMethodFactory.SHA_256)) {
111111
codeChallenge = createHash(codeVerifier);
112112
}
113113
cacheManager.setCache(CommonUtil.generateCacheKey(state, ParameterNameFactory.CODE_VERIFIER), codeVerifier);
114-
loginUri = String.format(
115-
"%s&code_challenge_method=%s&code_challenge=%s",
116-
loginUri,
117-
customOidcConfiguration.getCodeChallengeMethod(),
118-
codeChallenge);
119-
return loginUri;
114+
iDaaSLoginUri = iDaaSLoginUri
115+
+ "&code_challenge_method=" + URLEncoder.encode(customOidcConfiguration.getCodeChallengeMethod(), UTF_8)
116+
+ "&code_challenge=" + URLEncoder.encode(codeChallenge, UTF_8);
117+
return iDaaSLoginUri;
120118
}
121119

122120
/**
@@ -140,9 +138,8 @@ private static String createCodeVerifier() {
140138
*/
141139
private static String createHash(String codeVerifier) throws NoSuchAlgorithmException {
142140
MessageDigest md = MessageDigest.getInstance(SHA_256);
143-
byte[] digest = md.digest(codeVerifier.getBytes(StandardCharsets.UTF_8));
141+
byte[] digest = md.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII));
144142
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
145143
}
146-
147144
}
148145

src/main/java/com/aliyunidaas/sample/common/cache/CacheManager.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
**/
1010
public interface CacheManager {
1111
/**
12-
* set cache
12+
* 设置缓存
1313
*
1414
* @param cacheKey 缓存key
1515
* @param cacheValue 缓存value
1616
*/
1717
void setCache(String cacheKey, Object cacheValue);
1818

1919
/**
20-
* get cache
20+
* 获取缓存
2121
*
2222
* @param cacheKey 缓存key
2323
* @return value

src/main/java/com/aliyunidaas/sample/common/cache/LocalCacheManager.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
/**
1010
* Copyright (c) Alibaba Cloud Computing
11-
* Description: This project is a demo example. It is recommended to use cache middleware (redis) for storage in the project
11+
* Description: 本示例为demo演示示例,在实际项目中建议使用第三方缓存(如redis)
1212
*
1313
* @date: 2022/6/30 10:11 AM
1414
* @author: longqiuling

src/main/java/com/aliyunidaas/sample/common/config/CustomOidcConfiguration.java

+39-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22

33
import com.aliyunidaas.sample.common.factory.CodeChallengeMethodFactory;
44
import com.aliyunidaas.sample.common.factory.ParameterNameFactory;
5+
import org.apache.commons.lang.StringUtils;
56
import org.springframework.boot.context.properties.ConfigurationProperties;
67
import org.springframework.context.annotation.Configuration;
78

9+
import javax.annotation.PostConstruct;
10+
import java.net.URI;
11+
import java.net.URISyntaxException;
12+
813
/**
914
* Copyright (c) Alibaba Cloud Computing
1015
* Description:
@@ -16,7 +21,7 @@
1621
@ConfigurationProperties("idaas.oidc")
1722
public class CustomOidcConfiguration {
1823

19-
private boolean openPkce = false;
24+
private boolean pkceRequired = false;
2025

2126
private String codeChallengeMethod = CodeChallengeMethodFactory.SHA_256;
2227

@@ -33,12 +38,41 @@ public class CustomOidcConfiguration {
3338
public CustomOidcConfiguration() {
3439
}
3540

36-
public boolean isOpenPkce() {
37-
return openPkce;
41+
@PostConstruct
42+
private void valid() throws Exception {
43+
if (StringUtils.isBlank(clientId)) {
44+
throw new Exception(ParameterNameFactory.CLIENT_ID_IS_NULL);
45+
}
46+
if (StringUtils.isBlank(clientSecret)) {
47+
throw new Exception(ParameterNameFactory.CLIENT_SECRET_IS_NULL);
48+
}
49+
if (StringUtils.isBlank(issuer)) {
50+
throw new Exception(ParameterNameFactory.ISSUER_IS_NULL);
51+
}
52+
if (StringUtils.isBlank(redirectUri)) {
53+
throw new Exception(ParameterNameFactory.REDIRECT_URI_IS_NULL);
54+
}
55+
validRedirectUri();
56+
}
57+
58+
private void validRedirectUri() throws Exception {
59+
URI uri = null;
60+
try {
61+
uri = new URI(redirectUri);
62+
} catch (URISyntaxException e) {
63+
throw new Exception(ParameterNameFactory.REDIRECT_URI_IS_VALID);
64+
}
65+
if (uri.getHost() == null) {
66+
throw new Exception(ParameterNameFactory.REDIRECT_URI_IS_VALID);
67+
}
68+
}
69+
70+
public boolean isPkceRequired() {
71+
return pkceRequired;
3872
}
3973

40-
public void setOpenPkce(boolean openPkce) {
41-
this.openPkce = openPkce;
74+
public void setPkceRequired(boolean pkceRequired) {
75+
this.pkceRequired = pkceRequired;
4276
}
4377

4478
public String getCodeChallengeMethod() {

src/main/java/com/aliyunidaas/sample/common/config/WebAppConfigurer.java

-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,5 @@ SimpleAuthnInterceptor simpleAuthnInterceptor() {
2727
public void addInterceptors(InterceptorRegistry registry) {
2828
registry.addInterceptor(simpleAuthnInterceptor())
2929
.addPathPatterns("/token/**");
30-
3130
}
3231
}

src/main/java/com/aliyunidaas/sample/common/factory/CodeChallengeMethodFactory.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@
66
*
77
* @date: 2022/6/30 7:32 PM
88
* @author: longqiuling
9+
* CodeChallengeMethod 方法,详情查看RFC标准: @see "https://datatracker.ietf.org/doc/html/rfc7636"
910
**/
1011
public class CodeChallengeMethodFactory {
11-
12+
/**
13+
* S256
14+
*/
1215
public static final String SHA_256 = "S256";
13-
16+
/**
17+
* 原文
18+
*/
1419
public static final String PLAIN = "plain";
1520
}

0 commit comments

Comments
 (0)