界面上的字段和一些术语不懂的话可以自己百度。由于https需要证书不方便postman展示,所以使用http
查看openid配置详情
接口:http://localhost:8080/auth/realms/{创建的realm名字}/.well-known/openid-configuration
示例:http://localhost:8080/auth/realms/keycloak-manage-user/.well-known/openid-configuration
{
"issuer": "http://localhost:8080/auth/realms/keycloak-manage-user",
"authorization_endpoint": "http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/auth",
"token_endpoint": "http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/token",
"token_introspection_endpoint": "http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/token/introspect",
"userinfo_endpoint": "http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/userinfo",
"end_session_endpoint": "http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/logout",
"jwks_uri": "http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/certs",
"check_session_iframe": "http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/login-status-iframe.html",
"grant_types_supported": [
"authorization_code",
"implicit",
"refresh_token",
"password",
"client_credentials"
],
"response_types_supported": [
"code",
"none",
"id_token",
"token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"subject_types_supported": [
"public",
"pairwise"
],
"id_token_signing_alg_values_supported": [
"PS384",
"ES384",
"RS384",
"HS256",
"HS512",
"ES256",
"RS256",
"HS384",
"ES512",
"PS256",
"PS512",
"RS512"
],
"id_token_encryption_alg_values_supported": [
"RSA-OAEP",
"RSA1_5"
],
"id_token_encryption_enc_values_supported": [
"A128GCM",
"A128CBC-HS256"
],
"userinfo_signing_alg_values_supported": [
"PS384",
"ES384",
"RS384",
"HS256",
"HS512",
"ES256",
"RS256",
"HS384",
"ES512",
"PS256",
"PS512",
"RS512",
"none"
],
"request_object_signing_alg_values_supported": [
"PS384",
"ES384",
"RS384",
"HS256",
"HS512",
"ES256",
"RS256",
"HS384",
"ES512",
"PS256",
"PS512",
"RS512",
"none"
],
"response_modes_supported": [
"query",
"fragment",
"form_post"
],
"registration_endpoint": "http://localhost:8080/auth/realms/keycloak-manage-user/clients-registrations/openid-connect",
"token_endpoint_auth_methods_supported": [
"private_key_jwt",
"client_secret_basic",
"client_secret_post",
"tls_client_auth",
"client_secret_jwt"
],
"token_endpoint_auth_signing_alg_values_supported": [
"PS384",
"ES384",
"RS384",
"HS256",
"HS512",
"ES256",
"RS256",
"HS384",
"ES512",
"PS256",
"PS512",
"RS512"
],
"claims_supported": [
"aud",
"sub",
"iss",
"auth_time",
"name",
"given_name",
"family_name",
"preferred_username",
"email",
"acr"
],
"claim_types_supported": [
"normal"
],
"claims_parameter_supported": false,
"scopes_supported": [
"openid",
"address",
"email",
"microprofile-jwt",
"offline_access",
"phone",
"profile",
"roles",
"web-origins"
],
"request_parameter_supported": true,
"request_uri_parameter_supported": true,
"code_challenge_methods_supported": [
"plain",
"S256"
],
"tls_client_certificate_bound_access_tokens": true,
"introspection_endpoint": "http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/token/introspect"
}
大家看着理解,不懂的留言,这里面列举了openid支持的配置uma后面讲
6. 获取public client 用户token
“token_endpoint”: “http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/token”
curl --location --request POST 'http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=test' \
--data-urlencode 'password=123456' \
--data-urlencode 'client_id=user-token' \
--data-urlencode 'grant_type=password'
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJjQ3JWRXFCYUNxcG1vcFNTMFpicVozeGsydWdUT2NRQ19NLVV0NkVBWWlzIn0.eyJqdGkiOiJhNzkzMzM3MS0wNzVkLTQ2ZTctYjYzYS05NDQ2NDZhNjk1MDgiLCJleHAiOjE1ODgyMzA2OTksIm5iZiI6MCwiaWF0IjoxNTg4MjMwMzk5LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMva2V5Y2xvYWstbWFuYWdlLXVzZXIiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiZjcxMDgzZTctNGUxYy00NmU2LWJkNjYtMTQ5Yjg3ZDIxNWRhIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidXNlci10b2tlbiIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjUwMzU3OWZmLWZlMDUtNDZmMi04YjQ2LWU3MWE4M2RkNmFkNyIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDo4MDgwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0In0.TICwaP0N00YnSk_RNYZOM9miF_Bx8PZqsR-h91vdGr6lFlxSVSIspLzwRjM1oZ5NDcbEJvD43Nirn5L3aoVzdFoPWU2dEIO8Py_TiONCc1UPmzE9HJ2OudhcVc7tMCPnhm3QKJ1df3QO7icz0ErHrpBdcxjAjt9wLipxjgPF96Mdif1kvLeDmSCz4-adgWk2OHlaiGsjfuoWTliWHS5nY_92c3w2wglrplB-6UF11khR70-wZWYaxT1I85FiD9cJ9UaW2tnehPyNenGvi7n5PTEEraDtYxxgnEn6bXYXeBfor9hPs6rVuMyguBHqRSPexVnbcrdB3caHQKLnVfEF2A",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI3NWNiZjc2ZC0zNzkwLTRiMjYtYTRjMi0wMmQ0YjZkMjlhNmUifQ.eyJqdGkiOiJmMzNlZWE2MC1jMTM4LTQ5OGEtYmM4Yy1mZTE3YTkyMzNiZmMiLCJleHAiOjE1ODgyMzIxOTksIm5iZiI6MCwiaWF0IjoxNTg4MjMwMzk5LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMva2V5Y2xvYWstbWFuYWdlLXVzZXIiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMva2V5Y2xvYWstbWFuYWdlLXVzZXIiLCJzdWIiOiJmNzEwODNlNy00ZTFjLTQ2ZTYtYmQ2Ni0xNDliODdkMjE1ZGEiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoidXNlci10b2tlbiIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjUwMzU3OWZmLWZlMDUtNDZmMi04YjQ2LWU3MWE4M2RkNmFkNyIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIn0.FwDKqA3l-ZA3i8WBIur8lQ6LRkw7HHZNWgzgj8Wbs5Y",
"token_type": "bearer",
"not-before-policy": 0,
"session_state": "503579ff-fe05-46f2-8b46-e71a83dd6ad7",
"scope": "email profile"
}
curl --location --request POST 'http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/userinfo' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'access_token=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJjQ3JWRXFCYUNxcG1vcFNTMFpicVozeGsydWdUT2NRQ19NLVV0NkVBWWlzIn0.eyJqdGkiOiI3MjUwNjU2Zi1jNGY0LTQ2NWUtYjFmNC02MmU5Yzk2ODA0NzYiLCJleHAiOjE1ODgyMzExODcsIm5iZiI6MCwiaWF0IjoxNTg4MjMwODg3LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMva2V5Y2xvYWstbWFuYWdlLXVzZXIiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiZjcxMDgzZTctNGUxYy00NmU2LWJkNjYtMTQ5Yjg3ZDIxNWRhIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidXNlci10b2tlbiIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjhmNzJmMjU3LTg4ZTAtNDE5Ni1hZDllLTIyOGJjZDE1ZTU2NCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDo4MDgwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0In0.ZSfY_SouVSxcLsloQl8h1KEzH1TnrJvMT3bkcK0X116kq6M3Oa43tiUm4NYOxSDqg5L9nwbVaVaFrE7_n1M6V392sl1zhZljmKldsSHzUGzuk5gIk50LckDNC160YBpFgv8yBb_E_NTWcymp71q90IuZ36QgnbgDpxl5r_RrKjcuZMrzje5LGhVSI4FXyS3Ed--pP7GHSG8Rd1vAhvL4ZEsTNq5brwKaREmiNV0w959E78nB4SUWAdY0H8H2J7umaFUUMpFh4MgIdYl6iZTKIqXymxrnEhLcVWEqVDYHf1L097fqijW7QBxFjMihAca4QWxF2zoKguDJq5x5paQQWw'
8. confidential client 设置
public confidential 区别:
public 就是简单的账号密码登录
confidential 需要算法支持
从配置中可以看到支持5种:
"private_key_jwt",
"client_secret_basic",
"client_secret_post",
"tls_client_auth",
"client_secret_jwt"
curl --location --request POST 'http://localhost:8080/auth/realms/keycloak-manage-user/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=user-token' \
--data-urlencode 'client_secret=40bf9908-94e1-4571-8ad3-10940e21ee9f' \
--data-urlencode 'username=test' \
--data-urlencode 'password=123456'
注意grant_type=client_credentials参数的修改
10. client 认证 "client_secret_post " 获取client token
这里新建了realm和client方便区分
curl --location --request POST 'http://localhost:8080/auth/realms/openbanking/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client1-mtls-PS256-PS256' \
--data-urlencode 'client_secret=63795b52-65ec-468c-a7b9-4b73817bdfc9' \
--data-urlencode 'grant_type=client_credentials'
curl --location --request POST 'http://localhost:8080/auth/realms/openbanking/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic Y2xpZW50MS1tdGxzLVBTMjU2LVBTMjU2OjYzNzk1YjUyLTY1ZWMtNDY4Yy1hN2I5LTRiNzM4MTdiZGZjOQ==' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=client1-mtls-PS256-PS256' \
--data-urlencode 'scope=accounts'
Base64.getEncoder().encodeToString((id + “:” + secret).getBytes());
13. client 认证 “private_key_jwt” 获取client token
(1) 使用java代码生成公钥私钥
参考:https://connect2id.com/products/nimbus-jose-jwt/examples/jwt-with-rsa-signature
com.nimbusds
nimbus-jose-jwt
${nimbus-version}
org.bouncycastle
bcprov-jdk15on
${bcprov-version}
commons-codec
commons-codec
1.14
7.0
2.8.4
1.60
RSAKey privateKey = new RSAKeyGenerator(2048)
.algorithm(JWSAlgorithm.PS256)
.keyUse(KeyUse.SIGNATURE)
.keyID("client-PS256")
.generate();
RSAKey publicKey = privateKey.toPublicJWK();
生成的私钥
{
"keys": [
{
"p": "77_7i2uGN6RxaCU6m217guw2gdLeknEbi34IeJpvK5xqufrs00aMCqnCXwWAEsqCm3CmITdhTPNZSemETcsouW82NiHQ_cbI7agNpAaTU6kRdzaOVyJSJgs-pmxgFMbgkBmIHZbbtdrpIykTHlhmilPnRhQvwN3N_7eymmbXPe0",
"kty": "RSA",
"q": "nbVbCk7BMP1syPPR0IUbDwYmLkFnUL6TXgXA4o-3IIdP-bFJKc2ONTxtsLl-Q-H2dDKugjHe-AyVvVRmiMUna2VVEUbG3I6z0mvok19fC9Gd7kCalJuXfNBnJVyoJfCfOmDn3RepYUP7mWUAmlbtMxbud0-F90T60vIgewPN7e0",
"d": "COuCJEsUHUcg39kF9S9cTt8GsaxIEP-vqI9GonGz_nC3uBFDRhyqGKj-JWWoG3Vso3i658bnJdMf1aW4KjxJon-D0nfAAZXPUj7OfJZ__D874BKRSg9NqE7uubsU3ByPvLVexqsGAy4tRCM14zwXpNM_YAe0z7c4tmSrDmUdFZUpt8YhL33jPi5KDaxtEdMrbL2OKIdrolJjJnX7loVJcSneM7bXvx-6ieDfO8HgMlmnpik3t_9HVeJ1-o30ndv5KsVG1i7NtTDgAGTqbInHD7WdRLIzgTbXTmoAWY4ccHeDNzh6S_9jKk_sQE9A1oLFUmL-7qtk8qnl9ZBDoxGIYQ",
"e": "AQAB",
"use": "sig",
"kid": "client-PS256",
"qi": "AiADA77A6JGn1iOEd2CM0c17LK6AzpGkVUzMfI_yoIzwpaJh41zd77nO_xe49e0FQNEEIjh__C4svctz7RA_90pteFDQ8TBoSmm8j6Jb-BdaDVIt_Ax77RRAiPD5ZSSqj-XzkjIUCNjghWjDz6H_k1FbcBA-lTBaia1tvioWh7A",
"dp": "vue3TBAlgr8Nkqk6XrMyC1E-IeggVKl-DnggFLCcXzShA1CcLavaLU95t6IwlkXs9AsiLgbkEpsfeSxZrnxcBDRbDYWl3b3hFuSfYAHgZFiW0L9_XkC0-xgvHePkKgcmn3fFHBKZBti2lcnKMHqhw_oFiZbfY4r60mma7TmAoQ0",
"alg": "PS256",
"dq": "O75nNblt8Fwg6OOM2VyDSqa-sgku1WTMuPKfBnUBH76C6olhuQdY1wwEVc1_asHgNla4yzOPTxKdazLdAPUHIOUrW7cfQJCCyLT-T03y2KxZEtfAd4mV0r-0Q3Addvn3qArr61K6ZNF3L74Wg2FozFDkl6g1jN3B00XMTi27xmU",
"n": "k7KVREAwJPKOfJC9Q71ZA3MZyZpv2S0wRDtz4wurqu2kNNd33-pIAkUABnSFDWxHdrXF0lL6fBJ558f5ybLk4rXE4dT0UJQLYxjBONoilkOIbZhXvtd-GWRy6bzmuRMBgUzvULTjCXSUpyllche6uStZFMRrlrRaZYGU1K0MNGmC6VzbKyYU8SJg9T9OupdtVSA0zmRtMeI7tk8-vQceonStkNrBklJ_pbqtFh-UpqRlZABJ-UqTNRY3Cm2x8X_QaEzGA-3eeRk4uNLTPVdgOOKven0lZdePRtdCowfXRxVwGrFGINonZn--gqlVc6rqo2KRIJgSTWXndIsb12G9aQ"
}
]
}
公钥
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "client-PS256",
"alg": "PS256",
"n": "k7KVREAwJPKOfJC9Q71ZA3MZyZpv2S0wRDtz4wurqu2kNNd33-pIAkUABnSFDWxHdrXF0lL6fBJ558f5ybLk4rXE4dT0UJQLYxjBONoilkOIbZhXvtd-GWRy6bzmuRMBgUzvULTjCXSUpyllche6uStZFMRrlrRaZYGU1K0MNGmC6VzbKyYU8SJg9T9OupdtVSA0zmRtMeI7tk8-vQceonStkNrBklJ_pbqtFh-UpqRlZABJ-UqTNRY3Cm2x8X_QaEzGA-3eeRk4uNLTPVdgOOKven0lZdePRtdCowfXRxVwGrFGINonZn--gqlVc6rqo2KRIJgSTWXndIsb12G9aQ"
}
]
}
公钥导入到kecloak中 私钥进行加密
私钥加密过程
参考:https://tools.ietf.org/html/rfc7523
Provider bc = BouncyCastleProviderSingleton.getInstance();
Security.addProvider(bc);
// iss:client1-mtls-PS256-PS256
// sub:client1-mtls-PS256-PS256
// aud:http://192.168.11.63:8080/auth/realms/openbanking/protocol/openid-connect/token
// jti:bMLxAT7Kw0xhDN2ughzy
// iat:Long.toString(new Date(System.currentTimeMillis()).getTime()
// exp:Long.toString(new Date(System.currentTimeMillis() + 30 * 60 * 1000).getTime()
BufferedReader reader = new BufferedReader(new InputStreamReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("ps256jwt.json"), "UTF-8"));
StringBuilder sb = new StringBuilder();
String s; // 依次循环,至到读的值为空
while ((s = reader.readLine()) != null) {
sb.append(s);
}
reader.close();
String jwks = sb.toString();
JsonObject claims = new JsonObject();
claims.addProperty("iss", "client-PS256");
claims.addProperty("sub", "client-PS256");
claims.addProperty("aud", "http://localhost:8080/auth/realms/openbanking/protocol/openid-connect/token");
claims.addProperty("jti", RandomStringUtils.randomAlphanumeric(20));
Instant iat = Instant.now();
Instant exp = iat.plusSeconds(60);
claims.addProperty("iat", iat.getEpochSecond());
claims.addProperty("exp", exp.getEpochSecond());
JWTClaimsSet claimSet = JWTClaimsSet.parse(claims.toString());
JWKSet jwkSet = JWKSet.parse(jwks.toString());
SignedJWT assertion = null;
if (jwkSet.getKeys().size() == 1) {
// figure out which algorithm to use
JWK jwk = jwkSet.getKeys().iterator().next();
JWSSigner signer = null;
if (jwk.getKeyType().equals(KeyType.RSA)) {
signer = new RSASSASigner((RSAKey) jwk);
} else if (jwk.getKeyType().equals(KeyType.EC)) {
signer = new ECDSASigner((ECKey) jwk);
} else if (jwk.getKeyType().equals(KeyType.OCT)) {
signer = new MACSigner((OctetSequenceKey) jwk);
}
Algorithm alg = jwk.getAlgorithm();
JWSHeader header = new JWSHeader(JWSAlgorithm.parse(alg.getName()), null, null, null, null, null, null, null, null, null, jwk.getKeyID(), null, null);
assertion = new SignedJWT(header, claimSet);
assertion.sign(signer);
}
System.out.println(assertion.serialize());
}
client_assertion
eyJraWQiOiJjbGllbnQtUFMyNTYiLCJhbGciOiJQUzI1NiJ9.eyJzdWIiOiJjbGllbnQtUFMyNTYiLCJhdWQiOiJodHRwOlwvXC9sb2NhbGhvc3Q6ODA4MFwvYXV0aFwvcmVhbG1zXC9vcGVuYmFua2luZ1wvcHJvdG9jb2xcL29wZW5pZC1jb25uZWN0XC90b2tlbiIsImlzcyI6ImNsaWVudC1QUzI1NiIsImV4cCI6MTU5MDcxODAwOSwiaWF0IjoxNTkwNzE3OTQ5LCJqdGkiOiJtUk9lU2p6V2tHU0FCaFRwU3V0dCJ9.anYhhS5CGUXLmQN7VJkVmVqajVLKO62t48yLlp0WPYMRn9rQp7-RpTTVYRctHcLSN5INGbIjO8jJjAl5M3jpE5vGcOa-bzHWJiTGxxwUevwRVru9oCaVdd9ESBH9TOMuiaQyzHCZI5m8uWg_3TSzXxk9p3yAh8SswTEiOuSPGW7EvRZ8T_Q_z_hnJrUqGqYYodMxook1YtZ1TsX58UexbKKXpTLZpTPGaYWkY-DJoQ5_B8vupmcydSgVMH30Z-xpRcHL497l2R3zLBVi5wC1e7zIbVUcKjMaKC7-JpFafeTNFXAorfQ8TD0jYCCTJLZA34ntulRoJ8ptvZs3CGiaPA
curl命令
curl --location --request POST 'http://localhost:8080/auth/realms/openbanking/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client-PS256' \
--data-urlencode 'client_secret=9563616a-31d2-45ce-94a2-c0109963a134' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode 'client_assertion=eyJraWQiOiJjbGllbnQtUFMyNTYiLCJhbGciOiJQUzI1NiJ9.eyJzdWIiOiJjbGllbnQtUFMyNTYiLCJhdWQiOiJodHRwOlwvXC9sb2NhbGhvc3Q6ODA4MFwvYXV0aFwvcmVhbG1zXC9vcGVuYmFua2luZ1wvcHJvdG9jb2xcL29wZW5pZC1jb25uZWN0XC90b2tlbiIsImlzcyI6ImNsaWVudC1QUzI1NiIsImV4cCI6MTU5MDcxODAwOSwiaWF0IjoxNTkwNzE3OTQ5LCJqdGkiOiJtUk9lU2p6V2tHU0FCaFRwU3V0dCJ9.anYhhS5CGUXLmQN7VJkVmVqajVLKO62t48yLlp0WPYMRn9rQp7-RpTTVYRctHcLSN5INGbIjO8jJjAl5M3jpE5vGcOa-bzHWJiTGxxwUevwRVru9oCaVdd9ESBH9TOMuiaQyzHCZI5m8uWg_3TSzXxk9p3yAh8SswTEiOuSPGW7EvRZ8T_Q_z_hnJrUqGqYYodMxook1YtZ1TsX58UexbKKXpTLZpTPGaYWkY-DJoQ5_B8vupmcydSgVMH30Z-xpRcHL497l2R3zLBVi5wC1e7zIbVUcKjMaKC7-JpFafeTNFXAorfQ8TD0jYCCTJLZA34ntulRoJ8ptvZs3CGiaPA'
返回结果
{
"access_token": "eyJhbGciOiJQUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxaGdobFlJUURabU13cVpHbS1HSlFmUGJfU3NnWmk5UWV2Y0tYWFFTUTRVIn0.eyJqdGkiOiJiNDk2OTI4Yy0zZDZmLTQwZDUtYTcwOS00NTZhYjEwNzhkYzciLCJleHAiOjE1OTA3MTgyODEsIm5iZiI6MCwiaWF0IjoxNTkwNzE3OTgxLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvb3BlbmJhbmtpbmciLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiY2M5NTA5M2ItZDM2YS00ZWQ5LWFhZDQtZTVkMDAxZGY3NmRkIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiY2xpZW50LVBTMjU2IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZjA4YmZmMGEtNzhkOC00MTI3LWJiYmMtNGRhZGZhNTRjZTcxIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjgwODAiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJjbGllbnQtUFMyNTYiOnsicm9sZXMiOlsidW1hX3Byb3RlY3Rpb24iXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImNsaWVudElkIjoiY2xpZW50LVBTMjU2IiwiY2xpZW50SG9zdCI6IjEyNy4wLjAuMSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoic2VydmljZS1hY2NvdW50LWNsaWVudC1wczI1NiIsImNsaWVudEFkZHJlc3MiOiIxMjcuMC4wLjEiLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC1jbGllbnQtcHMyNTZAcGxhY2Vob2xkZXIub3JnIn0.k4tENDhWlOTUgJmHO7WHKBNc34KgqW2Cpuo04aeIJXDePTZr4FaC5HlcenfBUQpXwkEnaAuYVA08wIcUAVXRfpUbhRBZmZGNGcclfz3BqTQ8XH5lAiIslVHqJtDDExcADbUXjgv2PASlPSCI7LXy5vLNqO2AIvG-8mFXEx7r5NbIcu12AiZMwXPQYYlWq4iWAx6Zm_LyFoLjKAEvCO5gKNpXAeCDM-UkalGA9JqmFcjRimW_k8p8JGvLpUZiTxYozlTSFcViBm2RkCR2jQjzIgw2o1C2nNwqsp11fHiKCGr9cCRtD_9V33MePv3X8vxPMG3tv0npAY3DdNR_yTDJNw",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIwOGYxYmUyMC04ODU0LTRlYzQtYTllZi1jYTkyOTYzZTY4YmIifQ.eyJqdGkiOiI4YjQ0OWMzYS0yNzM3LTQxNTQtOGRjZi01YThlN2NmMGM3NzkiLCJleHAiOjE1OTA3MTk3ODEsIm5iZiI6MCwiaWF0IjoxNTkwNzE3OTgxLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvb3BlbmJhbmtpbmciLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvb3BlbmJhbmtpbmciLCJzdWIiOiJjYzk1MDkzYi1kMzZhLTRlZDktYWFkNC1lNWQwMDFkZjc2ZGQiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoiY2xpZW50LVBTMjU2IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZjA4YmZmMGEtNzhkOC00MTI3LWJiYmMtNGRhZGZhNTRjZTcxIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJjbGllbnQtUFMyNTYiOnsicm9sZXMiOlsidW1hX3Byb3RlY3Rpb24iXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSJ9.kBZtPPTLY1grDosKUSywxs8TIjm7ghq9GJp5KKJITBM",
"token_type": "bearer",
"not-before-policy": 0,
"session_state": "f08bff0a-78d8-4127-bbbc-4dadfa54ce71",
"scope": "email profile"
}
(2)使用工具生成公钥私钥
https://github.com/hkj123/jwk-keygen.git
curl -s -L https://github.com/openstandia/jwk-keygen/releases/download/v0.1/jwk-keygen-v0.1-linux-amd64.tar.gz | tar zx -C .bin
mkdir -p .bin
curl -s -L https://github.com/openstandia/jwk-keygen/releases/download/v0.1/jwk-keygen-v0.1-linux-amd64.tar.gz | tar zx -C .bin
PATH=$PATH:.bin
jwk-keygen --use=sig --format --jwks --pem --pem-body --alg=PS256 --kid=client1-PS256
jwk-keygen --use=sig --format --jwks --pem --pem-body --alg=PS256 --kid=client2-PS256
执行完毕会生成
导入client 私钥
获取keycloak公钥
curl --location --request GET 'http://localhost:8080/auth/realms/openbanking/protocol/openid-connect/certs'
org.bouncycastle
bcprov-jdk15on
1.60
commons-codec
commons-codec
1.14
org.apache.commons
commons-text
1.8
public static void main(String[] args) throws Exception{
Provider bc = BouncyCastleProviderSingleton.getInstance();
Security.addProvider(bc);
String pdpUrl = "https://qms-obp-pdp-test.qloudobp.service.sd";
String realmName = "openbanking";
String client_id = "client1-mtls-RS256-RS256";
String redirect_uri = "http://192.168.11.253:8888/v3/hello";
String scope = "openid%20accounts";
String state = "WBMDzUQtKO";
String nonce = "axo5pyKHfk";
String response_type = "code id_token";
// https://tools.ietf.org/html/rfc7636#section-4.1
char [][] pairs = {
{'A','Z'},
{'a','z'},
{'0','9'},
{'-','-'},
{'.','.'},
{'_','_'},
{'~','~'},
};
final SecureRandom rand = new SecureRandom();
RandomStringGenerator generator = new RandomStringGenerator.Builder()
.usingRandom(rand::nextInt)
.withinRange(pairs).build();
// test maximum permitted length
String code_verifier = generator.generate(128);
byte[] bytes = code_verifier.getBytes(StandardCharsets.US_ASCII);
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(bytes, 0, bytes.length);
byte[] digest = md.digest();
String code_challenge = org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(digest);
String code_challenge_method = "S256";
StringBuffer bufferAud = new StringBuffer();
bufferAud.append(pdpUrl);
bufferAud.append("/auth/realms/");
bufferAud.append(realmName);
bufferAud.append("/protocol/openid-connect/auth");
bufferAud.append("?client_id="+client_id);
bufferAud.append("&redirect_uri="+redirect_uri);
bufferAud.append("&scope="+scope);
bufferAud.append("&state="+state);
bufferAud.append("&nonce="+nonce);
bufferAud.append("&response_type="+response_type);
bufferAud.append("&code_challenge="+code_challenge);
bufferAud.append("&code_challenge_method="+code_challenge_method);
String path = bufferAud.toString();
System.out.println(path);
}
https://qms-obp-pdp-test.qloudobp.service.sd/auth/realms/openbanking/protocol/openid-connect/auth?client_id=client1-mtls-RS256-RS256&redirect_uri=http://192.168.11.253:8888/v3/hello&scope=openid%20accounts&state=WBMDzUQtKO&nonce=axo5pyKHfk&response_type=code