Complete the development of the jap-ids module

This commit is contained in:
yadong.zhang 2021-04-17 19:45:31 +08:00
parent 4c072bd060
commit c577b67266
79 changed files with 7460 additions and 33 deletions

2
jap-ids/README.md Normal file
View File

@ -0,0 +1,2 @@
## JAP IDS

View File

@ -5,14 +5,22 @@
<parent> <parent>
<groupId>com.fujieid</groupId> <groupId>com.fujieid</groupId>
<artifactId>jap</artifactId> <artifactId>jap</artifactId>
<version>1.0.1-alpha.1</version> <version>1.0.1</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jap-ids</artifactId> <artifactId>jap-ids</artifactId>
<name>jap-ids</name> <name>jap-ids</name>
<description> <description>
Authorization service based on rfc6749(https://tools.ietf.org/html/rfc6749) protocol specification Authorization service based on RFC6749(https://tools.ietf.org/html/rfc6749) protocol specification
and OpenID Connect Core 1.0(https://openid.net/specs/openid-connect-core-1_0.html) specification and OpenID Connect Core 1.0(https://openid.net/specs/openid-connect-core-1_0.html) specification
</description> </description>
<dependencies>
<dependency>
<groupId>com.fujieid</groupId>
<artifactId>jap-oidc</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project> </project>

View File

@ -15,40 +15,57 @@
*/ */
package com.fujieid.jap.ids; package com.fujieid.jap.ids;
import com.fujieid.jap.ids.config.IdsConfig;
import com.fujieid.jap.ids.context.IdsContext;
import com.fujieid.jap.ids.exception.IdsException;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.UserInfo;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
/** /**
* Authorization service based on rfc6749 protocol specification and OpenID Connect Core 1.0 specification * Authorization service based on RFC6749 protocol specification and OpenID Connect Core 1.0 specification
* <p>
* Features
* <p>
* 1. Authorization Code Grant
* <p>
* 2. Implicit Grant
* <p>
* 3. Resource Owner Password Credentials Grant
* <p>
* 4. Refresh Token
* <p>
* 5. Check Token
* <p>
* 6. Proof Key for Code Exchange by OAuth Public Clients
* <p>
* 7. OpenID Connect Discovery
* <p>
* 8. OpenID Connect Front-Channel Logout
* <p>
* 9. OpenID Connect Back-Channel Logout
* <p>
* 10. ...
* *
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0 * @version 1.0.0
* @see <a href="https://tools.ietf.org/html/rfc6749" target="_blank"> The OAuth 2.0 Authorization Framework</a>
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html" target="_blank">OpenID Connect Core 1.0 incorporating errata set 1</a>
* @see <a href="https://tools.ietf.org/html/rfc7636" target="_blank">Proof Key for Code Exchange by OAuth Public Clients</a>
* @see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html" target="_blank">OpenID Connect Discovery 1.0 incorporating errata set 1</a>
* @see <a href="https://openid.net/specs/openid-connect-frontchannel-1_0.html" target="_blank">OpenID Connect Front-Channel Logout 1.0</a>
* @see <a href="https://openid.net/specs/openid-connect-backchannel-1_0.html" target="_blank">OpenID Connect Back-Channel Logout 1.0</a>
* @since 1.0.0 * @since 1.0.0
*/ */
public class JapIds { public class JapIds implements Serializable {
private static IdsContext context;
private JapIds() {
}
public static void registerContext(IdsContext idsContext) {
context = idsContext;
}
public static IdsContext getContext() {
if (null == context) {
throw new IdsException("Unregistered ids context.Please use `JapIds.registerContext(IdsContext)` to register ids context.");
}
return context;
}
public static boolean isAuthenticated(HttpServletRequest request) {
return null != getUserInfo(request);
}
public static void saveUserInfo(UserInfo userInfo, HttpServletRequest request) {
request.getSession().setAttribute(IdsConsts.OAUTH_USERINFO_CACHE_KEY, userInfo);
}
public static UserInfo getUserInfo(HttpServletRequest request) {
return (UserInfo) request.getSession().getAttribute(IdsConsts.OAUTH_USERINFO_CACHE_KEY);
}
public static void removeUserInfo(HttpServletRequest request) {
request.getSession().removeAttribute(IdsConsts.OAUTH_USERINFO_CACHE_KEY);
}
public static IdsConfig getIdsConfig() {
IdsContext context = getContext();
return context.getIdsConfig();
}
} }

View File

@ -0,0 +1,278 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.config;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.enums.ClientSecretAuthMethod;
import com.fujieid.jap.ids.model.enums.TokenAuthMethod;
import com.fujieid.jap.ids.util.ObjectUtils;
import java.util.Collections;
import java.util.List;
/**
* ids general configuration
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsConfig {
/**
* Get the user name from request through {@code request.getParameter(`usernameField`)}, which defaults to "username"
*/
private String usernameField = "username";
/**
* Get the password from request through {@code request.getParameter(`passwordField`)}, which defaults to "password"
*/
private String passwordField = "password";
/**
* Identity provider
*/
private String issuer;
/**
* Login url, the default is {@code issuer + /oauth/login}
*/
private String loginUrl;
/**
* error url
*/
private String errorUrl;
/**
* The user confirms the authorized url, the default is {@code issuer + /oauth/confirm}
*/
private String confirmUrl;
/**
* Authorized url, the default is {@code issuer + /oauth/authorize}
*/
private String authorizeUrl;
/**
* token url, the default is {@code issuer + /oauth/token}
*/
private String tokenUrl;
/**
* userinfo url, the default is {@code issuer + /oauth/userinfo}
*/
private String userinfoUrl;
/**
* Register the the client detail, the default is {@code issuer + /oauth/registration}
*/
private String registrationUrl;
/**
* logout url, the default is {@code issuer + /oauth/logout}
*/
private String endSessionUrl;
/**
* check session url, the default is {@code issuer + /oauth/check_session}
*/
private String checkSessionUrl;
/**
* After logout, redirect to {@code logoutRedirectUrl}. Default is `/`
*/
private String logoutRedirectUrl = "/";
/**
* public key url, the default is {@code issuer + /.well-known/jwks.json}
*/
private String jwksUrl;
/**
* Get open id provider metadata, the default is {@code issuer + /.well-known/openid-configuration}
*/
private String discoveryUrl;
/**
* When requesting api, the way to pass token
*/
private List<TokenAuthMethod> tokenAuthMethods = Collections.singletonList(TokenAuthMethod.ALL);
/**
* When requesting the token endpoint, the way to pass the client secret
*/
private List<ClientSecretAuthMethod> clientSecretAuthMethods = Collections.singletonList(ClientSecretAuthMethod.ALL);
/**
* Generate/verify the global configuration of jwt token.
* If the caller needs to configure a set of jwt config for each client,
* you can specify jwt config when obtaining the token.
*/
private JwtConfig jwtConfig = new JwtConfig();
public IdsConfig(String issuer) {
this.issuer = issuer;
}
public IdsConfig() {
}
public String getUsernameField() {
return usernameField;
}
public IdsConfig setUsernameField(String usernameField) {
this.usernameField = usernameField;
return this;
}
public String getPasswordField() {
return passwordField;
}
public IdsConfig setPasswordField(String passwordField) {
this.passwordField = passwordField;
return this;
}
public String getIssuer() {
return issuer;
}
public IdsConfig setIssuer(String issuer) {
this.issuer = issuer;
return this;
}
public String getLoginUrl() {
return null == loginUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + "oauth/login" : loginUrl;
}
public IdsConfig setLoginUrl(String loginUrl) {
this.loginUrl = loginUrl;
return this;
}
public String getErrorUrl() {
return errorUrl;
}
public IdsConfig setErrorUrl(String errorUrl) {
this.errorUrl = errorUrl;
return this;
}
public String getConfirmUrl() {
return null == confirmUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + "oauth/confirm" : confirmUrl;
}
public IdsConfig setConfirmUrl(String confirmUrl) {
this.confirmUrl = confirmUrl;
return this;
}
public String getAuthorizeUrl() {
return null == authorizeUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + "oauth/authorize" : authorizeUrl;
}
public IdsConfig setAuthorizeUrl(String authorizeUrl) {
this.authorizeUrl = authorizeUrl;
return this;
}
public String getTokenUrl() {
return null == tokenUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + "oauth/token" : tokenUrl;
}
public IdsConfig setTokenUrl(String tokenUrl) {
this.tokenUrl = tokenUrl;
return this;
}
public String getUserinfoUrl() {
return null == userinfoUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + "oauth/userinfo" : userinfoUrl;
}
public IdsConfig setUserinfoUrl(String userinfoUrl) {
this.userinfoUrl = userinfoUrl;
return this;
}
public String getRegistrationUrl() {
return null == registrationUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + "oauth/registration" : registrationUrl;
}
public IdsConfig setRegistrationUrl(String registrationUrl) {
this.registrationUrl = registrationUrl;
return this;
}
public String getEndSessionUrl() {
return null == endSessionUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + "oauth/logout" : endSessionUrl;
}
public IdsConfig setEndSessionUrl(String endSessionUrl) {
this.endSessionUrl = endSessionUrl;
return this;
}
public String getCheckSessionUrl() {
return null == checkSessionUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + "oauth/check_session" : checkSessionUrl;
}
public IdsConfig setCheckSessionUrl(String checkSessionUrl) {
this.checkSessionUrl = checkSessionUrl;
return this;
}
public String getLogoutRedirectUrl() {
return logoutRedirectUrl;
}
public IdsConfig setLogoutRedirectUrl(String logoutRedirectUrl) {
this.logoutRedirectUrl = logoutRedirectUrl;
return this;
}
public String getJwksUrl() {
return null == jwksUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + ".well-known/jwks.json" : jwksUrl;
}
public IdsConfig setJwksUrl(String jwksUrl) {
this.jwksUrl = jwksUrl;
return this;
}
public String getDiscoveryUrl() {
return null == discoveryUrl ? ObjectUtils.appendIfNotEndWith(issuer, IdsConsts.SLASH) + ".well-known/openid-configuration" : discoveryUrl;
}
public IdsConfig setDiscoveryUrl(String discoveryUrl) {
this.discoveryUrl = discoveryUrl;
return this;
}
public JwtConfig getJwtConfig() {
return null == jwtConfig ? new JwtConfig() : jwtConfig;
}
public IdsConfig setJwtConfig(JwtConfig jwtConfig) {
this.jwtConfig = jwtConfig;
return this;
}
public List<TokenAuthMethod> getTokenAuthMethods() {
return tokenAuthMethods;
}
public IdsConfig setTokenAuthMethods(List<TokenAuthMethod> tokenAuthMethods) {
this.tokenAuthMethods = tokenAuthMethods;
return this;
}
public List<ClientSecretAuthMethod> getClientSecretAuthMethods() {
return clientSecretAuthMethods;
}
public IdsConfig setClientSecretAuthMethods(List<ClientSecretAuthMethod> clientSecretAuthMethods) {
this.clientSecretAuthMethods = clientSecretAuthMethods;
return this;
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.config;
import com.fujieid.jap.ids.model.enums.JwtVerificationType;
import com.fujieid.jap.ids.model.enums.TokenSigningAlg;
/**
* Generate/verify the global configuration of jwt token.
* If the caller needs to configure a set of jwt config for each client,
* you can specify jwt config when obtaining the token.
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class JwtConfig {
/**
* rsa encryption key id
*/
private String jwksKeyId = "jap-jwk-keyid";
/**
* <strong>Optional</strong>, jwt token verification type, the default is {@code JwtVerificationType.HTTPS_JWKS_ENDPOINT}
* <p>
* The usage is as follows:
* <p>
* 1. If the public key of the jwt issuer is at the https jwks endpoint, please set it to {@code JwtVerificationType.HTTPS_JWKS_ENDPOINT}
* <p>
* 2. When using jwks certificate string verification, please set it to {@code JwtVerificationType.JWKS}
* <p>
* 3. When using x.509 certificate string verification, please set it to {@code JwtVerificationType.X_509}
*/
private JwtVerificationType jwtVerificationType = JwtVerificationType.HTTPS_JWKS_ENDPOINT;
/**
* <strong>Optional</strong>, when {@link JwtConfig#jwtVerificationType} is equal to {@code JWKS}, this attribute is <strong>required</strong>
*/
private String jwksJson;
/**
* jwt token encryption algorithm, the default is {@code RS256}
*/
private TokenSigningAlg tokenSigningAlg = TokenSigningAlg.RS256;
public JwtVerificationType getJwtVerificationType() {
return jwtVerificationType;
}
public JwtConfig setJwtVerificationType(JwtVerificationType jwtVerificationType) {
this.jwtVerificationType = jwtVerificationType;
return this;
}
public String getJwksKeyId() {
return jwksKeyId;
}
public JwtConfig setJwksKeyId(String jwksKeyId) {
this.jwksKeyId = jwksKeyId;
return this;
}
public String getJwksJson() {
return jwksJson;
}
public JwtConfig setJwksJson(String jwksJson) {
this.jwksJson = jwksJson;
return this;
}
public TokenSigningAlg getTokenSigningAlg() {
return tokenSigningAlg;
}
public JwtConfig setTokenSigningAlg(TokenSigningAlg tokenSigningAlg) {
this.tokenSigningAlg = tokenSigningAlg;
return this;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* IDS config
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.config;

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.context;
import com.fujieid.jap.core.cache.JapCache;
import com.fujieid.jap.core.cache.JapLocalCache;
import com.fujieid.jap.ids.config.IdsConfig;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.service.IdsClientDetailService;
import com.fujieid.jap.ids.service.IdsIdentityService;
import com.fujieid.jap.ids.service.IdsUserService;
import java.io.Serializable;
/**
* ids context
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsContext implements Serializable {
private JapCache cache = new JapLocalCache();
private IdsClientDetailService clientDetailService;
private IdsUserService userService;
private IdsIdentityService identityService;
private IdsConfig idsConfig;
public JapCache getCache() {
return cache == null ? new JapLocalCache() : cache;
}
public IdsContext setCache(JapCache cache) {
this.cache = cache;
return this;
}
public IdsClientDetailService getClientDetailService() {
return clientDetailService;
}
public IdsContext setClientDetailService(IdsClientDetailService clientDetailService) {
this.clientDetailService = clientDetailService;
return this;
}
public IdsUserService getUserService() {
return userService;
}
public IdsContext setUserService(IdsUserService userService) {
this.userService = userService;
return this;
}
public IdsConfig getIdsConfig() {
return idsConfig;
}
public IdsContext setIdsConfig(IdsConfig idsConfig) {
this.idsConfig = idsConfig;
return this;
}
public IdsIdentityService getIdentityService() {
return identityService;
}
public IdsContext setIdentityService(IdsIdentityService identityService) {
this.identityService = identityService;
return this;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* IDS context
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.context;

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.endpoint;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.config.IdsConfig;
import com.fujieid.jap.ids.service.Oauth2Service;
import com.fujieid.jap.ids.service.Oauth2ServiceImpl;
/**
* Abstract classes common to various endpoints
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public abstract class AbstractEndpoint {
protected final Oauth2Service oauth2Service;
protected IdsConfig idsConfig;
public AbstractEndpoint() {
this.idsConfig = JapIds.getIdsConfig();
this.oauth2Service = new Oauth2ServiceImpl();
}
}

View File

@ -0,0 +1,183 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.endpoint;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.InvalidClientException;
import com.fujieid.jap.ids.model.ClientDetail;
import com.fujieid.jap.ids.model.IdsRequestParam;
import com.fujieid.jap.ids.model.IdsResponse;
import com.fujieid.jap.ids.model.IdsScope;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.provider.IdsRequestParamProvider;
import com.fujieid.jap.ids.provider.IdsScopeProvider;
import com.fujieid.jap.ids.util.OauthUtil;
import com.fujieid.jap.ids.util.ObjectUtils;
import com.xkcoding.json.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
/**
* Confirm authorization endpoint
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class ApprovalEndpoint extends AbstractEndpoint {
/**
* The default authorization confirmation page pops up
*
* @param request Current request
* @param response Current response
* @throws IOException IOException
*/
public void confirm(HttpServletRequest request, HttpServletResponse response) throws IOException {
final String approvalContent = createConfirmPageHtml(request);
response.setContentType("text/html;charset=UTF-8");
response.getWriter().append(approvalContent);
}
/**
* Obtain authorization information when you jump to the authorization confirmation page after successful login
*
* @param request HttpServletRequest
* @return IdsResponse
*/
public IdsResponse<String, Object> getAuthClientInfo(HttpServletRequest request) {
IdsRequestParam param = IdsRequestParamProvider.parseRequest(request);
// Verify client application
if (StringUtil.isEmpty(param.getClientId())) {
throw new InvalidClientException(ErrorResponse.INVALID_CLIENT);
}
ClientDetail clientDetail = JapIds.getContext().getClientDetailService().getByClientId(param.getClientId());
List<Map<String, Object>> scopeInfo = getScopeInfo(param);
Map<String, Object> result = new HashMap<>(5);
result.put("appInfo", clientDetail);
result.put("scopes", scopeInfo);
result.put("params", param);
return new IdsResponse<String, Object>().data(result);
}
/**
* Generate the html of the authorization confirmation page
*
* @param request Current request
* @return Confirm the html of the authorization page
*/
private String createConfirmPageHtml(HttpServletRequest request) {
IdsRequestParam param = IdsRequestParamProvider.parseRequest(request);
String clientId = param.getClientId();
ClientDetail clientDetail = JapIds.getContext().getClientDetailService().getByClientId(clientId);
OauthUtil.validClientDetail(clientDetail);
StringBuilder builder = new StringBuilder();
String html = "<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ " <head>\n"
+ " <meta charset=\"utf-8\">\n"
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
+ " <meta name=\"description\" content=\"\">\n"
+ " <meta name=\"author\" content=\"\">\n"
+ " <title>OAuth Approval</title>\n"
+ " </head>\n"
+ " \n";
builder.append(html).append("<body><h1>OAuth Approval</h1>");
builder.append("<p>Do you authorize \"<strong>").append(clientDetail.getAppName()).append("</strong>");
builder.append(" (").append(clientId).append(")");
builder.append("\" to access your protected resources?</p>");
builder.append("<form id=\"confirmationForm\" name=\"confirmationForm\" action=\"");
String requestPath = ObjectUtils.appendIfNotEndWith(JapIds.getIdsConfig().getAuthorizeUrl(), "?") + request.getQueryString();
builder.append(requestPath).append("\" method=\"post\">");
builder.append("<input name=\"user_oauth_approval\" value=\"true\" type=\"hidden\"/>");
String authorizeInputTemplate = "<label><input name=\"authorize\" value=\"Authorize\" type=\"submit\"/></label></form>";
if (param.getScope() != null) {
builder.append(createScopes(param, request));
builder.append(authorizeInputTemplate);
} else {
builder.append(authorizeInputTemplate);
builder.append("<form id=\"denialForm\" name=\"denialForm\" action=\"");
builder.append(requestPath).append("/oauth/authorize\" method=\"post\">");
builder.append("<input name=\"user_oauth_approval\" value=\"false\" type=\"hidden\"/>");
builder.append("<label><input name=\"deny\" value=\"Deny\" type=\"submit\"/></label></form>");
}
builder.append("</body></html>");
return builder.toString();
}
/**
* Generate the scope list of the authorization confirmation page
*
* @param param Parameters of the current request
* @param request Current request
* @return the scope list of the authorization confirmation page
*/
private String createScopes(IdsRequestParam param, HttpServletRequest request) {
StringBuilder builder = new StringBuilder("<ul style=\"list-style: none;padding-inline-start: 20px;\">");
List<Map<String, Object>> scopeInfo = getScopeInfo(param);
for (Map<String, Object> scope : scopeInfo) {
String approved = (Boolean) scope.get("selected") ? " checked" : "";
String denied = (Boolean) scope.get("selected") ? "" : " checked";
builder.append("<li><div class=\"form-group\">");
builder.append("<input type=\"checkbox\" name=\"scopes\"").append(" value=\"").append(scope.get("code")).append("\"").append(approved).append(" style=\"margin-right: 5px;\">")
.append(scope.get("code")).append(" - ").append(scope.get("description"))
.append("</input> ");
builder.append(denied).append("</div></li>");
}
builder.append("</ul>");
return builder.toString();
}
/**
* Reorganize scope information
*
* @param param Parameters of the current request
* @return List
*/
private List<Map<String, Object>> getScopeInfo(IdsRequestParam param) {
ClientDetail clientDetail = JapIds.getContext().getClientDetailService().getByClientId(param.getClientId());
OauthUtil.validClientDetail(clientDetail);
Set<String> userAuthorizedScopes = OauthUtil.validateScope(param.getScope(), clientDetail.getScopes());
Set<String> supportedScopes = OauthUtil.convertStrToList(clientDetail.getScopes());
List<IdsScope> scopeList = IdsScopeProvider.getScopeByCodes(supportedScopes);
List<Map<String, Object>> scopeInfo = new LinkedList<>();
Map<String, Object> scopeItem = null;
for (IdsScope idsScope : scopeList) {
scopeItem = new HashMap<>(5);
scopeItem.put("code", idsScope.getCode());
scopeItem.put("description", idsScope.getDescription());
scopeItem.put("selected", userAuthorizedScopes.contains(idsScope.getCode()));
scopeInfo.add(scopeItem);
}
return scopeInfo;
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.endpoint;
import cn.hutool.core.util.ArrayUtil;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.InvalidScopeException;
import com.fujieid.jap.ids.model.ClientDetail;
import com.fujieid.jap.ids.model.IdsRequestParam;
import com.fujieid.jap.ids.model.IdsResponse;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.model.enums.ResponseType;
import com.fujieid.jap.ids.provider.IdsAuthorizationProvider;
import com.fujieid.jap.ids.provider.IdsRequestParamProvider;
import com.fujieid.jap.ids.util.OauthUtil;
import com.xkcoding.json.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
/**
* Authorization endpoint
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class AuthorizationEndpoint extends AbstractEndpoint {
private final IdsAuthorizationProvider idsAuthorizationProvider = new IdsAuthorizationProvider(oauth2Service);
/**
* Authorize current request
* <p>
* When logged in, the method returns the callback url (with parameters such as code)
* <p>
* When not logged in, the method returns the login url (with the parameters of the current request)
*
* @param request Current request
* @return Callback url or authorization url
* @throws IOException IOException
*/
public IdsResponse<String, Object> authorize(HttpServletRequest request) throws IOException {
IdsRequestParam param = IdsRequestParamProvider.parseRequest(request);
ClientDetail clientDetail = JapIds.getContext().getClientDetailService().getByClientId(param.getClientId());
OauthUtil.validClientDetail(clientDetail);
OauthUtil.validateResponseType(param.getResponseType(), clientDetail.getResponseTypes());
OauthUtil.validateRedirectUri(param.getRedirectUri(), clientDetail);
OauthUtil.validateScope(param.getScope(), clientDetail.getScopes());
if (JapIds.isAuthenticated(request)) {
UserInfo userInfo = JapIds.getUserInfo(request);
String url = generateResponseUrl(param, param.getResponseType(), clientDetail, userInfo);
return new IdsResponse<String, Object>().data(url);
}
return new IdsResponse<String, Object>()
.data(OauthUtil.createAuthorizeUrl(idsConfig.getLoginUrl(), param));
}
/**
* User-initiated consent authorization
*
* @param request current request
* @return Return the callback url (with parameters such as code)
*/
public IdsResponse<String, Object> agree(HttpServletRequest request) {
IdsRequestParam param = IdsRequestParamProvider.parseRequest(request);
// The scope checked by the user may be inconsistent with the scope passed in the current request
String[] requestScopes = request.getParameterValues("scopes");
Set<String> scopes = null;
if (ArrayUtil.isEmpty(requestScopes)) {
if (StringUtil.isEmpty(param.getScope())) {
throw new InvalidScopeException(ErrorResponse.INVALID_SCOPE);
}
scopes = OauthUtil.convertStrToList(param.getScope()).stream().distinct().collect(Collectors.toSet());
} else {
scopes = new TreeSet<>(Arrays.asList(requestScopes));
}
// Ultimately participating in the authorized scope
param.setScope(String.join(" ", scopes));
ClientDetail clientDetail = JapIds.getContext().getClientDetailService().getByClientId(param.getClientId());
OauthUtil.validClientDetail(clientDetail);
String responseType = param.getResponseType();
UserInfo userInfo = JapIds.getUserInfo(request);
String url = generateResponseUrl(param, responseType, clientDetail, userInfo);
return new IdsResponse<String, Object>().data(url);
}
/**
* Generate callback url
*
* @param param Parameters in the current request
* @param responseType oauth authorized response type
* @param clientDetail Currently authorized client
* @return Callback url
*/
private String generateResponseUrl(IdsRequestParam param, String responseType, ClientDetail clientDetail, UserInfo userInfo) {
if (ResponseType.CODE.getType().equalsIgnoreCase(responseType)) {
return idsAuthorizationProvider.generateAuthorizationCodeResponse(userInfo, param, clientDetail);
}
if (ResponseType.TOKEN.getType().equalsIgnoreCase(responseType)) {
return idsAuthorizationProvider.generateImplicitGrantResponse(userInfo, param, clientDetail);
}
if (ResponseType.ID_TOKEN.getType().equalsIgnoreCase(responseType)) {
return idsAuthorizationProvider.generateIdTokenAuthorizationResponse(userInfo, param, clientDetail);
}
if (ResponseType.ID_TOKEN_TOKEN.getType().equalsIgnoreCase(responseType)) {
return idsAuthorizationProvider.generateIdTokenTokenAuthorizationResponse(userInfo, param, clientDetail);
}
if (ResponseType.CODE_ID_TOKEN.getType().equalsIgnoreCase(responseType)) {
return idsAuthorizationProvider.generateCodeIdTokenAuthorizationResponse(userInfo, param, clientDetail);
}
if (ResponseType.CODE_TOKEN.getType().equalsIgnoreCase(responseType)) {
return idsAuthorizationProvider.generateCodeTokenAuthorizationResponse(userInfo, param, clientDetail);
}
if (ResponseType.CODE_ID_TOKEN_TOKEN.getType().equalsIgnoreCase(responseType)) {
return idsAuthorizationProvider.generateCodeIdTokenTokenAuthorizationResponse(userInfo, param, clientDetail);
}
// none
return idsAuthorizationProvider.generateNoneAuthorizationResponse(param);
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.endpoint;
import com.fujieid.jap.ids.model.OidcDiscoveryDto;
import com.fujieid.jap.ids.oidc.OidcUtil;
/**
* OpenID Provider Endpoint
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata" target="_blank">OpenID Provider Metadata</a>
* @since 1.0.0
*/
public class DiscoveryEndpoint extends AbstractEndpoint {
/**
* OpenID Provider Configuration Response.
* <p>
* For multiple users (can be users, organizations, enterprises, etc.), different configurations can be generated through {@code identity}.
* <p>
* Such as the following scenario:
* <p>
* The issuer of idp is `http://localhost`, and the api in the idp is distinguished according to the user ID.
* <p>
* When {@code identity} is not empty, take the token endpoint as an example. The endpoint generated by this method is actually `http://localhost/oauth/token/{identity}`
*
* @param identity identity
* @return OpenID Provider Configuration
* @see <a href="https://tools.ietf.org/html/draft-ietf-oauth-discovery-06">https://tools.ietf.org/html/draft-ietf-oauth-discovery-06</a>
* @see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata" target="_blank">OpenID Provider Metadata</a>
* @see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">OpenID Provider Configuration Response</a>
*/
public OidcDiscoveryDto getOidcDiscoveryInfo(String identity) {
return OidcUtil.getOidcDiscoveryInfo(identity, idsConfig);
}
/**
* Get the public key of the encrypted token (can be used to decrypt the token)
*
* <p>
* For multiple users (can be users, organizations, enterprises, etc.), The public key information of different users can be obtained through {@code identity}.
* <p>
* Such as the following scenario:
* <p>
* Different users are assigned different keys in the idp system. When the {@code identity} is not empty,
* <p>
* the method will first obtain the user's certificate through the {@code identity}, and then generate the public key through the certificate
*
* @param identity identity
* @return public key
*/
public String getJwksPublicKey(String identity) {
return OidcUtil.getJwksPublicKey(identity, idsConfig);
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.endpoint;
import com.fujieid.jap.core.util.RequestUtil;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.xkcoding.json.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* OAuth 异常
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class ErrorEndpoint extends AbstractEndpoint {
/**
* Generate the html of the error authorization page
*
* @param error error type
* @param errorDescription error description
* @return error page html
*/
public String createErrorPageHtml(String error, String errorDescription) {
return generateErrorPageHtml(error, errorDescription);
}
/**
* Obtain exception information from the request url and display the exception page
*
* @param request Current request
* @param response Current response
* @throws IOException IOException
*/
public void showErrorPage(HttpServletRequest request, HttpServletResponse response) throws IOException {
ErrorResponse errorResponse = ErrorResponse.getByError(RequestUtil.getParam("error", request));
String errorPageHtml = createErrorPageHtml(errorResponse.getError(), errorResponse.getErrorDescription());
response.setContentType("text/html;charset=UTF-8");
response.setContentLength(errorPageHtml.getBytes(StandardCharsets.UTF_8).length);
response.getWriter().write(errorPageHtml);
}
/**
* Display customized exception content
*
* @param error error type
* @param errorDescription error description
* @param response Current response
* @throws IOException IOException
*/
public void showErrorPage(String error, String errorDescription, HttpServletResponse response) throws IOException {
String errorPageHtml = createErrorPageHtml(error, errorDescription);
response.setContentType("text/html;charset=UTF-8");
response.setContentLength(errorPageHtml.getBytes(StandardCharsets.UTF_8).length);
response.getWriter().write(errorPageHtml);
}
private String generateErrorPageHtml(String error, String errorDescription) {
String html = "<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ " <head>\n"
+ " <meta charset=\"utf-8\">\n"
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
+ " <meta name=\"description\" content=\"\">\n"
+ " <meta name=\"author\" content=\"\">\n"
+ " <title>Oops!, something went wrong</title>\n"
+ " <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n"
+ " </head>\n"
+ " <body>\n"
+ " <div class=\"container text-center\" style=\"margin-top: 10%;\">\n"
+ " <p><h1>Oops!, something went wrong</h1></p>\n";
if (StringUtil.isNotEmpty(error)) {
html += "<p>" + error + "</p>";
}
html += " <p>\n" + errorDescription + " </p>\n"
+ " <p>Feel free to contact us.</p>\n"
+ " <p>Please try again.</p>\n" +
"</div>\n" +
"</body></html>";
return html;
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.endpoint;
import cn.hutool.core.util.ObjectUtil;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.IdsException;
import com.fujieid.jap.ids.model.IdsResponse;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.util.ObjectUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* Login Endpoint
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class LoginEndpoint extends AbstractEndpoint {
/**
* 显示默认的登录页面
*
* @param request current request
* @param response current response
* @throws IOException IOException
*/
public void showLoginPage(HttpServletRequest request, HttpServletResponse response) throws IOException {
String loginPageHtml = generateLoginPageHtml(request);
response.setContentType("text/html;charset=UTF-8");
response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);
response.getWriter().write(loginPageHtml);
}
private String generateLoginPageHtml(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
sb.append("<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ " <head>\n"
+ " <meta charset=\"utf-8\">\n"
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
+ " <meta name=\"description\" content=\"\">\n"
+ " <meta name=\"author\" content=\"\">\n"
+ " <title>Please sign in</title>\n"
+ " <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n"
+ " <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"
+ " </head>\n"
+ " <body>\n"
+ " <div class=\"container\">\n");
String authenticationUrl = ObjectUtils.appendIfNotEndWith(JapIds.getIdsConfig().getLoginUrl(), "?") + request.getQueryString();
sb.append(" <form class=\"form-signin\" method=\"post\" action=\"").append(authenticationUrl).append("\">\n")
.append(" <h2 class=\"form-signin-heading\">Please sign in</h2>\n")
.append(" <p>\n")
.append(" <label for=\"username\" class=\"sr-only\">Username</label>\n")
.append(" <input type=\"text\" id=\"username\" name=\"").append(JapIds.getIdsConfig().getUsernameField())
.append("\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n").append(" </p>\n")
.append(" <p>\n").append(" <label for=\"password\" class=\"sr-only\">Password</label>\n")
.append(" <input type=\"password\" id=\"password\" name=\"")
.append(JapIds.getIdsConfig().getPasswordField()).append("\" class=\"form-control\" placeholder=\"Password\" required>\n")
.append(" </p>\n").append(" <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n")
.append(" </form>\n");
sb.append("</div>\n");
sb.append("</body></html>");
return sb.toString();
}
/**
* Login with account password
*
* @param request current request
* @return Confirm authorization page
*/
public IdsResponse<String, Object> signin(HttpServletRequest request) {
String username = request.getParameter(idsConfig.getUsernameField());
String password = request.getParameter(idsConfig.getPasswordField());
if (ObjectUtil.hasEmpty(username, password)) {
throw new IdsException(ErrorResponse.INVALID_USER_CERTIFICATE);
}
UserInfo userInfo = JapIds.getContext().getUserService().loginByUsernameAndPassword(username, password);
if (null == userInfo) {
throw new IdsException(ErrorResponse.INVALID_USER_CERTIFICATE);
}
JapIds.saveUserInfo(userInfo, request);
return new IdsResponse<String, Object>()
.data(ObjectUtils.appendIfNotEndWith(JapIds.getIdsConfig().getConfirmUrl(), "?") + request.getQueryString());
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.endpoint;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.model.IdsResponse;
import com.fujieid.jap.ids.util.TokenUtil;
import javax.servlet.http.HttpServletRequest;
/**
* Logout Endpoint
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class LogoutEndpoint extends AbstractEndpoint {
public IdsResponse<String, Object> logout(HttpServletRequest request) {
JapIds.removeUserInfo(request);
TokenUtil.invalidateToken(request);
request.getSession().invalidate();
return new IdsResponse<String, Object>()
.data(idsConfig.getLogoutRedirectUrl());
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.endpoint;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.UnsupportedGrantTypeException;
import com.fujieid.jap.ids.model.IdsRequestParam;
import com.fujieid.jap.ids.model.IdsResponse;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.model.enums.GrantType;
import com.fujieid.jap.ids.provider.IdsRequestParamProvider;
import com.fujieid.jap.ids.provider.IdsTokenProvider;
import com.fujieid.jap.ids.util.JwtUtil;
import com.fujieid.jap.ids.util.TokenUtil;
import com.xkcoding.json.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
/**
* Token Endpoint. According to the request parameters, to obtain different types of access tokens, refer to:
* <p>
* https://tools.ietf.org/html/rfc6749#section-5
* <p>
* https://tools.ietf.org/html/rfc6749#section-6
* <p>
* The OAuth 2.0 Authorization Framework: Bearer Token Usage: https://tools.ietf.org/html/rfc6750
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class TokenEndpoint extends AbstractEndpoint {
private final IdsTokenProvider idsTokenProvider = new IdsTokenProvider(oauth2Service);
public IdsResponse<String, Object> getToken(HttpServletRequest request) {
IdsRequestParam param = IdsRequestParamProvider.parseRequest(request);
if (StringUtil.isEmpty(param.getGrantType())) {
throw new UnsupportedGrantTypeException(ErrorResponse.UNSUPPORTED_GRANT_TYPE);
}
if (GrantType.AUTHORIZATION_CODE.getType().equals(param.getGrantType())) {
return idsTokenProvider.generateAuthorizationCodeResponse(param);
}
if (GrantType.PASSWORD.getType().equals(param.getGrantType())) {
return idsTokenProvider.generatePasswordResponse(param);
}
if (GrantType.CLIENT_CREDENTIALS.getType().equals(param.getGrantType())) {
return idsTokenProvider.generateClientCredentialsResponse(param);
}
if (GrantType.REFRESH_TOKEN.getType().equals(param.getGrantType())) {
return idsTokenProvider.generateRefreshTokenResponse(param);
}
throw new UnsupportedGrantTypeException(ErrorResponse.UNSUPPORTED_GRANT_TYPE);
}
public IdsResponse<String, Object> checkToken(String token) {
return new IdsResponse<String, Object>()
.addAll(JwtUtil.parseJwtToken(token, JapIds.getIdsConfig()));
}
public IdsResponse<String, Object> revokeToken(HttpServletRequest request) {
TokenUtil.invalidateToken(request);
return new IdsResponse<>();
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.endpoint;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.IdsException;
import com.fujieid.jap.ids.exception.InvalidTokenException;
import com.fujieid.jap.ids.model.AccessToken;
import com.fujieid.jap.ids.model.IdsResponse;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.util.TokenUtil;
import com.xkcoding.json.JsonUtil;
import javax.servlet.http.HttpServletRequest;
/**
* userinfo endpoint
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class UserInfoEndpoint extends AbstractEndpoint {
public IdsResponse<String, Object> getCurrentUserInfo(HttpServletRequest request) {
String accessTokenStr = TokenUtil.getAccessToken(request);
AccessToken accessToken = TokenUtil.getByAccessToken(accessTokenStr);
if (null == accessToken) {
throw new InvalidTokenException(ErrorResponse.INVALID_TOKEN);
}
UserInfo user = JapIds.getContext().getUserService().getById(accessToken.getUserId());
if (null == user) {
throw new IdsException(ErrorResponse.ACCESS_DENIED);
}
user.setEmail(null);
user.setPhone_number(null);
IdsResponse<String, Object> idsResponse = new IdsResponse<>();
idsResponse.putAll(JsonUtil.parseKv(JsonUtil.toJsonString(user)));
return idsResponse;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* ids The service endpoint that needs to be provided externally
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.endpoint;

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class IdsException extends RuntimeException {
private String error;
private String errorDescription;
public IdsException(String message) {
super(message);
this.errorDescription = message;
}
public IdsException(String error, String errorDescription) {
super(error + ": " + errorDescription);
this.error = error;
this.errorDescription = errorDescription;
}
public IdsException(ErrorResponse errorResponse) {
this.error = errorResponse.getError();
this.errorDescription = errorResponse.getErrorDescription();
}
public String getError() {
return error;
}
public IdsException setError(String error) {
this.error = error;
return this;
}
public String getErrorDescription() {
return errorDescription;
}
public IdsException setErrorDescription(String errorDescription) {
this.errorDescription = errorDescription;
return this;
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class IdsTokenException extends IdsException {
public IdsTokenException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
public IdsTokenException(String message) {
super(message);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class InvalidClientException extends IdsException {
public InvalidClientException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class InvalidCodeException extends IdsException {
public InvalidCodeException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class InvalidGrantException extends IdsException {
public InvalidGrantException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
public InvalidGrantException(String message) {
super(message);
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class InvalidJwksException extends IdsException {
public InvalidJwksException(String message) {
super(message);
}
public InvalidJwksException(ErrorResponse errorResponse) {
super(errorResponse);
}
public InvalidJwksException(String error, String errorDescription) {
super(error, errorDescription);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class InvalidRedirectUriException extends IdsException {
public InvalidRedirectUriException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class InvalidRequestException extends IdsException {
public InvalidRequestException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class InvalidScopeException extends IdsException {
public InvalidScopeException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
public InvalidScopeException(String message) {
super(message);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class InvalidTokenException extends IdsException {
public InvalidTokenException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class UnsupportedGrantTypeException extends IdsException {
public UnsupportedGrantTypeException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.exception;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class UnsupportedResponseTypeException extends IdsException {
public UnsupportedResponseTypeException(ErrorResponse codeEnum) {
super(codeEnum.getError(), codeEnum.getErrorDescription());
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* ids exceptions
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.exception;

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.filter;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.config.IdsConfig;
import com.xkcoding.json.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Ids Filter
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class AbstractIdsFilter {
protected static final Log log = LogFactory.get();
protected final List<String> ignoreUrls = new ArrayList<>();
/**
* Whether it is a servlet request that needs to be ignored
*
* @param request The current request to be intercepted
* @return boolean, the request does not need to be intercepted when true is returned
*/
protected boolean isIgnoredServletPath(HttpServletRequest request) {
String servletPath = request.getServletPath();
if (ignoreUrls.contains(servletPath)) {
return true;
}
for (String ignoreUrl : ignoreUrls) {
if (ignoreUrl.contains("**")) {
String[] urls = ignoreUrl.split("/*/*");
if (urls.length == 1) {
if (servletPath.startsWith(urls[0])) {
return true;
}
}
if (urls.length > 1) {
if (servletPath.startsWith(urls[0]) && servletPath.endsWith(urls[urls.length - 1])) {
return true;
}
}
}
}
return false;
}
/**
* Initialize the url of the filter to be released
*
* @param ignoreUrl URLs that do not need to be intercepted
*/
protected void initIgnoreUrls(String ignoreUrl) {
if (null != ignoreUrl) {
String[] ignoreUrls = ignoreUrl.split(",");
this.ignoreUrls.addAll(Arrays.asList(ignoreUrls));
} else {
// Fault-tolerant processing
IdsConfig config = JapIds.getIdsConfig();
String issuer = config.getIssuer();
String authorizeUrl = config.getAuthorizeUrl();
String loginUrl = config.getLoginUrl();
String errorUrl = config.getErrorUrl();
String confirmUrl = config.getConfirmUrl();
String tokenUrl = config.getTokenUrl();
String registrationUrl = config.getRegistrationUrl();
String jwksUrl = config.getJwksUrl();
String discoveryUrl = config.getDiscoveryUrl();
String[] urls = {authorizeUrl, loginUrl, errorUrl, confirmUrl, tokenUrl, registrationUrl, jwksUrl, discoveryUrl};
for (String url : urls) {
if (StringUtil.isNotEmpty(url) && url.startsWith(issuer)) {
this.ignoreUrls.add(url.substring(issuer.length()));
}
}
}
this.ignoreUrls.add("/favicon.ico");
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.filter;
import com.fujieid.jap.ids.util.TokenUtil;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* access token filter to verify the validity of the token
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsAccessTokenFilter extends AbstractIdsFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
boolean ignored = this.isIgnoredServletPath(request);
if (ignored) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
log.debug("{} - {}", request.getMethod(), request.getRequestURI());
String accessToken = TokenUtil.getAccessToken(request);
TokenUtil.validateAccessToken(accessToken);
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
Filter.super.destroy();
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
String ignoreUrl = filterConfig.getInitParameter("ignoreUrl");
this.initIgnoreUrls(ignoreUrl);
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.filter;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.IdsException;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* User status filter to verify the user's login status
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsUserStatusFilter extends AbstractIdsFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
boolean ignored = this.isIgnoredServletPath(request);
if (ignored) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
if (JapIds.isAuthenticated(request)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
throw new IdsException(ErrorResponse.INVALID_USER_STATUS);
}
@Override
public void destroy() {
Filter.super.destroy();
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
String ignoreUrl = filterConfig.getInitParameter("ignoreUrl");
this.initIgnoreUrls(ignoreUrl);
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* ids filter, including access token filter and user status filter
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.filter;

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* access token
*
* @author generate by HouTu Generator
* @version 1.0.0
* @since 1.0.1
*/
public class AccessToken implements Serializable {
private String accessToken;
private String refreshToken;
private String userId;
private String userName;
private String grantType;
private String scope;
private String clientId;
private Long accessTokenExpiresIn;
private Long refreshTokenExpiresIn;
private LocalDateTime accessTokenExpiration;
private LocalDateTime refreshTokenExpiration;
public String getAccessToken() {
return accessToken;
}
public AccessToken setAccessToken(String accessToken) {
this.accessToken = accessToken;
return this;
}
public String getRefreshToken() {
return refreshToken;
}
public AccessToken setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
return this;
}
public String getUserId() {
return userId;
}
public AccessToken setUserId(String userId) {
this.userId = userId;
return this;
}
public String getUserName() {
return userName;
}
public AccessToken setUserName(String userName) {
this.userName = userName;
return this;
}
public String getGrantType() {
return grantType;
}
public AccessToken setGrantType(String grantType) {
this.grantType = grantType;
return this;
}
public String getScope() {
return scope;
}
public AccessToken setScope(String scope) {
this.scope = scope;
return this;
}
public String getClientId() {
return clientId;
}
public AccessToken setClientId(String clientId) {
this.clientId = clientId;
return this;
}
public Long getAccessTokenExpiresIn() {
return accessTokenExpiresIn;
}
public AccessToken setAccessTokenExpiresIn(Long accessTokenExpiresIn) {
this.accessTokenExpiresIn = accessTokenExpiresIn;
return this;
}
public Long getRefreshTokenExpiresIn() {
return refreshTokenExpiresIn;
}
public AccessToken setRefreshTokenExpiresIn(Long refreshTokenExpiresIn) {
this.refreshTokenExpiresIn = refreshTokenExpiresIn;
return this;
}
public LocalDateTime getAccessTokenExpiration() {
return accessTokenExpiration;
}
public AccessToken setAccessTokenExpiration(LocalDateTime accessTokenExpiration) {
this.accessTokenExpiration = accessTokenExpiration;
return this;
}
public LocalDateTime getRefreshTokenExpiration() {
return refreshTokenExpiration;
}
public AccessToken setRefreshTokenExpiration(LocalDateTime refreshTokenExpiration) {
this.refreshTokenExpiration = refreshTokenExpiration;
return this;
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
import java.io.Serializable;
/**
* Authorization code
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public class AuthCode implements Serializable {
private String scope;
private UserInfo user;
private String nonce;
private String codeChallengeMethod;
private String codeChallenge;
public String getScope() {
return scope;
}
public AuthCode setScope(String scope) {
this.scope = scope;
return this;
}
public UserInfo getUser() {
return user;
}
public AuthCode setUser(UserInfo user) {
this.user = user;
return this;
}
public String getNonce() {
return nonce;
}
public AuthCode setNonce(String nonce) {
this.nonce = nonce;
return this;
}
public String getCodeChallengeMethod() {
return codeChallengeMethod;
}
public AuthCode setCodeChallengeMethod(String codeChallengeMethod) {
this.codeChallengeMethod = codeChallengeMethod;
return this;
}
public String getCodeChallenge() {
return codeChallenge;
}
public AuthCode setCodeChallenge(String codeChallenge) {
this.codeChallenge = codeChallenge;
return this;
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class ClientCertificate {
private String id;
private String secret;
public ClientCertificate() {
}
public ClientCertificate(String id, String secret) {
this.id = id;
this.secret = secret;
}
public String getId() {
return id;
}
public ClientCertificate setId(String id) {
this.id = id;
return this;
}
public String getSecret() {
return secret;
}
public ClientCertificate setSecret(String secret) {
this.secret = secret;
return this;
}
}

View File

@ -0,0 +1,317 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
import java.io.Serializable;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class ClientDetail implements Serializable {
private String id;
/**
* 应用名
*/
private String appName;
/**
* 应用ID
*/
private String clientId;
/**
* 应用密钥
*/
private String clientSecret;
/**
* 自定义二级域名
*/
private String siteDomain;
/**
* 认证成功后跳转的地址
*/
private String redirectUri;
/**
* 退出成功后跳转的地址
*/
private String logoutRedirectUri;
/**
* 应用图标
*/
private String logo;
/**
* 是否可用
*/
private Boolean available;
/**
* 应用描述
*/
private String description;
/**
* 权限范围
*/
private String scopes;
/**
* 授权类型
*/
private String grantTypes;
/**
* 返回类型
*/
private String responseTypes;
/**
* code 授权码有效时间
*/
private Long codeExpiresIn;
/**
* id token有效时间
*/
private Long idTokenExpiresIn;
/**
* access token有效时间
*/
private Long accessTokenExpiresIn;
/**
* refresh token有效时间
*/
private Long refreshTokenExpiresIn;
/**
* 附加信息
*/
private String additionalInformation;
/**
* 自动批准(不显示确认页面)
*/
private Boolean autoApprove;
/**
* 启用 PKCE 增强协议
*/
private Boolean enablePkce;
/**
* PKCE 质询码的加密方式
*/
private String codeChallengeMethod;
public String getId() {
return id;
}
public ClientDetail setId(String id) {
this.id = id;
return this;
}
public String getAppName() {
return appName;
}
public ClientDetail setAppName(String appName) {
this.appName = appName;
return this;
}
public String getClientId() {
return clientId;
}
public ClientDetail setClientId(String clientId) {
this.clientId = clientId;
return this;
}
public String getClientSecret() {
return clientSecret;
}
public ClientDetail setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
return this;
}
public String getSiteDomain() {
return siteDomain;
}
public ClientDetail setSiteDomain(String siteDomain) {
this.siteDomain = siteDomain;
return this;
}
public String getRedirectUri() {
return redirectUri;
}
public ClientDetail setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
return this;
}
public String getLogoutRedirectUri() {
return logoutRedirectUri;
}
public ClientDetail setLogoutRedirectUri(String logoutRedirectUri) {
this.logoutRedirectUri = logoutRedirectUri;
return this;
}
public String getLogo() {
return logo;
}
public ClientDetail setLogo(String logo) {
this.logo = logo;
return this;
}
public Boolean getAvailable() {
return available;
}
public ClientDetail setAvailable(Boolean available) {
this.available = available;
return this;
}
public String getDescription() {
return description;
}
public ClientDetail setDescription(String description) {
this.description = description;
return this;
}
public String getScopes() {
return scopes;
}
public ClientDetail setScopes(String scopes) {
this.scopes = scopes;
return this;
}
public String getGrantTypes() {
return grantTypes;
}
public ClientDetail setGrantTypes(String grantTypes) {
this.grantTypes = grantTypes;
return this;
}
public String getResponseTypes() {
return responseTypes;
}
public ClientDetail setResponseTypes(String responseTypes) {
this.responseTypes = responseTypes;
return this;
}
public Long getCodeExpiresIn() {
return codeExpiresIn;
}
public ClientDetail setCodeExpiresIn(Long codeExpiresIn) {
this.codeExpiresIn = codeExpiresIn;
return this;
}
public Long getIdTokenExpiresIn() {
return idTokenExpiresIn;
}
public ClientDetail setIdTokenExpiresIn(Long idTokenExpiresIn) {
this.idTokenExpiresIn = idTokenExpiresIn;
return this;
}
public Long getAccessTokenExpiresIn() {
return accessTokenExpiresIn;
}
public ClientDetail setAccessTokenExpiresIn(Long accessTokenExpiresIn) {
this.accessTokenExpiresIn = accessTokenExpiresIn;
return this;
}
public Long getRefreshTokenExpiresIn() {
return refreshTokenExpiresIn;
}
public ClientDetail setRefreshTokenExpiresIn(Long refreshTokenExpiresIn) {
this.refreshTokenExpiresIn = refreshTokenExpiresIn;
return this;
}
public String getAdditionalInformation() {
return additionalInformation;
}
public ClientDetail setAdditionalInformation(String additionalInformation) {
this.additionalInformation = additionalInformation;
return this;
}
public Boolean getAutoApprove() {
return autoApprove;
}
public ClientDetail setAutoApprove(Boolean autoApprove) {
this.autoApprove = autoApprove;
return this;
}
public Boolean getEnablePkce() {
return enablePkce;
}
public ClientDetail setEnablePkce(Boolean enablePkce) {
this.enablePkce = enablePkce;
return this;
}
public String getCodeChallengeMethod() {
return codeChallengeMethod;
}
public ClientDetail setCodeChallengeMethod(String codeChallengeMethod) {
this.codeChallengeMethod = codeChallengeMethod;
return this;
}
}

View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
/**
* ids constant
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public interface IdsConsts {
String SLASH = "/";
/**
* The default validity period of the authorization code is 10 minutes (600 seconds)
*/
long AUTHORIZATION_CODE_ACTIVITY_TIME = 600;
/**
* The default validity period of access token is 30 days (2592000 seconds)
*/
long ACCESS_TOKEN_ACTIVITY_TIME = 2592000;
/**
* The default validity period of refresh token is 365 days (31536000 seconds)
*/
long REFRESH_TOKEN_ACTIVITY_TIME = 31536000;
/**
* The default validity period of id token is 365 days (31536000 seconds)
*/
long ID_TOKEN_ACTIVITY_TIME = 31536000;
/**
* token header name
*/
String AUTHORIZATION_HEADER_NAME = "Authorization";
/**
* Token Type
*/
String TOKEN_TYPE_BEARER = "Bearer";
/**
* Cache key of oauth authorized user
*/
String IDS_OAUTH_CACHE_KEY = "JAPIDS:OAUTH2:";
/**
* Cache key of oauth authorized user
*/
String OAUTH_USERINFO_CACHE_KEY = IDS_OAUTH_CACHE_KEY + "USERINFO";
/**
* Cache the key of access token
*/
String OAUTH_ACCESS_TOKEN_CACHE_KEY = IDS_OAUTH_CACHE_KEY + "ACCESS_TOKEN:";
/**
* Cache the key of refresh token
*/
String OAUTH_REFRESH_TOKEN_CACHE_KEY = IDS_OAUTH_CACHE_KEY + "REFRESH_TOKEN:";
/**
* Cache the key of the oauth code
*/
String OAUTH_CODE_CACHE_KEY = IDS_OAUTH_CACHE_KEY + "CODE:";
String CODE_CHALLENGE = "code_challenge";
String CODE_CHALLENGE_METHOD = "code_challenge_method";
String CODE_VERIFIER = "code_verifier";
String CLIENT_ID = "client_id";
String CLIENT_SECRET = "client_secret";
String SCOPE = "scope";
String REDIRECT_URI = "redirect_uri";
String STATE = "state";
String RESPONSE_TYPE = "response_type";
String GRANT_TYPE = "grant_type";
String TOKEN_TYPE = "token_type";
String ACCESS_TOKEN = "access_token";
String REFRESH_TOKEN = "refresh_token";
String ID_TOKEN = "id_token";
String EXPIRES_IN = "expires_in";
String UID = "uid";
String CODE = "code";
String RESPONSE_MODE = "response_mode";
String DISPLAY = "display";
String PROMPT = "prompt";
String MAX_AGE = "max_age";
String ID_TOKEN_HINT = "id_token_hint";
String AUTOAPPROVE = "autoapprove";
String USERNAME = "username";
String PASSWORD = "password";
/**
* {@code auth_time} - the time when the End-User authentication occurred
*/
String AUTH_TIME = "auth_time";
/**
* {@code nonce} - a {@code String} value used to associate a Client session with an ID Token,
* and to mitigate replay attacks.
*/
String NONCE = "nonce";
/**
* {@code acr} - the Authentication Context Class Reference
*/
String ACR = "acr";
}

View File

@ -0,0 +1,329 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
import com.xkcoding.json.util.StringUtil;
/**
* Parameters of oauth request
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsRequestParam {
private String clientId;
private String clientSecret;
private String grantType;
private String code;
private String redirectUri;
private String scope;
private String state;
private String accessToken;
private String refreshToken;
private String responseType;
private String uid;
private String autoapprove;
private String username;
private String password;
private String codeVerifier;
private String codeChallengeMethod;
private String codeChallenge;
/* The following are the parameters supported by the oidc protocol, referenced from: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest */
/**
* optional, The nonce parameter value needs to include per-session state and be unguessable to attackers
*/
private String nonce;
/**
* Optional. The newly defined parameter of oidc (oauth 2.0 form post response mode) is used to specify how the authorization endpoint returns data.
*/
private String responseMode;
/**
* OPTIONAL. ASCII string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are:
* <p>
* <strong>page</strong> - The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view.
* If the display parameter is not specified, this is the default display mode.
* <p>
* <strong>popup</strong> - The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window.
* The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over.
* <p>
* <strong>touch</strong> - The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface.
* <p>
* <strong>wap</strong> - The Authorization Server SHOULD display the authentication and consent UI consistent with a "feature phone" type display.
*/
private String display;
/**
* OPTIONAL. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent. The defined values are:
* <p>
* <strong>none</strong> - The Authorization Server MUST NOT display any authentication or consent user interface pages.
* An error is returned if an End-User is not already authenticated or the Client does not have pre-configured
* consent for the requested Claims or does not fulfill other conditions for processing the request.
* The error code will typically be login_required, interaction_required, or another code defined in Section 3.1.2.6.
* This can be used as a method to check for existing authentication and/or consent.
* <p>
* <strong>login</strong> - The Authorization Server SHOULD prompt the End-User for reauthentication.
* If it cannot reauthenticate the End-User, it MUST return an error, typically login_required.
* <p>
* <strong>consent</strong> - The Authorization Server SHOULD prompt the End-User for consent before returning information to the Client.
* If it cannot obtain consent, it MUST return an error, typically consent_required.
* <p>
* <strong>select_account</strong> - The Authorization Server SHOULD prompt the End-User to select a user account.
* This enables an End-User who has multiple accounts at the Authorization Server to select amongst
* the multiple accounts that they might have current sessions for.
* If it cannot obtain an account selection choice made by the End-User,
* it MUST return an error, typically account_selection_required.
*/
private String prompt;
/**
* Optional. Represents the valid time of the eu authentication information,
* corresponding to the claim of auth time in the id token. For example,
* if the setting is 20 minutes, if the time is exceeded, you need to guide eu to re-authenticate.
*/
private String authTime;
/**
* Optional. For the previously issued id token, if the id token is verified and valid, it needs to return a normal response;
* if there is an error, it returns a corresponding error prompt.
*/
private String idTokenHint;
/**
* Optional. Requested Authentication Context Class Reference values.
* Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request,
* with the values appearing in order of preference
*/
private String acr;
public boolean isEnablePkce() {
return !StringUtil.isEmpty(this.getCodeVerifier());
}
public String getClientId() {
return clientId;
}
public IdsRequestParam setClientId(String clientId) {
this.clientId = clientId;
return this;
}
public String getClientSecret() {
return clientSecret;
}
public IdsRequestParam setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
return this;
}
public String getGrantType() {
return grantType;
}
public IdsRequestParam setGrantType(String grantType) {
this.grantType = grantType;
return this;
}
public String getCode() {
return code;
}
public IdsRequestParam setCode(String code) {
this.code = code;
return this;
}
public String getRedirectUri() {
return redirectUri;
}
public IdsRequestParam setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
return this;
}
public String getScope() {
return scope;
}
public IdsRequestParam setScope(String scope) {
this.scope = scope;
return this;
}
public String getState() {
return state;
}
public IdsRequestParam setState(String state) {
this.state = state;
return this;
}
public String getAccessToken() {
return accessToken;
}
public IdsRequestParam setAccessToken(String accessToken) {
this.accessToken = accessToken;
return this;
}
public String getRefreshToken() {
return refreshToken;
}
public IdsRequestParam setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
return this;
}
public String getResponseType() {
return responseType;
}
public IdsRequestParam setResponseType(String responseType) {
this.responseType = responseType;
return this;
}
public String getUid() {
return uid;
}
public IdsRequestParam setUid(String uid) {
this.uid = uid;
return this;
}
public String getNonce() {
return nonce;
}
public IdsRequestParam setNonce(String nonce) {
this.nonce = nonce;
return this;
}
public String getResponseMode() {
return responseMode;
}
public IdsRequestParam setResponseMode(String responseMode) {
this.responseMode = responseMode;
return this;
}
public String getDisplay() {
return display;
}
public IdsRequestParam setDisplay(String display) {
this.display = display;
return this;
}
public String getPrompt() {
return prompt;
}
public IdsRequestParam setPrompt(String prompt) {
this.prompt = prompt;
return this;
}
public String getAuthTime() {
return authTime;
}
public IdsRequestParam setAuthTime(String authTime) {
this.authTime = authTime;
return this;
}
public String getIdTokenHint() {
return idTokenHint;
}
public IdsRequestParam setIdTokenHint(String idTokenHint) {
this.idTokenHint = idTokenHint;
return this;
}
public String getAcr() {
return acr;
}
public IdsRequestParam setAcr(String acr) {
this.acr = acr;
return this;
}
public String getAutoapprove() {
return autoapprove;
}
public IdsRequestParam setAutoapprove(String autoapprove) {
this.autoapprove = autoapprove;
return this;
}
public String getUsername() {
return username;
}
public IdsRequestParam setUsername(String username) {
this.username = username;
return this;
}
public String getPassword() {
return password;
}
public IdsRequestParam setPassword(String password) {
this.password = password;
return this;
}
public String getCodeVerifier() {
return codeVerifier;
}
public IdsRequestParam setCodeVerifier(String codeVerifier) {
this.codeVerifier = codeVerifier;
return this;
}
public String getCodeChallengeMethod() {
return codeChallengeMethod;
}
public IdsRequestParam setCodeChallengeMethod(String codeChallengeMethod) {
this.codeChallengeMethod = codeChallengeMethod;
return this;
}
public String getCodeChallenge() {
return codeChallenge;
}
public IdsRequestParam setCodeChallenge(String codeChallenge) {
this.codeChallenge = codeChallenge;
return this;
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
import cn.hutool.core.util.ObjectUtil;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.xkcoding.json.util.StringUtil;
import java.util.HashMap;
import java.util.Map;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsResponse<K, V> extends HashMap<String, Object> {
private final String error = "error";
private final String error_description = "error_description";
private final String error_uri = "error_uri";
private final String state = "state";
private final String data = "data";
public IdsResponse<K, V> error(ErrorResponse errorCode) {
return this.error(errorCode.getError())
.errorDescription(errorCode.getErrorDescription());
}
public IdsResponse<K, V> error(String errorCode) {
this.put(this.error, errorCode);
return this;
}
public IdsResponse<K, V> errorDescription(String errorDescription) {
this.put(this.error_description, errorDescription);
return this;
}
public IdsResponse<K, V> errorUri(String errorUri) {
this.put(this.error_uri, errorUri);
return this;
}
public IdsResponse<K, V> state(String state) {
this.put(this.state, state);
return this;
}
public IdsResponse<K, V> data(Object data) {
this.put(this.error, "");
this.put(this.error_description, "");
this.put(this.data, data);
return this;
}
public boolean isSuccess() {
return StringUtil.isEmpty(this.getError());
}
public IdsResponse<K, V> add(String key, Object value) {
this.put(key, value);
return this;
}
public IdsResponse<K, V> addAll(Map<String, Object> map) {
this.putAll(map);
return this;
}
public String getError() {
return ObjectUtil.isEmpty(this.get(error)) ? null : String.valueOf(this.get(error));
}
public String getErrorDescription() {
return ObjectUtil.isEmpty(this.get(error_description)) ? null : String.valueOf(this.get(error_description));
}
public String getErrorUri() {
return ObjectUtil.isEmpty(this.get(error_uri)) ? null : String.valueOf(this.get(error_uri));
}
public String getState() {
return ObjectUtil.isEmpty(this.get(state)) ? null : String.valueOf(this.get(state));
}
public Object getData() {
return this.get(data);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
import java.io.Serializable;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsScope implements Serializable {
/**
* scope code, such as: basic, super
*/
private String code;
/**
* scope description
*/
private String description;
public String getCode() {
return code;
}
public IdsScope setCode(String code) {
this.code = code;
return this;
}
public String getDescription() {
return description;
}
public IdsScope setDescription(String description) {
this.description = description;
return this;
}
}

View File

@ -0,0 +1,248 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
import java.io.Serializable;
import java.util.List;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class OidcDiscoveryDto implements Serializable {
private String issuer;
private String authorization_endpoint;
private String token_endpoint;
private String userinfo_endpoint;
private String registration_endpoint;
private String end_session_endpoint;
private String check_session_iframe;
private String jwks_uri;
private List<String> grant_types_supported;
private List<String> response_modes_supported;
private List<String> response_types_supported;
private List<String> scopes_supported;
private List<String> token_endpoint_auth_methods_supported;
private List<String> request_object_signing_alg_values_supported;
private List<String> userinfo_signing_alg_values_supported;
private boolean request_parameter_supported;
private boolean request_uri_parameter_supported;
private boolean require_request_uri_registration;
private boolean claims_parameter_supported;
private List<String> id_token_signing_alg_values_supported;
private List<String> subject_types_supported;
private List<String> claims_supported;
public String getIssuer() {
return issuer;
}
public OidcDiscoveryDto setIssuer(String issuer) {
this.issuer = issuer;
return this;
}
public String getAuthorization_endpoint() {
return authorization_endpoint;
}
public OidcDiscoveryDto setAuthorization_endpoint(String authorization_endpoint) {
this.authorization_endpoint = authorization_endpoint;
return this;
}
public String getToken_endpoint() {
return token_endpoint;
}
public OidcDiscoveryDto setToken_endpoint(String token_endpoint) {
this.token_endpoint = token_endpoint;
return this;
}
public String getUserinfo_endpoint() {
return userinfo_endpoint;
}
public OidcDiscoveryDto setUserinfo_endpoint(String userinfo_endpoint) {
this.userinfo_endpoint = userinfo_endpoint;
return this;
}
public String getRegistration_endpoint() {
return registration_endpoint;
}
public OidcDiscoveryDto setRegistration_endpoint(String registration_endpoint) {
this.registration_endpoint = registration_endpoint;
return this;
}
public String getEnd_session_endpoint() {
return end_session_endpoint;
}
public OidcDiscoveryDto setEnd_session_endpoint(String end_session_endpoint) {
this.end_session_endpoint = end_session_endpoint;
return this;
}
public String getCheck_session_iframe() {
return check_session_iframe;
}
public OidcDiscoveryDto setCheck_session_iframe(String check_session_iframe) {
this.check_session_iframe = check_session_iframe;
return this;
}
public String getJwks_uri() {
return jwks_uri;
}
public OidcDiscoveryDto setJwks_uri(String jwks_uri) {
this.jwks_uri = jwks_uri;
return this;
}
public List<String> getGrant_types_supported() {
return grant_types_supported;
}
public OidcDiscoveryDto setGrant_types_supported(List<String> grant_types_supported) {
this.grant_types_supported = grant_types_supported;
return this;
}
public List<String> getResponse_modes_supported() {
return response_modes_supported;
}
public OidcDiscoveryDto setResponse_modes_supported(List<String> response_modes_supported) {
this.response_modes_supported = response_modes_supported;
return this;
}
public List<String> getResponse_types_supported() {
return response_types_supported;
}
public OidcDiscoveryDto setResponse_types_supported(List<String> response_types_supported) {
this.response_types_supported = response_types_supported;
return this;
}
public List<String> getScopes_supported() {
return scopes_supported;
}
public OidcDiscoveryDto setScopes_supported(List<String> scopes_supported) {
this.scopes_supported = scopes_supported;
return this;
}
public List<String> getToken_endpoint_auth_methods_supported() {
return token_endpoint_auth_methods_supported;
}
public OidcDiscoveryDto setToken_endpoint_auth_methods_supported(List<String> token_endpoint_auth_methods_supported) {
this.token_endpoint_auth_methods_supported = token_endpoint_auth_methods_supported;
return this;
}
public List<String> getRequest_object_signing_alg_values_supported() {
return request_object_signing_alg_values_supported;
}
public OidcDiscoveryDto setRequest_object_signing_alg_values_supported(List<String> request_object_signing_alg_values_supported) {
this.request_object_signing_alg_values_supported = request_object_signing_alg_values_supported;
return this;
}
public List<String> getUserinfo_signing_alg_values_supported() {
return userinfo_signing_alg_values_supported;
}
public OidcDiscoveryDto setUserinfo_signing_alg_values_supported(List<String> userinfo_signing_alg_values_supported) {
this.userinfo_signing_alg_values_supported = userinfo_signing_alg_values_supported;
return this;
}
public boolean isRequest_parameter_supported() {
return request_parameter_supported;
}
public OidcDiscoveryDto setRequest_parameter_supported(boolean request_parameter_supported) {
this.request_parameter_supported = request_parameter_supported;
return this;
}
public boolean isRequest_uri_parameter_supported() {
return request_uri_parameter_supported;
}
public OidcDiscoveryDto setRequest_uri_parameter_supported(boolean request_uri_parameter_supported) {
this.request_uri_parameter_supported = request_uri_parameter_supported;
return this;
}
public boolean isRequire_request_uri_registration() {
return require_request_uri_registration;
}
public OidcDiscoveryDto setRequire_request_uri_registration(boolean require_request_uri_registration) {
this.require_request_uri_registration = require_request_uri_registration;
return this;
}
public boolean isClaims_parameter_supported() {
return claims_parameter_supported;
}
public OidcDiscoveryDto setClaims_parameter_supported(boolean claims_parameter_supported) {
this.claims_parameter_supported = claims_parameter_supported;
return this;
}
public List<String> getId_token_signing_alg_values_supported() {
return id_token_signing_alg_values_supported;
}
public OidcDiscoveryDto setId_token_signing_alg_values_supported(List<String> id_token_signing_alg_values_supported) {
this.id_token_signing_alg_values_supported = id_token_signing_alg_values_supported;
return this;
}
public List<String> getSubject_types_supported() {
return subject_types_supported;
}
public OidcDiscoveryDto setSubject_types_supported(List<String> subject_types_supported) {
this.subject_types_supported = subject_types_supported;
return this;
}
public List<String> getClaims_supported() {
return claims_supported;
}
public OidcDiscoveryDto setClaims_supported(List<String> claims_supported) {
this.claims_supported = claims_supported;
return this;
}
}

View File

@ -0,0 +1,346 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model;
import com.xkcoding.json.util.StringUtil;
import java.io.Serializable;
import java.util.Map;
/**
* User info
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims" target="_blank">Standard Claims</a>
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse" target="_blank">UserInfo Response</a>
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html#IDToken" target="_blank">ID Token</a>
* @since 1.0.1
*/
public class UserInfo implements Serializable {
/**
* string Subject - Identifier for the End-User at the Issuer.
*/
private String id;
/**
* string Subject - Identifier for the End-User at the Issuer.
*/
private String sub;
/**
* string End-User's full name in displayable form including all name parts, possibly including titles and suffixes,
* ordered according to the End-User's locale and preferences.
*/
private String name;
/**
* string End-User's full name in displayable form including all name parts, possibly including titles and suffixes,
* ordered according to the End-User's locale and preferences.
*/
private String username;
/**
* string Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names;
* all can be present, with the names being separated by space characters.
*/
private String given_name;
/**
* string Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name;
* all can be present, with the names being separated by space characters.
*/
private String family_name;
/**
* string Middle name(s) of the End-User. Note that in some cultures, people can have multiple middle names;
* all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used.
*/
private String middle_name;
/**
* string Casual name of the End-User that may or may not be the same as the given_name. For instance,
* a nickname value of Mike might be returned alongside a given_name value of Michael.
*/
private String nickname;
/**
* string Shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe.
* This value MAY be any valid JSON string including special characters such as @, /, or whitespace.
* The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.
*/
private String preferred_username;
/**
* string URL of the End-User's profile page. The contents of this Web page SHOULD be about the End-User.
*/
private String profile;
/**
* string URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG,
* or GIF image file), rather than to a Web page containing an image.
* Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User,
* rather than an arbitrary photo taken by the End-User.
*/
private String picture;
/**
* string URL of the End-User's Web page or blog. This Web page SHOULD contain information published by the End-User
* or an organization that the End-User is affiliated with.
*/
private String website;
/**
* string End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 [RFC5322] addr-spec syntax.
* The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.
*/
private String email;
/**
* boolean True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true,
* this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed.
* The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.
*/
private String email_verified;
/**
* string End-User's gender. Values defined by this specification are female and male.
* Other values MAY be used when neither of the defined values are applicable.
*/
private String gender;
/**
* string End-User's birthday, represented as an ISO 8601:2004 [ISO86012004] YYYY-MM-DD format. The year MAY be 0000,
* indicating that it is omitted. To represent only the year, YYYY format is allowed.
* Note that depending on the underlying platform's date related function, providing just year can result in varying month and day,
* so the implementers need to take this factor into account to correctly process the dates.
*/
private String birthdate;
/**
* string String from zoneinfo [zoneinfo] time zone database representing the End-User's time zone. For example,
* Europe/Paris or America/Los_Angeles.
*/
private String zoneinfo;
/**
* string End-User's locale, represented as a BCP47 [RFC5646] language tag.
* This is typically an ISO 639-1 Alpha-2 [ISO6391] language code in lowercase and an ISO 3166-1 Alpha-2 [ISO31661] country code in uppercase,
* separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash,
* for example, en_US; Relying Parties MAY choose to accept this locale syntax as well.
*/
private String locale;
/**
* string End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of this Claim,
* for example, +1 (425) 555-1212 or +56 (2) 687 2400. If the phone number contains an extension,
* it is RECOMMENDED that the extension be represented using the RFC 3966 [RFC3966] extension syntax,
* for example, +1 (604) 555-1234;ext=5678.
*/
private String phone_number;
/**
* boolean True if the End-User's phone number has been verified; otherwise false. When this Claim Value is true,
* this means that the OP took affirmative steps to ensure that this phone number was controlled by the End-User at the time the verification was performed.
* The means by which a phone number is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.
* When true, the phone_number Claim MUST be in E.164 format and any extensions MUST be represented in RFC 3966 format.
*/
private String phone_number_verified;
/**
* JSON object End-User's preferred postal address. The value of the address member is a JSON [RFC4627] structure containing some or all of the members defined in Section 5.1.1.
*/
private Map<String, String> address;
private String updated_at;
public String getId() {
return id;
}
public UserInfo setId(String id) {
this.id = id;
return this;
}
public String getSub() {
return StringUtil.isEmpty(sub) ? getId() : sub;
}
public UserInfo setSub(String sub) {
this.sub = sub;
return this;
}
public String getName() {
return name;
}
public UserInfo setName(String name) {
this.name = name;
return this;
}
public String getUsername() {
return username;
}
public UserInfo setUsername(String username) {
this.username = username;
return this;
}
public String getGiven_name() {
return given_name;
}
public UserInfo setGiven_name(String given_name) {
this.given_name = given_name;
return this;
}
public String getFamily_name() {
return family_name;
}
public UserInfo setFamily_name(String family_name) {
this.family_name = family_name;
return this;
}
public String getMiddle_name() {
return middle_name;
}
public UserInfo setMiddle_name(String middle_name) {
this.middle_name = middle_name;
return this;
}
public String getNickname() {
return nickname;
}
public UserInfo setNickname(String nickname) {
this.nickname = nickname;
return this;
}
public String getPreferred_username() {
return preferred_username;
}
public UserInfo setPreferred_username(String preferred_username) {
this.preferred_username = preferred_username;
return this;
}
public String getProfile() {
return profile;
}
public UserInfo setProfile(String profile) {
this.profile = profile;
return this;
}
public String getPicture() {
return picture;
}
public UserInfo setPicture(String picture) {
this.picture = picture;
return this;
}
public String getWebsite() {
return website;
}
public UserInfo setWebsite(String website) {
this.website = website;
return this;
}
public String getEmail() {
return email;
}
public UserInfo setEmail(String email) {
this.email = email;
return this;
}
public String getEmail_verified() {
return email_verified;
}
public UserInfo setEmail_verified(String email_verified) {
this.email_verified = email_verified;
return this;
}
public String getGender() {
return gender;
}
public UserInfo setGender(String gender) {
this.gender = gender;
return this;
}
public String getBirthdate() {
return birthdate;
}
public UserInfo setBirthdate(String birthdate) {
this.birthdate = birthdate;
return this;
}
public String getZoneinfo() {
return zoneinfo;
}
public UserInfo setZoneinfo(String zoneinfo) {
this.zoneinfo = zoneinfo;
return this;
}
public String getLocale() {
return locale;
}
public UserInfo setLocale(String locale) {
this.locale = locale;
return this;
}
public String getPhone_number() {
return phone_number;
}
public UserInfo setPhone_number(String phone_number) {
this.phone_number = phone_number;
return this;
}
public String getPhone_number_verified() {
return phone_number_verified;
}
public UserInfo setPhone_number_verified(String phone_number_verified) {
this.phone_number_verified = phone_number_verified;
return this;
}
public Map<String, String> getAddress() {
return address;
}
public UserInfo setAddress(Map<String, String> address) {
this.address = address;
return this;
}
public String getUpdated_at() {
return updated_at;
}
public UserInfo setUpdated_at(String updated_at) {
this.updated_at = updated_at;
return this;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model.enums;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* The client secret authentication method supports the following four situations:
* <p>
* 1. Post parameter: {@link ClientSecretAuthMethod#CLIENT_SECRET_POST}
* <p>
* 2. The basic format string in the request header:{@link ClientSecretAuthMethod#CLIENT_SECRET_BASIC}
* <p>
* 3. url: {@link ClientSecretAuthMethod#NONE}
* <p>
* 4. All of the above support: {@link ClientSecretAuthMethod#ALL}
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public enum ClientSecretAuthMethod {
/**
* Post parameter
*/
CLIENT_SECRET_POST,
/**
* The basic format string in the request header
*/
CLIENT_SECRET_BASIC,
/**
* url
*/
NONE,
/**
* All of the above support
*/
ALL;
public static List<String> getAllMethods() {
return Arrays.stream(ClientSecretAuthMethod.values())
.filter((method) -> method != ALL)
.map((method) -> method.name().toLowerCase())
.collect(Collectors.toList());
}
public String getMethod() {
return this.name().toLowerCase();
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model.enums;
import com.xkcoding.json.util.StringUtil;
/**
* Authorization error code
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @see <a href="https://tools.ietf.org/html/rfc6749#section-4.1.2.1" target="_blank">https://tools.ietf.org/html/rfc6749#section-4.1.2.1</a>
* @see <a href="https://tools.ietf.org/html/rfc6749#section-5.2" target="_blank">https://tools.ietf.org/html/rfc6749#section-5.2</a>
* @since 1.0.1
*/
public enum ErrorResponse {
/**
* Authorization error code
*/
INVALID_REQUEST("invalid_request", "Request parameters are missing or not supported or incorrect."),
INVALID_CLIENT("invalid_client", "The requested client id or client secret parameter is invalid."),
INVALID_GRANT("invalid_grant", "Invalid grant, request parameters are incorrect or expired."),
INVALID_CODE("invalid_code", "The authorization code is invalid or expired."),
INVALID_USER_CERTIFICATE("invalid_user_certificate", "Invalid user credentials."),
INVALID_USER_STATUS("invalid_user_status", "Invalid user status, the user may have logged out."),
INVALID_JWKS("invalid_jwks", "Invalid jwks json. Please check if `IdsConfig.JwtConfig.jwksJson` is configured correctly."),
INVALID_CODE_CHALLENGE("invalid_code_challenge", "Illegal request, code challenge verification failed."),
INVALID_TOKEN("invalid_token", "Invalid token (access token, refresh token, or id token)."),
INVALID_SCOPE("invalid_scope", "The requested scope parameter is invalid, unknown, or the requested permission scope exceeds the permission scope granted by the data owner."),
INVALID_REDIRECT_URI("invalid_redirect_uri", "The requested callback URL is incorrect."),
UNSUPPORTED_GRANT_TYPE("unsupported_grant_type", "The grant type is not supported by the authorization server, or the current client is not authorized for the grant type."),
UNSUPPORTED_RESPONSE_TYPE("unsupported_response_type", "The response type is not supported by the authorization server, or the current client does not allow the response type."),
ACCESS_DENIED("access_denied", "The authorization server rejected the current request。"),
SERVER_ERROR("server_error", "The authorization server is temporarily unavailable. Please try again later."),
AUTHORIZATION_FAILED("authorization_failed", "Authorization failed, please contact the systems administrator."),
EXPIRED_TOKEN("expired_token", "The requested token has expired (access token, refresh token, or id token)."),
DISABLED_CLIENT("disabled_client", "The client is not accessible and may have been disabled by the administrator."),
;
private final String error;
private final String errorDescription;
ErrorResponse(String error, String errorDescription) {
this.error = error;
this.errorDescription = errorDescription;
}
public static ErrorResponse getByError(String error) {
if (StringUtil.isEmpty(error)) {
return AUTHORIZATION_FAILED;
}
ErrorResponse[] errorResponses = ErrorResponse.values();
for (ErrorResponse errorResponse : errorResponses) {
if (errorResponse.getError().equalsIgnoreCase(error)) {
return errorResponse;
}
}
return AUTHORIZATION_FAILED;
}
public String getError() {
return error;
}
public String getErrorDescription() {
return errorDescription;
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model.enums;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* oauth grant type
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public enum GrantType {
/**
* Authorization code grant
*
* @see <a href="https://tools.ietf.org/html/rfc6749#section-1.3.1" target="_blank">RFC 6749 (OAuth 2.0), 1.3.1. Authorization Code</a>
* @see <a href="http://tools.ietf.org/html/rfc6749#section-4.1.3" target="_blank">RFC 6749 (OAuth 2.0), 4.1.3. Access Token Request</a>
* @see <a href="http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint" target="_blank">OpenID Connect Core 1.0, 3.1.3. Token Endpoint</a>
*/
AUTHORIZATION_CODE("authorization_code"),
/**
* The implicit grant is a simplified authorization code flow optimized for clients implemented in a browser using a scripting language such as JavaScript.
*
* @see <a href="https://tools.ietf.org/html/rfc6749#section-1.3.2" target="_blank">RFC 6749 (OAuth 2.0), 1.3.2. Implicit</a>
*/
IMPLICIT("implicit"),
/**
* The resource owner password credentials (i.e., username and password) can be used directly as an authorization grant to obtain an access token.
* The credentials should only be used when there is a <strong>high degree of trust between the resource owner and the client</strong>
* (e.g., the client is part of the device operating system or a highly privileged application),
* and when other authorization grant types are <strong>not available</strong> (such as an authorization code).
*
* @see <a href="https://tools.ietf.org/html/rfc6749#section-1.3.3" target="_blank">RFC 6749 (OAuth 2.0), 1.3.3. Resource Owner Password Credentials</a>
* @see <a href="http://tools.ietf.org/html/rfc6749#section-4.3.2" target="_blank">RFC 6749 (OAuth 2.0), 4.3.2. Access Token Request</a>
*/
PASSWORD("password"),
/**
* Client Credentials grant
*
* @see <a href="https://tools.ietf.org/html/rfc6749#section-1.3.4" target="_blank">RFC 6749 (OAuth 2.0), 1.3.4. Client Credentials</a>
* @see <a href="http://tools.ietf.org/html/rfc6749#section-4.4.2" target="_blank">RFC 6749 (OAuth 2.0), 4.4.2. Access Token Request</a>
*/
CLIENT_CREDENTIALS("client_credentials"),
/**
* The grant type used when refreshing the token
*
* @see <a href="http://tools.ietf.org/html/rfc6749#section-6" target="_blank">RFC 6749 (OAuth 2.0), 6. Refreshing an Access Token</a>
* @see <a href="http://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens" target="_blank">OpenID Connect Core 1.0, 12. Using Refresh Tokens</a>
*/
REFRESH_TOKEN("refresh_token"),
/**
* The grant type used when obtaining the access token
*/
TOKEN("token");
private final String type;
GrantType(String type) {
this.type = type;
}
public static List<String> grantTypes() {
return Arrays.stream(GrantType.values())
.map(GrantType::getType)
.collect(Collectors.toList());
}
public String getType() {
return type;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model.enums;
import com.fujieid.jap.ids.config.IdsConfig;
/**
* The verification type when the user verifies the jwt token (access token, refresh token, id token)
* For specific usage, please refer to {@link com.fujieid.jap.ids.util.JwtUtil#validateJwtToken(String, String, String, IdsConfig)}
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public enum JwtVerificationType {
/**
* Using an HTTPS JWKS endpoint
*/
HTTPS_JWKS_ENDPOINT,
/**
* Using JWKs
*/
JWKS
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model.enums;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Response Type
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.1
*/
public enum ResponseType {
/**
* https://tools.ietf.org/html/rfc6749#section-3.1.1
* https://tools.ietf.org/html/rfc6749#section-4.1.1
*/
CODE("code"),
/**
* "token" for requesting an access token (implicit grant) as described
* https://tools.ietf.org/html/rfc6749#section-4.2.1
*/
TOKEN("token"),
/**
* a registered extension value as described by Section 8.4.
* https://tools.ietf.org/html/rfc6749#section-8.4
*/
ID_TOKEN("id_token"),
ID_TOKEN_TOKEN("id_token token"),
CODE_ID_TOKEN("code id_token"),
CODE_TOKEN("code token"),
CODE_ID_TOKEN_TOKEN("code id_token token"),
/**
* https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#none
*/
NONE("none");
private final String type;
ResponseType(String type) {
this.type = type;
}
public static List<String> responseTypes() {
return Arrays.stream(ResponseType.values())
.map(ResponseType::getType)
.collect(Collectors.toList());
}
public String getType() {
return type;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model.enums;
/**
* When accessing the api, the token usage mode supports the following four situations:
* <p>
* 1. Set {@code bearer access token} in the request header: {@link TokenAuthMethod#TOKEN_HEADER}
* <p>
* 2. Set access token in cookie: {@link TokenAuthMethod#TOKEN_COOKIE}
* <p>
* 3. url: {@link TokenAuthMethod#TOKEN_URL}
* <p>
* 4. All of the above support:{@link TokenAuthMethod#ALL}
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public enum TokenAuthMethod {
/**
* request header
*/
TOKEN_HEADER,
/**
* cookie
*/
TOKEN_COOKIE,
/**
* url
*/
TOKEN_URL,
/**
* 支持全部
*/
ALL
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.model.enums;
import org.jose4j.keys.EcKeyUtil;
import org.jose4j.keys.RsaKeyUtil;
/**
* jwt token encryption algorithm, Supports two types of algorithms, RSA and EC
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public enum TokenSigningAlg {
/**
* RS256
*/
RS256("RS256", RsaKeyUtil.RSA),
RS384("RS384", RsaKeyUtil.RSA),
RS512("RS512", RsaKeyUtil.RSA),
ES256("ES256", EcKeyUtil.EC),
ES384("ES384", EcKeyUtil.EC),
ES512("ES512", EcKeyUtil.EC),
;
private final String alg;
private final String keyType;
TokenSigningAlg(String alg, String keyType) {
this.alg = alg;
this.keyType = keyType;
}
public String getAlg() {
return alg;
}
public String getKeyType() {
return keyType;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* ids model
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.model;

View File

@ -0,0 +1,209 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.oidc;
import java.io.Serializable;
/**
* According to standard specifications, construct id token
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html#IDToken" target="_blank">ID Token</a>
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims" target="_blank">Standard Claims</a>
* @since 1.0.1
*/
public class IdToken implements Serializable {
/**
* {@code iss} - the Issuer identifier
*/
private String iss;
/**
* {@code sub} - the Subject identifier
*/
private String sub;
/**
* {@code aud} - the Audience(s) that the ID Token is intended for
*/
private String aud;
/**
* {@code exp} - the Expiration time on or after which the ID Token MUST NOT be accepted
*/
private Long exp;
/**
* {@code iat} - the time at which the ID Token was issued
*/
private Long iat;
/**
* {@code auth_time} - the time when the End-User authentication occurred
*/
private String auth_time;
/**
* {@code nonce} - a {@code String} value used to associate a Client session with an ID Token,
* and to mitigate replay attacks.
*/
private String nonce;
/**
* {@code acr} - the Authentication Context Class Reference
*/
private String acr;
/**
* {@code amr} - the Authentication Methods References
*/
private String amr;
/**
* {@code azp} - the Authorized party to which the ID Token was issued
*/
private String azp;
/**
* {@code at_hash} - the Access Token hash value
*/
private String at_hash;
/**
* {@code c_hash} - the Authorization Code hash value
*/
private String c_hash;
private Object extra;
public String getIss() {
return iss;
}
public IdToken setIss(String iss) {
this.iss = iss;
return this;
}
public String getSub() {
return sub;
}
public IdToken setSub(String sub) {
this.sub = sub;
return this;
}
public String getAud() {
return aud;
}
public IdToken setAud(String aud) {
this.aud = aud;
return this;
}
public Long getExp() {
return exp;
}
public IdToken setExp(Long exp) {
this.exp = exp;
return this;
}
public Long getIat() {
return iat;
}
public IdToken setIat(Long iat) {
this.iat = iat;
return this;
}
public String getAuth_time() {
return auth_time;
}
public IdToken setAuth_time(String auth_time) {
this.auth_time = auth_time;
return this;
}
public String getNonce() {
return nonce;
}
public IdToken setNonce(String nonce) {
this.nonce = nonce;
return this;
}
public String getAcr() {
return acr;
}
public IdToken setAcr(String acr) {
this.acr = acr;
return this;
}
public String getAmr() {
return amr;
}
public IdToken setAmr(String amr) {
this.amr = amr;
return this;
}
public String getAzp() {
return azp;
}
public IdToken setAzp(String azp) {
this.azp = azp;
return this;
}
public String getAt_hash() {
return at_hash;
}
public IdToken setAt_hash(String at_hash) {
this.at_hash = at_hash;
return this;
}
public String getC_hash() {
return c_hash;
}
public IdToken setC_hash(String c_hash) {
this.c_hash = c_hash;
return this;
}
public Object getExtra() {
return extra;
}
public IdToken setExtra(Object extra) {
this.extra = extra;
return this;
}
}

View File

@ -0,0 +1,128 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.oidc;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.config.IdsConfig;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.OidcDiscoveryDto;
import com.fujieid.jap.ids.model.enums.ClientSecretAuthMethod;
import com.fujieid.jap.ids.model.enums.GrantType;
import com.fujieid.jap.ids.model.enums.ResponseType;
import com.fujieid.jap.ids.provider.IdsScopeProvider;
import com.fujieid.jap.ids.util.JwtUtil;
import com.fujieid.jap.ids.util.ObjectUtils;
import com.xkcoding.json.util.StringUtil;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jwt.ReservedClaimNames;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class OidcUtil {
public static OidcDiscoveryDto getOidcDiscoveryInfo(String identity, IdsConfig config) {
List<String> scopes = IdsScopeProvider.getScopeCodes();
Map<String, Object> model = new HashMap<>();
String issuer = config.getIssuer();
model.put("issuer", issuer);
model.put("authorization_endpoint", ObjectUtils.appendIfNotEndWith(config.getAuthorizeUrl(), identity));
model.put("token_endpoint", ObjectUtils.appendIfNotEndWith(config.getTokenUrl(), identity));
model.put("userinfo_endpoint", ObjectUtils.appendIfNotEndWith(config.getUserinfoUrl(), identity));
model.put("registration_endpoint", ObjectUtils.appendIfNotEndWith(config.getRegistrationUrl(), identity));
model.put("end_session_endpoint", ObjectUtils.appendIfNotEndWith(config.getEndSessionUrl(), identity));
model.put("check_session_iframe", ObjectUtils.appendIfNotEndWith(config.getCheckSessionUrl(), identity));
model.put("jwks_uri", ObjectUtils.appendIfNotEndWith(config.getJwksUrl(), identity));
model.put("grant_types_supported", GrantType.grantTypes());
model.put("response_modes_supported", Arrays.asList(
"fragment",
"query"));
model.put("response_types_supported", ResponseType.responseTypes());
model.put("scopes_supported", scopes);
List<ClientSecretAuthMethod> clientSecretAuthMethods = config.getClientSecretAuthMethods();
if (ObjectUtil.isEmpty(clientSecretAuthMethods)) {
clientSecretAuthMethods = Collections.singletonList(ClientSecretAuthMethod.ALL);
}
if (clientSecretAuthMethods.contains(ClientSecretAuthMethod.ALL)) {
model.put("token_endpoint_auth_methods_supported", ClientSecretAuthMethod.getAllMethods());
} else {
model.put("token_endpoint_auth_methods_supported", clientSecretAuthMethods
.stream()
.map(ClientSecretAuthMethod::getMethod)
.collect(Collectors.toList()));
}
model.put("request_object_signing_alg_values_supported", Arrays.asList(
"none",
"RS256",
"ES256"
)
);
model.put("userinfo_signing_alg_values_supported", Arrays.asList(
"RS256",
"ES256"
)
);
model.put("request_parameter_supported", true);
model.put("request_uri_parameter_supported", true);
model.put("require_request_uri_registration", false);
model.put("claims_parameter_supported", true);
model.put("id_token_signing_alg_values_supported", Arrays.asList(
"RS256",
"ES256"
)
);
model.put("subject_types_supported", Collections.singletonList("public"));
model.put("claims_supported", Arrays.asList(
ReservedClaimNames.ISSUER,
ReservedClaimNames.SUBJECT,
ReservedClaimNames.AUDIENCE,
ReservedClaimNames.EXPIRATION_TIME,
ReservedClaimNames.ISSUED_AT,
IdsConsts.NONCE,
IdsConsts.AUTH_TIME,
IdsConsts.USERNAME
));
model.put("code_challenge_methods_supported", Arrays.asList(
"PLAIN",
"S256"
));
return BeanUtil.mapToBean(model, OidcDiscoveryDto.class, false, null);
}
public static String getJwksPublicKey(String identity, IdsConfig idsConfig) {
String jwksJson = null;
if (StringUtil.isNotEmpty(identity)) {
jwksJson = JapIds.getContext().getIdentityService().getJwksJson(identity);
} else {
jwksJson = idsConfig.getJwtConfig().getJwksJson();
}
JsonWebKeySet jsonWebKeySet = JwtUtil.IdsVerificationKeyResolver.createJsonWebKeySet(jwksJson);
return jsonWebKeySet.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Support openid connect
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.oidc;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
/** /**
* Authorization service based on rfc6749 protocol specification and OpenID Connect Core 1.0 specification * Authorization service based on RFC6749 protocol specification and OpenID Connect Core 1.0 specification
* <p> * <p>
* Features * Features
* <p> * <p>
@ -43,6 +43,7 @@
* @see <a href="https://tools.ietf.org/html/rfc6749" target="_blank"> The OAuth 2.0 Authorization Framework</a> * @see <a href="https://tools.ietf.org/html/rfc6749" target="_blank"> The OAuth 2.0 Authorization Framework</a>
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html" target="_blank">OpenID Connect Core 1.0 incorporating errata set 1</a> * @see <a href="https://openid.net/specs/openid-connect-core-1_0.html" target="_blank">OpenID Connect Core 1.0 incorporating errata set 1</a>
* @see <a href="https://tools.ietf.org/html/rfc7636" target="_blank">Proof Key for Code Exchange by OAuth Public Clients</a> * @see <a href="https://tools.ietf.org/html/rfc7636" target="_blank">Proof Key for Code Exchange by OAuth Public Clients</a>
* @see <a href="https://tools.ietf.org/html/rfc7033" target="_blank">WebFinger</a>
* @see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html" target="_blank">OpenID Connect Discovery 1.0 incorporating errata set 1</a> * @see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html" target="_blank">OpenID Connect Discovery 1.0 incorporating errata set 1</a>
* @see <a href="https://openid.net/specs/openid-connect-frontchannel-1_0.html" target="_blank">OpenID Connect Front-Channel Logout 1.0</a> * @see <a href="https://openid.net/specs/openid-connect-frontchannel-1_0.html" target="_blank">OpenID Connect Front-Channel Logout 1.0</a>
* @see <a href="https://openid.net/specs/openid-connect-backchannel-1_0.html" target="_blank">OpenID Connect Back-Channel Logout 1.0</a> * @see <a href="https://openid.net/specs/openid-connect-backchannel-1_0.html" target="_blank">OpenID Connect Back-Channel Logout 1.0</a>

View File

@ -0,0 +1,177 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.provider;
import com.fujieid.jap.ids.model.*;
import com.fujieid.jap.ids.service.Oauth2Service;
import com.fujieid.jap.ids.util.OauthUtil;
import com.fujieid.jap.ids.util.ObjectUtils;
import com.fujieid.jap.ids.util.TokenUtil;
import com.xkcoding.json.util.StringUtil;
import java.util.HashMap;
import java.util.Map;
/**
* Authorize the endpoint to create a callback url, and pass different callback parameters according to the request parameters
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsAuthorizationProvider {
private final Oauth2Service oauth2Service;
public IdsAuthorizationProvider(Oauth2Service oauth2Service) {
this.oauth2Service = oauth2Service;
}
/**
* 4.2. Implicit Grant
*
* @param userInfo Logged-in user information
* @param param Request parameter
* @param clientDetail Application information
* @return String
* @see <a href="https://tools.ietf.org/html/rfc6749#section-4.2">4.2. Implicit Grant</a>
*/
public String generateImplicitGrantResponse(UserInfo userInfo, IdsRequestParam param, ClientDetail clientDetail) {
AccessToken accessToken = TokenUtil.createAccessToken(userInfo, clientDetail, param.getGrantType(), param.getScope(), param.getNonce());
Map<String, String> tokenResponse = new HashMap<>(9);
// https://tools.ietf.org/html/rfc6749#section-4.2.2
// The authorization server MUST NOT issue a refresh token.
tokenResponse.put(IdsConsts.ACCESS_TOKEN, accessToken.getAccessToken());
tokenResponse.put(IdsConsts.EXPIRES_IN, String.valueOf(OauthUtil.getAccessTokenExpiresIn(clientDetail.getAccessTokenExpiresIn())));
tokenResponse.put(IdsConsts.TOKEN_TYPE, IdsConsts.TOKEN_TYPE_BEARER);
tokenResponse.put(IdsConsts.SCOPE, param.getScope());
if (OauthUtil.isOidcProtocol(param.getScope())) {
tokenResponse.put(IdsConsts.ID_TOKEN, TokenUtil.createIdToken(clientDetail, userInfo, param.getNonce()));
}
if (StringUtil.isNotEmpty(param.getState())) {
tokenResponse.put(IdsConsts.STATE, param.getState());
}
String params = ObjectUtils.parseMapToString(tokenResponse, false);
return param.getRedirectUri() + "?" + params;
}
/**
* 4.1. Authorization Code Grant
*
* @param userInfo Logged-in user information
* @param param Request parameter
* @param clientDetail Application information
* @return String
* @see <a href="https://tools.ietf.org/html/rfc6749#section-4.1">4.1. Authorization Code Grant</a>
*/
public String generateAuthorizationCodeResponse(UserInfo userInfo, IdsRequestParam param, ClientDetail clientDetail) {
String authorizationCode = oauth2Service.createAuthorizationCode(param, userInfo, OauthUtil.getCodeExpiresIn(clientDetail.getCodeExpiresIn()));
String params = "?code=" + authorizationCode;
if (StringUtil.isNotEmpty(param.getState())) {
params = params + "&state=" + param.getState();
}
return param.getRedirectUri() + params;
}
/**
* When the value of {@code response_type} is {@code code id_token}, return {@code code} and {@code id_token} from the authorization endpoint
*
* @param userInfo Logged-in user information
* @param param Request parameter
* @param clientDetail Application information
* @return String
* @see <a href="https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Combinations">Definitions of Multiple-Valued Response Type Combinations</a>
*/
public String generateCodeIdTokenAuthorizationResponse(UserInfo userInfo, IdsRequestParam param, ClientDetail clientDetail) {
String params = "&id_token=" + TokenUtil.createIdToken(clientDetail, userInfo, param.getNonce());
return this.generateAuthorizationCodeResponse(userInfo, param, clientDetail) + params;
}
/**
* When the value of {@code response_type} is {@code id_token}, an {@code id_token} is returned from the authorization endpoint.
* This mode does not require the use of token endpoints.
*
* @param userInfo Logged-in user information
* @param param Request parameter
* @param clientDetail Application information
* @return String
*/
public String generateIdTokenAuthorizationResponse(UserInfo userInfo, IdsRequestParam param, ClientDetail clientDetail) {
String params = "?id_token=" + TokenUtil.createIdToken(clientDetail, userInfo, param.getNonce());
return param.getRedirectUri() + params;
}
/**
* When the value of {@code response_type} is {@code id_token token}, the {@code id_token} and {@code access_token} are returned from the authorization endpoint.
* This mode does not require the use of token endpoints.
*
* @param userInfo Logged-in user information
* @param param Request parameter
* @param clientDetail Application information
* @return String
* @see <a href="https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Combinations">Definitions of Multiple-Valued Response Type Combinations</a>
*/
public String generateIdTokenTokenAuthorizationResponse(UserInfo userInfo, IdsRequestParam param, ClientDetail clientDetail) {
AccessToken accessToken = TokenUtil.createAccessToken(userInfo, clientDetail, param.getGrantType(), param.getScope(), param.getNonce());
String params = "?access_token=" + accessToken.getAccessToken() + "&id_token=" + TokenUtil.createIdToken(clientDetail, userInfo, param.getNonce());
return param.getRedirectUri() + params;
}
/**
* When the value of {@code response_type} is {@code code token}, return {@code code} and {@code token} from the authorization endpoint
*
* @param userInfo Logged-in user information
* @param param Request parameter
* @param clientDetail Application information
* @return String
* @see <a href="https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Combinations">Definitions of Multiple-Valued Response Type Combinations</a>
*/
public String generateCodeTokenAuthorizationResponse(UserInfo userInfo, IdsRequestParam param, ClientDetail clientDetail) {
AccessToken accessToken = TokenUtil.createAccessToken(userInfo, clientDetail, param.getGrantType(), param.getScope(), param.getNonce());
String params = "&access_token=" + accessToken.getAccessToken();
return this.generateAuthorizationCodeResponse(userInfo, param, clientDetail) + params;
}
/**
* When the value of {@code response_type} is {@code code id_token token}, return {@code code},{@code id_token} and {@code token} from the authorization endpoint
*
* @param userInfo Logged-in user information
* @param param Request parameter
* @param clientDetail Application information
* @return String
* @see <a href="https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Combinations">Definitions of Multiple-Valued Response Type Combinations</a>
*/
public String generateCodeIdTokenTokenAuthorizationResponse(UserInfo userInfo, IdsRequestParam param, ClientDetail clientDetail) {
String params = "&id_token=" + TokenUtil.createIdToken(clientDetail, userInfo, param.getNonce());
return this.generateCodeTokenAuthorizationResponse(userInfo, param, clientDetail) + params;
}
/**
* When the value of {@code response_type} is {@code none}, the {@code code},{@code id_token} and {@code token} is not returned from the authorization endpoint,
* if there is a state, it is returned as it is.
*
* @param param Request parameter
* @return String
* @see <a href="https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#none">None Response Type</a>
*/
public String generateNoneAuthorizationResponse(IdsRequestParam param) {
String params = "";
if (!StringUtil.isEmpty(param.getState())) {
params = "?state=" + param.getState();
}
return param.getRedirectUri() + params;
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.provider;
import cn.hutool.core.util.ObjectUtil;
import com.fujieid.jap.ids.exception.InvalidRequestException;
import com.fujieid.jap.ids.model.ClientCertificate;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.IdsRequestParam;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.util.ClientCertificateUtil;
import javax.servlet.http.HttpServletRequest;
/**
* Parameter parser for oauth request
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsRequestParamProvider {
public static IdsRequestParam parseRequest(HttpServletRequest request) {
if (ObjectUtil.isEmpty(request.getParameterMap())) {
throw new InvalidRequestException(ErrorResponse.INVALID_REQUEST);
}
IdsRequestParam param = new IdsRequestParam();
ClientCertificate clientCertificate = ClientCertificateUtil.getClientCertificate(request);
param.setClientId(clientCertificate.getId());
param.setClientSecret(clientCertificate.getSecret());
param.setCode(request.getParameter(IdsConsts.CODE));
param.setRedirectUri(request.getParameter(IdsConsts.REDIRECT_URI));
param.setScope(request.getParameter(IdsConsts.SCOPE));
param.setState(request.getParameter(IdsConsts.STATE));
param.setGrantType(request.getParameter(IdsConsts.GRANT_TYPE));
param.setAccessToken(request.getParameter(IdsConsts.ACCESS_TOKEN));
param.setRefreshToken(request.getParameter(IdsConsts.REFRESH_TOKEN));
param.setResponseType(request.getParameter(IdsConsts.RESPONSE_TYPE));
param.setNonce(request.getParameter(IdsConsts.NONCE));
param.setUid(request.getParameter(IdsConsts.UID));
param.setResponseMode(request.getParameter(IdsConsts.RESPONSE_MODE));
param.setDisplay(request.getParameter(IdsConsts.DISPLAY));
param.setPrompt(request.getParameter(IdsConsts.PROMPT));
param.setAuthTime(request.getParameter(IdsConsts.MAX_AGE));
param.setIdTokenHint(request.getParameter(IdsConsts.ID_TOKEN_HINT));
param.setAcr(request.getParameter(IdsConsts.ACR));
param.setAutoapprove(request.getParameter(IdsConsts.AUTOAPPROVE));
// Get username and password Applies to:<a href="https://tools.ietf.org/html/rfc6749#section-4.3" target="_blank">Resource Owner Password Credentials Grant</a>
param.setUsername(request.getParameter(IdsConsts.USERNAME));
param.setPassword(request.getParameter(IdsConsts.PASSWORD));
/*
* Applicable to open pkce enhanced protocol in authorization code mode
* @see <a href="https://tools.ietf.org/html/rfc7636" target="_blank">Proof Key for Code Exchange by OAuth Public Clients</a>
*/
param.setCodeVerifier(request.getParameter(IdsConsts.CODE_VERIFIER));
param.setCodeChallengeMethod(request.getParameter(IdsConsts.CODE_CHALLENGE_METHOD));
param.setCodeChallenge(request.getParameter(IdsConsts.CODE_CHALLENGE));
return param;
}
}

View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.provider;
import cn.hutool.core.util.ObjectUtil;
import com.fujieid.jap.ids.model.IdsScope;
import java.util.*;
import java.util.stream.Collectors;
/**
* Define and manage the scope used in oauth authorization.
* Read, write, openid, email, and phone are supported by default.
* If developers need to modify the scope, they can use the following methods:
* <p>
* {@link com.fujieid.jap.ids.provider.IdsScopeProvider#initScopes(List)}
* <p>
* {@link com.fujieid.jap.ids.provider.IdsScopeProvider#addScope(IdsScope)}
* <p>
* Note: Whether it is a custom scope or a system built-in scope, openid must be included, which is a required option for enabling OIDC
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsScopeProvider {
private static List<IdsScope> scopes = new ArrayList<>();
static {
addScope(new IdsScope().setCode("read").setDescription("Allows to read resources, including users, protected resources, etc."));
addScope(new IdsScope().setCode("write").setDescription("Allows to modify resources, including adding, deleting, and modifying resources such as users and protected resources."));
addScope(new IdsScope().setCode("openid").setDescription("OpenID connect must include scope"));
addScope(new IdsScope().setCode("email").setDescription("Allow access to user's mailbox"));
addScope(new IdsScope().setCode("phone").setDescription("Allow access to the users phone number"));
}
/**
* Initialize scope
* <p>
* Note: This method will reset the existing scope collection
*
* @param idsScopes scope collection
*/
public static void initScopes(List<IdsScope> idsScopes) {
if (ObjectUtil.isNotEmpty(idsScopes)) {
scopes = idsScopes;
}
}
/**
* Add a single scope.
* Note: This method is to add data to the existing scope collection
*
* @param idsScope single scope
*/
public static void addScope(IdsScope idsScope) {
scopes.add(idsScope);
}
/**
* Return the set of available scopes after deduplication according to the scope code
*
* @return Unique scope collection
*/
public static List<IdsScope> getScopes() {
return scopes.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(
IdsScope::getCode))), ArrayList::new));
}
/**
* Obtain the scope collection through scope code (does not contain duplicates)
*
* @param codes scope code
* @return Unique scope collection
*/
public static List<IdsScope> getScopeByCodes(Collection<String> codes) {
if (ObjectUtil.isEmpty(codes)) {
return new ArrayList<>(0);
}
return Optional.ofNullable(scopes.stream().filter((scope) -> codes.contains(scope.getCode()))
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(
IdsScope::getCode))), ArrayList::new)))
.orElse(new ArrayList<>(0));
}
/**
* Get the code of all scopes
*
* @return code of all scopes
*/
public static List<String> getScopeCodes() {
return scopes.stream().map(IdsScope::getCode).distinct().collect(Collectors.toList());
}
}

View File

@ -0,0 +1,202 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.provider;
import cn.hutool.core.util.ObjectUtil;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.IdsException;
import com.fujieid.jap.ids.exception.InvalidClientException;
import com.fujieid.jap.ids.exception.InvalidRequestException;
import com.fujieid.jap.ids.model.*;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.model.enums.GrantType;
import com.fujieid.jap.ids.service.Oauth2Service;
import com.fujieid.jap.ids.util.OauthUtil;
import com.fujieid.jap.ids.util.TokenUtil;
import com.xkcoding.json.util.StringUtil;
/**
* The token endpoint creates a token, and returns different token information for different authorization types
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class IdsTokenProvider {
private final Oauth2Service oauth2Service;
public IdsTokenProvider(Oauth2Service oauth2Service) {
this.oauth2Service = oauth2Service;
}
/**
* RFC6749 4.1. authorization code grant
*
* @param param request params
* @return IdsResponse
* @see <a href="https://tools.ietf.org/html/rfc6749#section-4.1" target="_blank">4.1. Authorization Code Grant</a>
*/
public IdsResponse<String, Object> generateAuthorizationCodeResponse(IdsRequestParam param) {
AuthCode codeInfo = oauth2Service.validateAndGetAuthrizationCode(param.getGrantType(), param.getCode());
String scope = codeInfo.getScope();
UserInfo userInfo = codeInfo.getUser();
String nonce = codeInfo.getNonce();
ClientDetail clientDetail = JapIds.getContext().getClientDetailService().getByClientId(param.getClientId());
OauthUtil.validClientDetail(clientDetail);
OauthUtil.validateGrantType(param.getGrantType(), clientDetail.getGrantTypes(), GrantType.AUTHORIZATION_CODE);
OauthUtil.validateSecret(param, clientDetail, oauth2Service);
OauthUtil.validateRedirectUri(param.getRedirectUri(), clientDetail);
oauth2Service.invalidateCode(param.getCode());
long expiresIn = OauthUtil.getAccessTokenExpiresIn(clientDetail.getAccessTokenExpiresIn());
AccessToken accessToken = TokenUtil.createAccessToken(userInfo, clientDetail, param.getGrantType(), scope, nonce);
IdsResponse<String, Object> response = new IdsResponse<String, Object>()
.add(IdsConsts.ACCESS_TOKEN, accessToken.getAccessToken())
.add(IdsConsts.REFRESH_TOKEN, accessToken.getRefreshToken())
.add(IdsConsts.EXPIRES_IN, expiresIn)
.add(IdsConsts.TOKEN_TYPE, IdsConsts.TOKEN_TYPE_BEARER)
.add(IdsConsts.SCOPE, scope);
if (OauthUtil.isOidcProtocol(scope)) {
response.add(IdsConsts.ID_TOKEN, TokenUtil.createIdToken(clientDetail, userInfo, nonce));
}
return response;
}
/**
* RFC6749 4.3. Resource Owner Password Credentials Grant
*
* @param param request params
* @return IdsResponse
* @see <a href="https://tools.ietf.org/html/rfc6749#section-4.3" target="_blank">4.3. Resource Owner Password Credentials Grant</a>
*/
public IdsResponse<String, Object> generatePasswordResponse(IdsRequestParam param) {
if (ObjectUtil.hasNull(param.getClientId())) {
throw new InvalidClientException(ErrorResponse.INVALID_CLIENT);
}
String username = param.getUsername();
String password = param.getPassword();
UserInfo userInfo = JapIds.getContext().getUserService().loginByUsernameAndPassword(username, password);
if (null == userInfo) {
throw new IdsException(ErrorResponse.INVALID_USER_CERTIFICATE);
}
ClientDetail clientDetail = JapIds.getContext().getClientDetailService().getByClientId(param.getClientId());
String requestScope = param.getScope();
OauthUtil.validClientDetail(clientDetail);
OauthUtil.validateScope(requestScope, clientDetail.getScopes());
OauthUtil.validateGrantType(param.getGrantType(), clientDetail.getGrantTypes(), GrantType.PASSWORD);
OauthUtil.validateSecret(param, clientDetail, oauth2Service);
long expiresIn = OauthUtil.getAccessTokenExpiresIn(clientDetail.getAccessTokenExpiresIn());
AccessToken accessToken = TokenUtil.createAccessToken(userInfo, clientDetail, param.getGrantType(), requestScope, param.getNonce());
IdsResponse<String, Object> response = new IdsResponse<String, Object>()
.add(IdsConsts.ACCESS_TOKEN, accessToken.getAccessToken())
.add(IdsConsts.REFRESH_TOKEN, accessToken.getRefreshToken())
.add(IdsConsts.EXPIRES_IN, expiresIn)
.add(IdsConsts.TOKEN_TYPE, IdsConsts.TOKEN_TYPE_BEARER)
.add(IdsConsts.SCOPE, requestScope);
if (OauthUtil.isOidcProtocol(requestScope)) {
response.add(IdsConsts.ID_TOKEN, TokenUtil.createIdToken(clientDetail, userInfo, param.getNonce()));
}
return response;
}
/**
* RFC6749 4.4. Client Credentials Grant
*
* @param param request params
* @return IdsResponse
* @see <a href="https://tools.ietf.org/html/rfc6749#section-4.4" target="_blank">4.4. Client Credentials Grant</a>
*/
public IdsResponse<String, Object> generateClientCredentialsResponse(IdsRequestParam param) {
String clientId = param.getClientId();
if (ObjectUtil.hasNull(param.getClientId(), param.getClientSecret())) {
throw new InvalidClientException(ErrorResponse.INVALID_CLIENT);
}
ClientDetail clientDetail = JapIds.getContext().getClientDetailService().getByClientId(clientId);
String requestScope = param.getScope();
OauthUtil.validClientDetail(clientDetail);
OauthUtil.validateScope(requestScope, clientDetail.getScopes());
OauthUtil.validateGrantType(param.getGrantType(), clientDetail.getGrantTypes(), GrantType.CLIENT_CREDENTIALS);
OauthUtil.validateSecret(param, clientDetail, oauth2Service);
long expiresIn = OauthUtil.getAccessTokenExpiresIn(clientDetail.getAccessTokenExpiresIn());
AccessToken accessToken = TokenUtil.createClientCredentialsAccessToken(clientDetail, param.getGrantType(), requestScope, param.getNonce());
// https://tools.ietf.org/html/rfc6749#section-4.2.2
// The authorization server MUST NOT issue a refresh token.
IdsResponse<String, Object> response = new IdsResponse<String, Object>()
.add(IdsConsts.ACCESS_TOKEN, accessToken.getAccessToken())
.add(IdsConsts.EXPIRES_IN, expiresIn)
.add(IdsConsts.TOKEN_TYPE, IdsConsts.TOKEN_TYPE_BEARER);
if (!StringUtil.isEmpty(requestScope)) {
response.add(IdsConsts.SCOPE, requestScope);
}
return response;
}
/**
* RFC6749 6. Refreshing an Access Token
*
* @param param request params
* @return IdsResponse
* @see <a href="https://tools.ietf.org/html/rfc6749#section-6" target="_blank">6. Refreshing an Access Token</a>
*/
public IdsResponse<String, Object> generateRefreshTokenResponse(IdsRequestParam param) {
TokenUtil.validateRefreshToken(param.getRefreshToken());
try {
AccessToken token = TokenUtil.getByRefreshToken(param.getRefreshToken());
ClientDetail clientDetail = JapIds.getContext().getClientDetailService().getByClientId(token.getClientId());
String requestScope = param.getScope();
OauthUtil.validClientDetail(clientDetail);
OauthUtil.validateScope(requestScope, clientDetail.getScopes());
OauthUtil.validateGrantType(param.getGrantType(), clientDetail.getGrantTypes(), GrantType.REFRESH_TOKEN);
OauthUtil.validateSecret(param, clientDetail, oauth2Service);
UserInfo user = JapIds.getContext().getUserService().getById(token.getUserId());
long expiresIn = OauthUtil.getRefreshTokenExpiresIn(clientDetail.getRefreshTokenExpiresIn());
AccessToken accessToken = TokenUtil.refreshAccessToken(user, clientDetail, token, param.getNonce());
return new IdsResponse<String, Object>()
.add(IdsConsts.ACCESS_TOKEN, accessToken.getAccessToken())
.add(IdsConsts.REFRESH_TOKEN, accessToken.getRefreshToken())
.add(IdsConsts.EXPIRES_IN, expiresIn)
.add(IdsConsts.TOKEN_TYPE, IdsConsts.TOKEN_TYPE_BEARER)
.add(IdsConsts.SCOPE, requestScope);
} catch (Exception e) {
throw new InvalidRequestException(ErrorResponse.SERVER_ERROR);
}
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Provide oauth service
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.provider;

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.service;
import com.fujieid.jap.ids.exception.IdsException;
import com.fujieid.jap.ids.model.ClientDetail;
import java.util.List;
/**
* Client application related interfaces
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public interface IdsClientDetailService {
/**
* Query client information through client id
*
* @param clientId Client application id
* @return ClientDetail
*/
default ClientDetail getByClientId(String clientId) {
throw new IdsException("Not implemented `IdsClientDetailService.getByClientId(String)`");
}
/**
* Add client
*
* @param clientDetail Client application details
* @return ClientDetail
*/
default ClientDetail add(ClientDetail clientDetail) {
throw new IdsException("Not implemented `IdsClientDetailService.add(ClientDetail)`");
}
/**
* Modify the client
*
* @param clientDetail Client application details
* @return ClientDetail
*/
default ClientDetail update(ClientDetail clientDetail) {
throw new IdsException("Not implemented `IdsClientDetailService.update(ClientDetail)`");
}
/**
* Delete client by primary key
*
* @param id Primary key of the client application
* @return boolean
*/
default boolean removeById(String id) {
throw new IdsException("Not implemented `IdsClientDetailService.removeById(String)`");
}
/**
* Delete client by client id
*
* @param clientId Client application id
* @return ClientDetail
*/
default boolean removeByClientId(String clientId) {
throw new IdsException("Not implemented `IdsClientDetailService.removeByClientId(String)`");
}
/**
* Get all client detail
*
* @return List
*/
default List<ClientDetail> getAllClientDetail() {
throw new IdsException("Not implemented `IdsClientDetailService.getAllClientDetail()`");
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.service;
import com.fujieid.jap.ids.config.JwtConfig;
import com.fujieid.jap.ids.exception.IdsException;
/**
* User/organization/enterprise and other identity service related interfaces
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public interface IdsIdentityService {
/**
* Get the jwt token encryption key string
*
* @param identity User/organization/enterprise identification
* @return Encryption key string in json format
*/
default String getJwksJson(String identity) {
throw new IdsException("Not implemented `IdsIdentityService.getJwksJson(String)`");
}
/**
* Get the configuration of jwt token encryption
*
* @param identity User/organization/enterprise identification
* @return Encryption key string in json format
*/
default JwtConfig getJwtConfig(String identity) {
throw new IdsException("Not implemented `IdsIdentityService.getJwtConfig(String)`");
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.service;
import com.fujieid.jap.ids.exception.IdsException;
import com.fujieid.jap.ids.model.UserInfo;
/**
* User-related interface
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public interface IdsUserService {
default UserInfo loginByUsernameAndPassword(String username, String password) {
throw new IdsException("Not implemented `IdsUserService.loginByUsernameAndPassword(String, String)`");
}
/**
* Get user info by userid.
*
* @param userId userId of the business system
* @return JapUser
*/
default UserInfo getById(String userId) {
throw new IdsException("Not implemented `IdsUserService.getById(String)`");
}
/**
* Get user info by username.
*
* @param username username of the business system
* @return JapUser
*/
default UserInfo getByName(String username) {
throw new IdsException("Not implemented `IdsUserService.getByName(String)`");
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.service;
import com.fujieid.jap.ids.model.AuthCode;
import com.fujieid.jap.ids.model.IdsRequestParam;
import com.fujieid.jap.ids.model.UserInfo;
/**
* oauth 2.0 related methods
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public interface Oauth2Service {
/**
* Generate authorization code
*
* @param param Parameters requested by the client
* @param user User Info
* @param codeExpiresIn code expiration time
* @return String
*/
String createAuthorizationCode(IdsRequestParam param, UserInfo user, Long codeExpiresIn);
/**
* Verification authorization code
*
* @param grantType grant Type
* @param code authorization code
* @return AuthCode
*/
AuthCode validateAndGetAuthrizationCode(String grantType, String code);
/**
* When the pkce protocol is enabled, the code challenge needs to be verified
*
* @param codeVerifier code verifier
* @param code authorization code
* @see <a href="https://tools.ietf.org/html/rfc7636">https://tools.ietf.org/html/rfc7636</a>
*/
void validateAuthrizationCodeChallenge(String codeVerifier, String code);
/**
* Obtain auth code info by authorization code
*
* @param code authorization code
* @return string
*/
AuthCode getCodeInfo(String code);
/**
* Delete authorization code
*
* @param code authorization code
*/
void invalidateCode(String code);
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.service;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.InvalidCodeException;
import com.fujieid.jap.ids.model.AuthCode;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.IdsRequestParam;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.model.enums.GrantType;
import com.fujieid.jap.ids.util.OauthUtil;
/**
* oauth 2.0 related methods
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class Oauth2ServiceImpl implements Oauth2Service {
private static final Log log = LogFactory.get();
@Override
public String createAuthorizationCode(IdsRequestParam param, UserInfo user, Long codeExpiresIn) {
String scopeStr = param.getScope();
String nonce = param.getNonce();
String code = RandomUtil.randomString(12);
AuthCode authCode = new AuthCode()
.setScope(scopeStr)
.setUser(user)
.setNonce(nonce)
.setCodeChallenge(param.getCodeChallenge())
.setCodeChallengeMethod(param.getCodeChallengeMethod());
JapIds.getContext().getCache().set(IdsConsts.OAUTH_CODE_CACHE_KEY + code, authCode, codeExpiresIn * 1000);
return code;
}
@Override
public AuthCode validateAndGetAuthrizationCode(String grantType, String code) {
if (!GrantType.AUTHORIZATION_CODE.getType().equals(grantType)) {
return null;
}
AuthCode authCode = this.getCodeInfo(code);
if (null == authCode || ObjectUtil.hasNull(authCode.getUser(), authCode.getScope())) {
throw new InvalidCodeException(ErrorResponse.INVALID_CODE);
}
return authCode;
}
@Override
public void validateAuthrizationCodeChallenge(String codeVerifier, String code) {
log.debug("客户端开启了 PKCE 增强协议,开始校验 code challenge 的合法性...");
AuthCode authCode = this.getCodeInfo(code);
if (ObjectUtil.isNull(authCode)) {
throw new InvalidCodeException(ErrorResponse.INVALID_CODE);
}
if (ObjectUtil.hasNull(authCode.getCodeChallenge(), authCode.getCodeChallengeMethod())) {
log.debug("客户端开启了 PKCE 增强协议code challenge 的合法性,校验失败...");
throw new InvalidCodeException(ErrorResponse.INVALID_CODE_CHALLENGE);
}
String codeChallengeMethod = authCode.getCodeChallengeMethod();
String cacheCodeChallenge = authCode.getCodeChallenge();
String currentCodeChallenge = OauthUtil.generateCodeChallenge(codeChallengeMethod, codeVerifier);
if (!currentCodeChallenge.equals(cacheCodeChallenge)) {
throw new InvalidCodeException(ErrorResponse.INVALID_CODE_CHALLENGE);
}
}
@Override
public AuthCode getCodeInfo(String code) {
return (AuthCode) JapIds.getContext().getCache().get(IdsConsts.OAUTH_CODE_CACHE_KEY + code);
}
@Override
public void invalidateCode(String code) {
JapIds.getContext().getCache().removeKey(IdsConsts.OAUTH_CODE_CACHE_KEY + code);
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* The interface exposed by ids needs to be implemented by the caller
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.service;

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import com.fujieid.jap.ids.model.ClientCertificate;
import org.jose4j.base64url.internal.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Credentials in Basic authentication.
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @see <a href="http://tools.ietf.org/html/rfc2617#section-2">RFC 2617 (HTTP Authentication), 2. Basic Authentication Scheme</a>
*/
public class BasicCredentials {
/**
* Regular expression to parse {@code Authorization} header.
*/
private static final Pattern CHALLENGE_PATTERN = Pattern.compile("^Basic *([^ ]+) *$", Pattern.CASE_INSENSITIVE);
private final ClientCertificate clientCertificate;
/**
* "Basic {base64-encoded id:secret}"
*/
private transient String formatted;
/**
* Constructor with credentials.
*
* @param id The client ID.
* @param secret The client secret.
*/
public BasicCredentials(String id, String secret) {
this.clientCertificate = new ClientCertificate(null == id ? "" : id, null == secret ? "" : secret);
}
/**
* Parse {@code Authorization} header for Basic authentication.
*
* @param input The value of {@code Authorization} header. Expected inputs
* are either <code>"Basic <i>{Base64-Encoded-id-and-secret}</i>"</code>,
* or <code>"<i>{Base64-Encoded-id-and-secret}</i>"</code>.
* @return Parsed credentials. If {@code input} is {@code null} is returned.
*/
public static BasicCredentials parse(String input) {
if (input == null) {
return null;
}
Matcher matcher = CHALLENGE_PATTERN.matcher(input);
if (!matcher.matches()) {
return new BasicCredentials(null, null);
}
String encoded = matcher.group(1);
byte[] decoded = Base64.decodeBase64(encoded);
String value = new String(decoded, StandardCharsets.UTF_8);
String[] credentials = value.split(":", 2);
String id = credentials[0];
String secret = null;
if (credentials.length == 2) {
secret = credentials[1];
}
return new BasicCredentials(id, secret);
}
public String getId() {
return clientCertificate.getId();
}
public String getSecret() {
return clientCertificate.getSecret();
}
public ClientCertificate getClientCertificate() {
return clientCertificate;
}
/**
* Create a value suitable as the value of {@code Authorization} header.
*
* @return {@code Authorization} header value for Basic authentication.
*/
public String create() {
if (formatted != null) {
return formatted;
}
String credentials = String.format("%s:%s", clientCertificate.getId(), clientCertificate.getSecret());
byte[] credentialsBytes = credentials.getBytes(StandardCharsets.UTF_8);
String encoded = Base64.encodeBase64String(credentialsBytes);
return (formatted = "Basic " + encoded);
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright (C) 2014 Authlete, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import com.fujieid.jap.ids.model.IdsConsts;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class for Bearer Token defined in
* <a href="http://tools.ietf.org/html/rfc6750">RFC 6750</a>.
*
* @author Takahiko Kawasaki
* @see <a href="http://tools.ietf.org/html/rfc6750">RFC 6750 (OAuth 2.0 Bearer Token Usage)</a>
*/
public class BearerToken {
/**
* Regular expression to parse {@code Authorization} header.
*/
private static final Pattern CHALLENGE_PATTERN
= Pattern.compile("^Bearer *([^ ]+) *$", Pattern.CASE_INSENSITIVE);
private BearerToken() {
}
/**
* Extract the access token embedded in the input string.
*
* <p>
* This method assumes that the input string comes from one of
* the following three places that are mentioned in "RFC 6750
* (OAuth 2.0 Bearer Token Usage),
* <a href="http://tools.ietf.org/html/rfc6750#section-2"
* >2. Authenticated Requests</a>".
* </p>
*
* <blockquote>
* <ol>
* <li><a href="http://tools.ietf.org/html/rfc6750#section-2.1"
* >Authorization Request Header Field</a>
* <li><a href="http://tools.ietf.org/html/rfc6750#section-2.2"
* >Form-Encoded Body Parameter</a>
* <li><a href="http://tools.ietf.org/html/rfc6750#section-2.3"
* >URI Query Parameter</a>
* </ol>
* </blockquote>
*
* <p>
* To be concrete, this method assumes that the format of the
* input string is either of the following two.
* </p>
*
* <blockquote>
* <ol>
* <li><code>"Bearer <i>{access-token}</i>"</code>
* <li>Parameters formatted in <code>application/x-www-form-urlencoded</code>
* containing <code>access_token=<i>{access-token}</i></code>.
* </ol>
* </blockquote>
*
* <p>
* For example, both {@link BearerToken#parse(String) parse} method calls below
* return <code>"hello-world"</code>.
* </p>
*
* <pre>
* BearerToken.parse("Bearer hello-world");
* BearerToken.parse("key1=value1&amp;access_token=hello-world");
* </pre>
*
* @param input The input string to be parsed.
* @return The extracted access token, or <code>null</code> if not found.
* @see <a href="http://tools.ietf.org/html/rfc6750"
* >RFC 6750 (OAuth 2.0 Bearer Token Usage)</a>
*/
public static String parse(String input) {
if (input == null) {
return null;
}
if (!input.startsWith(IdsConsts.TOKEN_TYPE_BEARER) && !input.contains("&")) {
return input;
}
// First, check whether the input matches the pattern
// "Bearer {access-token}".
Matcher matcher = CHALLENGE_PATTERN.matcher(input);
// If the input matches the pattern.
if (matcher.matches()) {
// Return the value as is. Note that it is not Base64-encoded.
// See https://www.ietf.org/mail-archive/web/oauth/current/msg08489.html
return matcher.group(1);
} else {
// Assume that the input is formatted in
// application/x-www-form-urlencoded.
return extractFromFormParameters(input);
}
}
private static String extractFromFormParameters(String input) {
for (String parameter : input.split("&")) {
String[] pair = parameter.split("=", 2);
if (pair.length != 2 || pair[1].length() == 0) {
continue;
}
if (!"access_token".equals(pair[0])) {
continue;
}
try {
// URL-decode
return URLDecoder.decode(pair[1], "UTF-8");
} catch (UnsupportedEncodingException e) {
// This won't happen.
return null;
}
}
// Not found.
return null;
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import cn.hutool.core.util.ObjectUtil;
import com.fujieid.jap.core.util.RequestUtil;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.model.ClientCertificate;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.enums.ClientSecretAuthMethod;
import com.xkcoding.json.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
/**
* Get the client secret, client id from the request
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class ClientCertificateUtil {
public static ClientCertificate getClientCertificate(HttpServletRequest request) {
List<ClientSecretAuthMethod> clientSecretAuthMethods = JapIds.getIdsConfig().getClientSecretAuthMethods();
if (ObjectUtil.isEmpty(clientSecretAuthMethods)) {
clientSecretAuthMethods = Collections.singletonList(ClientSecretAuthMethod.ALL);
}
if (clientSecretAuthMethods.contains(ClientSecretAuthMethod.ALL)) {
ClientCertificate clientCertificate = getClientCertificateFromRequestParameter(request);
if (StringUtil.isEmpty(clientCertificate.getId())) {
clientCertificate = getClientCertificateFromHeader(request);
}
return clientCertificate;
} else {
if (clientSecretAuthMethods.contains(ClientSecretAuthMethod.CLIENT_SECRET_POST)
|| clientSecretAuthMethods.contains(ClientSecretAuthMethod.NONE)) {
return getClientCertificateFromRequestParameter(request);
}
if (clientSecretAuthMethods.contains(ClientSecretAuthMethod.CLIENT_SECRET_BASIC)) {
return getClientCertificateFromHeader(request);
}
}
return new ClientCertificate();
}
private static ClientCertificate getClientCertificateFromRequestParameter(HttpServletRequest request) {
String clientId = RequestUtil.getParam(IdsConsts.CLIENT_ID, request);
String clientSecret = RequestUtil.getParam(IdsConsts.CLIENT_SECRET, request);
return new ClientCertificate(clientId, clientSecret);
}
private static ClientCertificate getClientCertificateFromHeader(HttpServletRequest request) {
String authorizationHeader = RequestUtil.getHeader(IdsConsts.AUTHORIZATION_HEADER_NAME, request);
if (StringUtil.isNotEmpty(authorizationHeader)) {
BasicCredentials credentials = BasicCredentials.parse(authorizationHeader);
return credentials.getClientCertificate();
}
return new ClientCertificate();
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class DateUtil extends cn.hutool.core.date.DateUtil {
/**
* Convert timestamp to LocalDateTime
*
* @param second Long type timestamp, in seconds
* @param zoneOffset Time zone, default is {@code +8}
* @return java.time.LocalDateTime
*/
public static LocalDateTime ofEpochSecond(Long second, ZoneOffset zoneOffset) {
if (zoneOffset == null) {
return LocalDateTime.ofEpochSecond(second, 0, ZoneOffset.ofHours(8));
} else {
return LocalDateTime.ofEpochSecond(second, 0, zoneOffset);
}
}
/**
* Get the current Date
*
* @return LocalDateTime
*/
public static LocalDateTime nowDate() {
return LocalDateTime.now();
}
}

View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import com.fujieid.jap.ids.exception.InvalidJwksException;
import com.fujieid.jap.ids.model.enums.TokenSigningAlg;
import org.jose4j.jwk.*;
import org.jose4j.keys.EllipticCurves;
import org.jose4j.lang.JoseException;
import java.util.Arrays;
/**
* Generate json web key encryption certificate
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class JwkUtil {
/**
* Create rsa json web key
*
* @param keyId key id
* @param signingAlg Encryption Algorithm
* @return RsaJsonWebKey
*/
public static RsaJsonWebKey createRsaJsonWebKey(String keyId, TokenSigningAlg signingAlg) {
if (!Arrays.asList(TokenSigningAlg.RS256, TokenSigningAlg.RS384, TokenSigningAlg.RS512).contains(signingAlg)) {
throw new InvalidJwksException("Unable to create RSA Json Web Key. Unsupported jwk algorithm, only supports RS256, RS384, RS512");
}
RsaJsonWebKey jwk = null;
try {
jwk = RsaJwkGenerator.generateJwk(2048);
jwk.setKeyId(keyId);
jwk.setAlgorithm(signingAlg.getAlg());
} catch (JoseException e) {
e.printStackTrace();
throw new InvalidJwksException("Unable to create RSA Json Web Key.");
}
return jwk;
}
/**
* Create the json string of rsa json web key
*
* @param keyId key id
* @param signingAlg Encryption Algorithm
* @return RSA Json Web Key Json
*/
public static String createRsaJsonWebKeyJson(String keyId, TokenSigningAlg signingAlg) {
RsaJsonWebKey jwk = createRsaJsonWebKey(keyId, signingAlg);
return jwk.toJson(RsaJsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
}
/**
* Create a set collection of rsa json web key json string
*
* @param keyId key id
* @param signingAlg Encryption Algorithm
* @return RSA Json Web Key Set Json
*/
public static String createRsaJsonWebKeySetJson(String keyId, TokenSigningAlg signingAlg) {
RsaJsonWebKey jwk = createRsaJsonWebKey(keyId, signingAlg);
return new JsonWebKeySet(jwk).toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
}
/**
* Create es json web key
*
* @param keyId key id
* @param signingAlg Encryption Algorithm
* @return Elliptic Curve Json Web Key
*/
public static EllipticCurveJsonWebKey createEsJsonWebKey(String keyId, TokenSigningAlg signingAlg) {
if (!Arrays.asList(TokenSigningAlg.ES256, TokenSigningAlg.ES384, TokenSigningAlg.ES512).contains(signingAlg)) {
throw new InvalidJwksException("Unable to create ES Json Web Key. Unsupported jwk algorithm, only supports ES256, ES384, ES512");
}
EllipticCurveJsonWebKey jwk = null;
try {
jwk = EcJwkGenerator.generateJwk(EllipticCurves.P256);
jwk.setUse(Use.SIGNATURE);
jwk.setKeyId(keyId);
jwk.setAlgorithm(signingAlg.getAlg());
} catch (JoseException e) {
e.printStackTrace();
throw new InvalidJwksException("Unable to create ES Json Web Key.");
}
return jwk;
}
/**
* Create json string of es json web key
*
* @param keyId key id
* @param signingAlg Encryption Algorithm
* @return ES Json Web Key Json
*/
public static String createEsJsonWebKeyJson(String keyId, TokenSigningAlg signingAlg) {
EllipticCurveJsonWebKey jwk = createEsJsonWebKey(keyId, signingAlg);
return jwk.toJson(RsaJsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
}
/**
* Create a set collection of es json web key json string
*
* @param keyId key id
* @param signingAlg Encryption Algorithm
* @return RS Json Web Key Set Json
*/
public static String createEsJsonWebKeySetJson(String keyId, TokenSigningAlg signingAlg) {
EllipticCurveJsonWebKey jwk = createEsJsonWebKey(keyId, signingAlg);
return new JsonWebKeySet(jwk).toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
}
}

View File

@ -0,0 +1,280 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.fujieid.jap.ids.config.IdsConfig;
import com.fujieid.jap.ids.config.JwtConfig;
import com.fujieid.jap.ids.exception.IdsTokenException;
import com.fujieid.jap.ids.exception.InvalidJwksException;
import com.fujieid.jap.ids.exception.InvalidTokenException;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.model.enums.JwtVerificationType;
import com.fujieid.jap.ids.model.enums.TokenSigningAlg;
import com.xkcoding.json.JsonUtil;
import com.xkcoding.json.util.StringUtil;
import org.jose4j.jwk.*;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.NumericDate;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.keys.EcKeyUtil;
import org.jose4j.keys.RsaKeyUtil;
import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver;
import org.jose4j.keys.resolvers.JwksVerificationKeyResolver;
import org.jose4j.keys.resolvers.VerificationKeyResolver;
import org.jose4j.lang.JoseException;
import java.util.Map;
/**
* simple JSON Web Key generatorhttps://mkjwk.org/?spm=a2c4g.11186623.2.33.4b2040ecxvsKD7
* <p>
* JWT Decoder: http://jwt.calebb.net/
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class JwtUtil {
private static final Log log = LogFactory.get();
/**
* https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples
*
* @param clientId Client Identifier
* @param userinfo User Profile
* @param tokenExpireIn Id Token validity (seconds)
* @param nonce noncestr
* @param idsConfig ids config
* @return jwt token
*/
public static String createJwtToken(String clientId, UserInfo userinfo, Long tokenExpireIn, String nonce, IdsConfig idsConfig) {
JwtClaims claims = new JwtClaims();
// required
// A unique identity of the person providing the authentication information. Usually an HTTPS URL (excl. queryString and Fragment)
claims.setIssuer(idsConfig.getIssuer());
// The LOGO of EU provided by ISS is unique within the scope of ISS. It is used by the RP to identify a unique user. The maximum length is 255 ASCII characters
claims.setSubject(null == userinfo ? clientId : userinfo.getId());
// Identify the audience for ID Token. OAuth2's client_ID must be included
claims.setAudience(clientId);
// Expiration time. ID Token beyond this time will become invalid and will no longer be authenticated
claims.setExpirationTime(NumericDate.fromMilliseconds(System.currentTimeMillis() + (tokenExpireIn * 1000)));
// JWT build time
claims.setIssuedAt(NumericDate.fromMilliseconds(System.currentTimeMillis()));
// optional
// The random string provided by the RP when it sends a request is used to mitigate replay attacks, and the ID Token can also be associated with the RP's own Session
if (!StringUtil.isEmpty(nonce)) {
claims.setStringClaim(IdsConsts.NONCE, nonce);
}
if (null != userinfo) {
// Time of completion of EU certification. This Claim is required if the RP carries the max_AGE parameter when sending the AuthN request
// claims.setClaim("auth_time", "auth_time");
// If you include other claim reference: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
claims.setStringClaim("username", userinfo.getUsername());
}
// A JWT is a JWS and/or a JWE with JSON claims as the payload.
// In this example it is a JWS so we create a JsonWebSignature object.
JsonWebSignature jws = new JsonWebSignature();
// The payload of the JWS is JSON content of the JWT Claims
jws.setPayload(claims.toJson());
JwtConfig jwtConfig = idsConfig.getJwtConfig();
PublicJsonWebKey publicJsonWebKey = IdsVerificationKeyResolver.createPublicJsonWebKey(jwtConfig.getJwksKeyId(), jwtConfig.getJwksJson(), jwtConfig.getTokenSigningAlg());
if (null == publicJsonWebKey) {
throw new InvalidJwksException("Unable to create Jwt Token: Unable to create public json web key.");
}
// The JWT is signed using the private key
jws.setKey(publicJsonWebKey.getPrivateKey());
// Set the Key ID (kid) header because it's just the polite thing to do.
// We only have one key in this example but a using a Key ID helps
// facilitate a smooth key rollover process
jws.setKeyIdHeaderValue(publicJsonWebKey.getKeyId());
// Set the signature algorithm on the JWT/JWS that will integrity protect the claims
jws.setAlgorithmHeaderValue(jwtConfig.getTokenSigningAlg().getAlg());
String idToken = null;
// Sign the JWS and produce the compact serialization or the complete JWT/JWS
// representation, which is a string consisting of three dot ('.') separated
// base64url-encoded parts in the form Header.Payload.Signature
// If you wanted to encrypt it, you can simply set this jwt as the payload
// of a JsonWebEncryption object and set the cty (Content Type) header to "jwt".
try {
idToken = jws.getCompactSerialization();
} catch (JoseException e) {
throw new IdsTokenException("Unable to create Jwt Token: " + e.getMessage());
}
return idToken;
}
public static Map<String, Object> parseJwtToken(String jwtToken, IdsConfig idsConfig) {
JwtConfig jwtConfig = idsConfig.getJwtConfig();
PublicJsonWebKey publicJsonWebKey = IdsVerificationKeyResolver.createPublicJsonWebKey(jwtConfig.getJwksKeyId(), jwtConfig.getJwksJson(), jwtConfig.getTokenSigningAlg());
if (null == publicJsonWebKey) {
throw new InvalidJwksException("Unable to parse Jwt Token: Unable to create public json web key.");
}
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setSkipDefaultAudienceValidation()
// whom the JWT needs to have been issued by
// allow some leeway in validating time based claims to account for clock skew
.setAllowedClockSkewInSeconds(30)
// verify the signature with the public key
.setVerificationKey(publicJsonWebKey.getPublicKey())
// create the JwtConsumer instance
.build();
try {
// Validate the JWT and process it to the Claims
JwtClaims jwtClaims = jwtConsumer.processToClaims(jwtToken);
return jwtClaims.getClaimsMap();
} catch (InvalidJwtException e) {
// InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway.
// Hopefully with meaningful explanations(s) about what went wrong.
log.error("Invalid Jwt Token : " + JsonUtil.toJsonString(e.getErrorDetails()), e);
if (e.hasExpired()) {
throw new InvalidTokenException(ErrorResponse.EXPIRED_TOKEN);
}
throw new InvalidTokenException(ErrorResponse.INVALID_TOKEN);
}
}
public static Map<String, Object> validateJwtToken(String clientId, String userId, String jwtToken, IdsConfig idsConfig) {
// Use JwtConsumerBuilder to construct an appropriate JwtConsumer, which will
// be used to validate and process the JWT.
// The specific validation requirements for a JWT are context dependent, however,
// it typically advisable to require a (reasonable) expiration time, a trusted issuer, and
// and audience that identifies your system as the intended recipient.
// If the JWT is encrypted too, you need only provide a decryption key or
// decryption key resolver to the builder.
JwtConfig jwtConfig = idsConfig.getJwtConfig();
JwtConsumerBuilder jwtConsumerBuilder = new JwtConsumerBuilder();
JwtVerificationType jwtVerificationType = jwtConfig.getJwtVerificationType();
if (null != jwtVerificationType) {
if (jwtVerificationType == JwtVerificationType.HTTPS_JWKS_ENDPOINT) {
// The HttpsJwks retrieves and caches keys from a the given HTTPS JWKS endpoint.
// Because it retains the JWKs after fetching them, it can and should be reused
// to improve efficiency by reducing the number of outbound calls the the endpoint.
VerificationKeyResolver verificationKeyResolver = new HttpsJwksVerificationKeyResolver(new HttpsJwks(idsConfig.getJwksUrl()));
jwtConsumerBuilder.setVerificationKeyResolver(verificationKeyResolver);
} else if (jwtVerificationType == JwtVerificationType.JWKS) {
// There's also a key resolver that selects from among a given list of JWKs using the Key ID
// and other factors provided in the header of the JWS/JWT.
JsonWebKeySet jsonWebKeySet = IdsVerificationKeyResolver.createJsonWebKeySet(jwtConfig.getJwksJson());
JwksVerificationKeyResolver jwksResolver = new JwksVerificationKeyResolver(jsonWebKeySet.getJsonWebKeys());
jwtConsumerBuilder.setVerificationKeyResolver(jwksResolver);
}
}
PublicJsonWebKey publicJsonWebKey = IdsVerificationKeyResolver.createPublicJsonWebKey(jwtConfig.getJwksKeyId(), jwtConfig.getJwksJson(), jwtConfig.getTokenSigningAlg());
if (null == publicJsonWebKey) {
throw new InvalidJwksException("Unable to verify Jwt Token: Unable to create public json web key.");
}
JwtConsumer jwtConsumer = jwtConsumerBuilder
.setRequireIssuedAt()
// the JWT must have an expiration time
.setRequireExpirationTime()
// the JWT must have a subject claim
.setRequireSubject()
// whom the JWT needs to have been issued by
.setExpectedIssuer(idsConfig.getIssuer())
.setExpectedSubject(StringUtil.isEmpty(userId) ? clientId : userId)
// to whom the JWT is intended for
.setExpectedAudience(clientId)
// allow some leeway in validating time based claims to account for clock skew
.setAllowedClockSkewInSeconds(30)
// verify the signature with the public key
.setVerificationKey(publicJsonWebKey.getPublicKey())
// create the JwtConsumer instance
.build();
try {
// Validate the JWT and process it to the Claims
JwtClaims jwtClaims = jwtConsumer.processToClaims(jwtToken);
return jwtClaims.getClaimsMap();
} catch (InvalidJwtException e) {
// InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway.
// Hopefully with meaningful explanations(s) about what went wrong.
log.error("Invalid Jwt Token! ", e);
// Programmatic access to (some) specific reasons for JWT invalidity is also possible
// should you want different error handling behavior for certain conditions.
// Whether or not the JWT has expired being one common reason for invalidity
if (e.hasExpired()) {
try {
log.error("Jwt Token expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
} catch (MalformedClaimException malformedClaimException) {
malformedClaimException.printStackTrace();
}
throw new InvalidTokenException(ErrorResponse.EXPIRED_TOKEN);
}
throw new InvalidTokenException(ErrorResponse.INVALID_TOKEN);
}
}
public static class IdsVerificationKeyResolver {
public static JsonWebKeySet createJsonWebKeySet(String jwksJson) {
InvalidJwksException invalidJwksException = new InvalidJwksException(ErrorResponse.INVALID_JWKS);
if (StringUtil.isEmpty(jwksJson)) {
throw invalidJwksException;
}
JsonWebKeySet jsonWebKeySet = null;
try {
jsonWebKeySet = new JsonWebKeySet(jwksJson);
} catch (JoseException e) {
throw invalidJwksException;
}
return jsonWebKeySet;
}
public static PublicJsonWebKey createPublicJsonWebKey(String keyId, String jwksJson, TokenSigningAlg tokenSigningAlg) {
tokenSigningAlg = null == tokenSigningAlg ? TokenSigningAlg.RS256 : tokenSigningAlg;
JsonWebKeySet jsonWebKeySet = createJsonWebKeySet(jwksJson);
switch (tokenSigningAlg.getKeyType()) {
case RsaKeyUtil.RSA:
return (RsaJsonWebKey) jsonWebKeySet.findJsonWebKey(keyId, tokenSigningAlg.getKeyType(), Use.SIGNATURE, tokenSigningAlg.getAlg());
case EcKeyUtil.EC:
return (EllipticCurveJsonWebKey) jsonWebKeySet.findJsonWebKey(keyId, tokenSigningAlg.getKeyType(), Use.SIGNATURE, tokenSigningAlg.getAlg());
default:
return null;
}
}
}
}

View File

@ -0,0 +1,358 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.SecureUtil;
import com.fujieid.jap.ids.exception.*;
import com.fujieid.jap.ids.model.ClientDetail;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.IdsRequestParam;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.model.enums.GrantType;
import com.fujieid.jap.ids.service.Oauth2Service;
import com.xkcoding.json.util.StringUtil;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class OauthUtil {
private static final Collection<String> REDIRECT_GRANT_TYPES = Arrays.asList("implicit", "authorization_code");
/**
* Convert string to list
*
* @param text The string to be converted
* @param splitRegex Regular expression to split string
* @return List of strings (de-duplicated)
*/
public static Set<String> convertStrToList(String text, String splitRegex) {
Set<String> result = new TreeSet<>();
if (text != null && text.trim().length() > 0) {
String[] tokens = text.split(splitRegex);
result.addAll(Arrays.asList(tokens));
}
return result;
}
/**
* Convert string to list
*
* @param text The string to be converted
* @return List of strings (de-duplicated)
*/
public static Set<String> convertStrToList(String text) {
return convertStrToList(text, "[\\s+]");
}
/**
* @param requestScopes The scope parameter in the current request
* @param clientScopes Scope in client detail
* @return After the verification is passed, return the scope list
*/
public static Set<String> validateScope(String requestScopes, String clientScopes) {
if (StringUtil.isEmpty(requestScopes)) {
throw new InvalidScopeException("Empty scope (either the client or the user is not allowed the requested scopes)");
}
Set<String> scopes = OauthUtil.convertStrToList(requestScopes);
if (StringUtil.isNotEmpty(clientScopes)) {
Set<String> appScopes = OauthUtil.convertStrToList(clientScopes);
for (String scope : scopes) {
if (!appScopes.contains(scope)) {
throw new InvalidScopeException("Invalid scope: " + scope + ". " + clientScopes);
}
}
}
return scopes;
}
/**
* Determine whether the current grant type supports redirect uri
*
* @param grantTypes some grant types
* @return true if the supplied grant types includes one or more of the redirect types
*/
private static boolean containsRedirectGrantType(Set<String> grantTypes) {
for (String type : grantTypes) {
if (REDIRECT_GRANT_TYPES.contains(type)) {
return true;
}
}
return false;
}
/**
* Verify the callback url
*
* @param requestRedirectUri The callback url passed in the current request
* @param clientDetail client detail
*/
public static void validateRedirectUri(String requestRedirectUri, ClientDetail clientDetail) {
String clientGrantTypes = clientDetail.getGrantTypes();
Set<String> clientGrantTypeSet = OauthUtil.convertStrToList(clientGrantTypes);
if (clientGrantTypeSet.isEmpty()) {
throw new InvalidGrantException("A client must have at least one authorized grant type.");
}
if (!containsRedirectGrantType(clientGrantTypeSet)) {
throw new InvalidGrantException(
"A redirect_uri can only be used by implicit or authorization_code grant types.");
}
String clientRedirectUri = clientDetail.getRedirectUri();
if (requestRedirectUri == null || !requestRedirectUri.equals(clientRedirectUri)) {
throw new InvalidRedirectUriException(ErrorResponse.INVALID_REDIRECT_URI);
}
}
/**
* 1. Only the authorization code mode will verify pkce, and the client secret will be verified when pkce is not enabled
* <p>
* 2. Implicit authorization grant and password authorization grant do not need to verify client secret
* <p>
* 3. Client authorizatio grantn needs to verify the client secret
*
* @param param request params
* @param clientDetail client detail
* @param oauth2Service oauth2Service
*/
public static void validateSecret(IdsRequestParam param, ClientDetail clientDetail, Oauth2Service oauth2Service) {
if (param.getGrantType().equals(GrantType.AUTHORIZATION_CODE.getType())) {
if (param.isEnablePkce()) {
oauth2Service.validateAuthrizationCodeChallenge(param.getCodeVerifier(), param.getCode());
} else {
if (!clientDetail.getClientSecret().equals(param.getClientSecret())) {
throw new InvalidClientException(ErrorResponse.INVALID_CLIENT);
}
}
} else {
if (!clientDetail.getClientSecret().equals(param.getClientSecret())) {
throw new InvalidClientException(ErrorResponse.INVALID_CLIENT);
}
}
}
/**
* Verify the response type
*
* @param requestResponseType The response type in the current request
* @param clientResponseTypes Response type in client detail
*/
public static void validateResponseType(String requestResponseType, String clientResponseTypes) {
Set<String> clientResponseTypeSet = OauthUtil.convertStrToList(clientResponseTypes);
if (!StringUtil.isEmpty(clientResponseTypes) && !clientResponseTypeSet.contains(requestResponseType)) {
throw new UnsupportedResponseTypeException(ErrorResponse.UNSUPPORTED_RESPONSE_TYPE);
}
}
/**
* Verify the grant type
*
* @param requestGrantType The grant type in the current request
* @param clientGrantTypes Grant type in client detail
* @param equalTo {@code requestGrantType} Must match grant type value
*/
public static void validateGrantType(String requestGrantType, String clientGrantTypes, GrantType equalTo) {
Set<String> grantTypeSet = OauthUtil.convertStrToList(clientGrantTypes);
if (StringUtil.isEmpty(requestGrantType) || ArrayUtil.isEmpty(grantTypeSet) || !grantTypeSet.contains(requestGrantType)) {
throw new UnsupportedGrantTypeException(ErrorResponse.UNSUPPORTED_GRANT_TYPE);
}
if (null != equalTo && !requestGrantType.equals(equalTo.getType())) {
throw new UnsupportedGrantTypeException(ErrorResponse.UNSUPPORTED_GRANT_TYPE);
}
}
public static void validClientDetail(ClientDetail clientDetail) {
if (clientDetail == null) {
throw new InvalidClientException(ErrorResponse.INVALID_CLIENT);
}
if (!Optional.ofNullable(clientDetail.getAvailable()).orElse(false)) {
throw new InvalidClientException(ErrorResponse.DISABLED_CLIENT);
}
}
/**
* Get the expiration time of access token, default is {@link IdsConsts#ACCESS_TOKEN_ACTIVITY_TIME}
*
* @param expiresIn The expiration time of the access token in the client detail
* @return long
*/
public static long getAccessTokenExpiresIn(Long expiresIn) {
return Optional.ofNullable(expiresIn).orElse(IdsConsts.ACCESS_TOKEN_ACTIVITY_TIME);
}
/**
* Get the expiration time of refresh token, the default is {@link IdsConsts#REFRESH_TOKEN_ACTIVITY_TIME}
*
* @param expiresIn The expiration time of the refresh token in the client detail
* @return long
*/
public static long getRefreshTokenExpiresIn(Long expiresIn) {
return Optional.ofNullable(expiresIn).orElse(IdsConsts.REFRESH_TOKEN_ACTIVITY_TIME);
}
/**
* Get the expiration time of the authorization code code, the default is {@link IdsConsts#AUTHORIZATION_CODE_ACTIVITY_TIME}
*
* @param expiresIn The expiration time of the code in the client detail
* @return long
*/
public static long getCodeExpiresIn(Long expiresIn) {
return Optional.ofNullable(expiresIn).orElse(IdsConsts.AUTHORIZATION_CODE_ACTIVITY_TIME);
}
/**
* Get the expiration time of id token, the default is{@link IdsConsts#ID_TOKEN_ACTIVITY_TIME}
*
* @param expiresIn The expiration time of the id token in the client detail
* @return long
*/
public static long getIdTokenExpiresIn(Long expiresIn) {
return Optional.ofNullable(expiresIn).orElse(IdsConsts.ID_TOKEN_ACTIVITY_TIME);
}
/**
* Get the expiration deadline of access token
*
* @param expiresIn The expiration time of the access token in the client detail
* @return long
*/
public static LocalDateTime getAccessTokenExpiresAt(Long expiresIn) {
expiresIn = getAccessTokenExpiresIn(expiresIn);
return DateUtil.ofEpochSecond(System.currentTimeMillis() + expiresIn * 1000, null);
}
/**
* Get the expiration deadline of refresh token
*
* @param expiresIn The expiration time of the refresh token in the client detail
* @return long
*/
public static LocalDateTime getRefreshTokenExpiresAt(Long expiresIn) {
expiresIn = getRefreshTokenExpiresIn(expiresIn);
return DateUtil.ofEpochSecond(System.currentTimeMillis() + expiresIn * 1000, null);
}
/**
* Get the expiration deadline of authorization code
*
* @param expiresIn The expiration time of the code in the client detail
* @return long
*/
public static LocalDateTime getCodeExpiresAt(Long expiresIn) {
expiresIn = getCodeExpiresIn(expiresIn);
return DateUtil.ofEpochSecond(System.currentTimeMillis() + expiresIn * 1000, null);
}
/**
* Get the expiration deadline of id token
*
* @param expiresIn The expiration time of the id token in the client detail
* @return long
*/
public static LocalDateTime getIdTokenExpiresAt(Long expiresIn) {
expiresIn = getIdTokenExpiresIn(expiresIn);
return DateUtil.ofEpochSecond(System.currentTimeMillis() + expiresIn * 1000, null);
}
/**
* Create authorize url
*
* @param authorizeUrl authorize url
* @param param request params
* @return authorizeUrl
*/
public static String createAuthorizeUrl(String authorizeUrl, IdsRequestParam param) {
Map<String, Object> model = new HashMap<>(13);
model.put("client_id", param.getClientId());
if (StringUtil.isNotEmpty(param.getRedirectUri())) {
model.put("redirect_uri", param.getRedirectUri());
}
if (StringUtil.isNotEmpty(param.getScope())) {
model.put("scope", param.getScope());
}
if (StringUtil.isNotEmpty(param.getState())) {
model.put("state", param.getState());
}
if (StringUtil.isNotEmpty(param.getNonce())) {
model.put("nonce", param.getNonce());
}
if (StringUtil.isNotEmpty(param.getResponseType())) {
model.put("response_type", param.getResponseType());
}
if (StringUtil.isNotEmpty(param.getCodeChallengeMethod()) || StringUtil.isNotEmpty(param.getCodeChallenge())) {
model.put("code_challenge_method", param.getCodeChallengeMethod());
model.put("code_challenge", param.getCodeChallenge());
}
if (StringUtil.isNotEmpty(param.getAutoapprove())) {
model.put("autoapprove", param.getAutoapprove());
}
String uriParams = URLUtil.buildQuery(model, StandardCharsets.UTF_8);
if (authorizeUrl.contains("?")) {
authorizeUrl = authorizeUrl + (authorizeUrl.endsWith("?") ? "" : "&") + uriParams;
} else {
authorizeUrl = authorizeUrl + "?" + uriParams;
}
return authorizeUrl;
}
public static String generateClientId() {
return RandomUtil.randomString(32);
}
public static String generateClientSecret() {
return RandomUtil.randomString(40);
}
public static boolean isOidcProtocol(String scopes) {
Set<String> scopeList = OauthUtil.convertStrToList(scopes);
return scopeList.contains("openid");
}
/**
* Suitable for oauth 2.0 pkce enhanced protocol
*
* @param codeChallengeMethod s256 / plain
* @param codeVerifier code verifier, Generated by the developer
* @return code challenge
*/
public static String generateCodeChallenge(String codeChallengeMethod, String codeVerifier) {
if ("S256".equalsIgnoreCase(codeChallengeMethod)) {
// https://tools.ietf.org/html/rfc7636#section-4.2
// code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
return Base64.encodeUrlSafe(SecureUtil.sha256().digest(codeVerifier));
} else {
return codeVerifier;
}
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import cn.hutool.core.util.URLUtil;
import com.xkcoding.json.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class ObjectUtils {
/**
* If the {@code str} does not end in {@code suffix}, then {@code suffix} is appended after {@code str};
* If {@code suffix} is already included, return directly to {@code str}
*
* @param str str
* @param suffix Content to be added
* @return String
*/
public static String appendIfNotEndWith(String str, String suffix) {
if (StringUtil.isEmpty(str) || StringUtil.isEmpty(suffix)) {
return str;
}
return str.endsWith(suffix) ? str : str + suffix;
}
/**
* Map to string, the format of the converted string is {@code xxx=xxx&xxx=xxx}
*
* @param params Map to be converted
* @param encode Whether to encode the value of the map
* @return String
*/
public static String parseMapToString(Map<String, String> params, boolean encode) {
if (null == params || params.isEmpty()) {
return "";
}
List<String> paramList = new ArrayList<>();
params.forEach((k, v) -> {
if (null == v) {
paramList.add(k + "=");
} else {
paramList.add(k + "=" + (encode ? URLUtil.encode(v) : v));
}
});
return String.join("&", paramList);
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.fujieid.jap.core.util.RequestUtil;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.InvalidTokenException;
import com.fujieid.jap.ids.model.AccessToken;
import com.fujieid.jap.ids.model.ClientDetail;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.model.enums.TokenAuthMethod;
import com.xkcoding.json.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class TokenUtil {
/**
* Get access token from request
*
* @param request request
* @return String
*/
public static String getAccessToken(HttpServletRequest request) {
List<TokenAuthMethod> tokenAuthMethods = JapIds.getIdsConfig().getTokenAuthMethods();
if (ObjectUtil.isEmpty(tokenAuthMethods)) {
tokenAuthMethods = Collections.singletonList(TokenAuthMethod.ALL);
}
if (tokenAuthMethods.contains(TokenAuthMethod.ALL)) {
String accessToken = getAccessTokenFromUrl(request);
if (StringUtil.isEmpty(accessToken)) {
accessToken = getAccessTokenFromHeader(request);
if (StringUtil.isEmpty(accessToken)) {
accessToken = getAccessTokenFromCookie(request);
}
}
return accessToken;
} else {
if (tokenAuthMethods.contains(TokenAuthMethod.TOKEN_URL)) {
String accessToken = getAccessTokenFromUrl(request);
if (accessToken != null) {
return accessToken;
}
}
if (tokenAuthMethods.contains(TokenAuthMethod.TOKEN_HEADER)) {
String accessToken = getAccessTokenFromHeader(request);
if (accessToken != null) {
return accessToken;
}
}
if (tokenAuthMethods.contains(TokenAuthMethod.TOKEN_COOKIE)) {
return getAccessTokenFromCookie(request);
}
}
return null;
}
private static String getAccessTokenFromUrl(HttpServletRequest request) {
String accessToken = RequestUtil.getParam(IdsConsts.ACCESS_TOKEN, request);
if (StringUtil.isNotEmpty(accessToken)) {
return accessToken;
}
return null;
}
private static String getAccessTokenFromHeader(HttpServletRequest request) {
String accessToken = RequestUtil.getHeader(IdsConsts.AUTHORIZATION_HEADER_NAME, request);
return BearerToken.parse(accessToken);
}
private static String getAccessTokenFromCookie(HttpServletRequest request) {
return RequestUtil.getCookieVal(request, IdsConsts.ACCESS_TOKEN);
}
public static String createIdToken(ClientDetail clientDetail, UserInfo user, String nonce) {
long idTokenExpiresIn = OauthUtil.getIdTokenExpiresIn(clientDetail.getIdTokenExpiresIn());
return JwtUtil.createJwtToken(clientDetail.getClientId(), user, idTokenExpiresIn, nonce, JapIds.getIdsConfig());
}
public static AccessToken createAccessToken(UserInfo user, ClientDetail clientDetail, String grantType, String scope, String nonce) {
String clientId = clientDetail.getClientId();
long accessTokenExpiresIn = OauthUtil.getAccessTokenExpiresIn(clientDetail.getAccessTokenExpiresIn());
long refreshTokenExpiresIn = OauthUtil.getAccessTokenExpiresIn(clientDetail.getRefreshTokenExpiresIn());
String accessTokenStr = JwtUtil.createJwtToken(clientId, user, accessTokenExpiresIn, nonce, JapIds.getIdsConfig());
String refreshTokenStr = SecureUtil.sha256(clientId.concat(scope).concat(System.currentTimeMillis() + ""));
AccessToken accessToken = new AccessToken();
accessToken.setAccessToken(accessTokenStr);
accessToken.setRefreshToken(refreshTokenStr);
accessToken.setGrantType(grantType);
if (null != user) {
accessToken.setUserName(user.getUsername());
accessToken.setUserId(user.getId());
}
accessToken.setClientId(clientId);
accessToken.setScope(scope);
accessToken.setRefreshTokenExpiresIn(refreshTokenExpiresIn);
accessToken.setAccessTokenExpiresIn(accessTokenExpiresIn);
accessToken.setAccessTokenExpiration(OauthUtil.getAccessTokenExpiresAt(accessTokenExpiresIn));
accessToken.setRefreshTokenExpiration(OauthUtil.getRefreshTokenExpiresAt(refreshTokenExpiresIn));
String token = IdsConsts.OAUTH_ACCESS_TOKEN_CACHE_KEY + accessTokenStr;
String rtoken = IdsConsts.OAUTH_REFRESH_TOKEN_CACHE_KEY + refreshTokenStr;
JapIds.getContext().getCache().set(token, accessToken, accessTokenExpiresIn * 1000);
JapIds.getContext().getCache().set(rtoken, accessToken, refreshTokenExpiresIn * 1000);
return accessToken;
}
public static AccessToken refreshAccessToken(UserInfo user, ClientDetail clientDetail, AccessToken accessToken, String nonce) {
String rawToken = accessToken.getAccessToken();
String accessTokenStr = JwtUtil.createJwtToken(clientDetail.getClientId(), user, clientDetail.getAccessTokenExpiresIn(), nonce, JapIds.getIdsConfig());
accessToken.setAccessToken(accessTokenStr);
accessToken.setAccessTokenExpiresIn(clientDetail.getAccessTokenExpiresIn());
accessToken.setAccessTokenExpiration(OauthUtil.getAccessTokenExpiresAt(clientDetail.getAccessTokenExpiresIn()));
String tokenCacheKey = IdsConsts.OAUTH_ACCESS_TOKEN_CACHE_KEY + accessTokenStr;
JapIds.getContext().getCache().set(tokenCacheKey, accessTokenStr, clientDetail.getAccessTokenExpiresIn() * 1000);
String rawTokenCacheKey = IdsConsts.OAUTH_ACCESS_TOKEN_CACHE_KEY + rawToken;
JapIds.getContext().getCache().removeKey(rawTokenCacheKey);
return accessToken;
}
public static AccessToken createClientCredentialsAccessToken(ClientDetail clientDetail, String grantType, String scope, String nonce) {
return createAccessToken(null, clientDetail, grantType, scope, nonce);
}
public static void invalidateToken(HttpServletRequest request) {
String accessTokenStr = TokenUtil.getAccessToken(request);
AccessToken accessToken = TokenUtil.getByAccessToken(accessTokenStr);
if (null != accessToken) {
String token = IdsConsts.OAUTH_ACCESS_TOKEN_CACHE_KEY + accessTokenStr;
String rtoken = IdsConsts.OAUTH_REFRESH_TOKEN_CACHE_KEY + accessToken.getRefreshToken();
JapIds.getContext().getCache().removeKey(token);
JapIds.getContext().getCache().removeKey(rtoken);
}
}
public static void validateAccessToken(String accessToken) {
AccessToken token = getByAccessToken(accessToken);
if (token == null) {
throw new InvalidTokenException(ErrorResponse.INVALID_TOKEN);
}
LocalDateTime nowDateTime = DateUtil.nowDate();
if (token.getAccessTokenExpiration().isBefore(nowDateTime)) {
throw new InvalidTokenException(ErrorResponse.EXPIRED_TOKEN);
}
}
public static void validateRefreshToken(String refreshToken) {
AccessToken token = getByRefreshToken(refreshToken);
if (token == null) {
throw new InvalidTokenException(ErrorResponse.INVALID_TOKEN);
}
LocalDateTime nowDateTime = DateUtil.nowDate();
if (token.getRefreshTokenExpiration().isBefore(nowDateTime)) {
throw new InvalidTokenException(ErrorResponse.EXPIRED_TOKEN);
}
}
public static AccessToken getByAccessToken(String accessToken) {
if (null == accessToken) {
return null;
}
accessToken = BearerToken.parse(accessToken);
String token = IdsConsts.OAUTH_ACCESS_TOKEN_CACHE_KEY + accessToken;
return (AccessToken) JapIds.getContext().getCache().get(token);
}
public static AccessToken getByRefreshToken(String refreshToken) {
if (null == refreshToken) {
return null;
}
String token = IdsConsts.OAUTH_REFRESH_TOKEN_CACHE_KEY + refreshToken;
return (AccessToken) JapIds.getContext().getCache().get(token);
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Tools of ids
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
package com.fujieid.jap.ids.util;

View File

@ -0,0 +1,286 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujieid.jap.ids.util;
import com.fujieid.jap.ids.config.IdsConfig;
import com.fujieid.jap.ids.config.JwtConfig;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.model.enums.JwtVerificationType;
import com.fujieid.jap.ids.model.enums.TokenSigningAlg;
import org.junit.Test;
import java.util.Map;
/**
* https://mkjwk.org/
*/
public class JwtUtilTest {
String clientId = "xxxxxxx";
UserInfo userInfo = new UserInfo();
String nonce = "asdasd";
String issuer = "http://www.baidu.com";
Long tokenExpireIn = 365 * 24 * 60 * 60L;
String rs256JwksJson = "{\n" +
" \"keys\": [\n" +
" {\n" +
" \"p\": \"v5G0QPkr9zi1znff2g7p5K1ac1F2KNjXmk31Etl0UrRBwHiTxM_MkkldGlxnXWoFL4_cPZZMt_W14Td5qApknLFOh9iRWRPwqlFgC-eQzUjPeYvxjRbtV5QUHtbzrDCLjLiSNyhsLXHyi_yOawD2BS4U6sBWMSJlL2lShU7EAaU\",\n" +
" \"kty\": \"RSA\",\n" +
" \"q\": \"s2X9UeuEWky_io9hFAoHZjBxMBheNAGrHXtWat6zlg2tf_SIKpZ7Xs8C_-kr9Pvj-D428QsOjFZE-EtNBSXoMrvlMk7fGDl9x1dHvLS9GSitkXH2-Wthg8j0j0nfAmyEt94jP-XEkYic1Ok7EfBOPuvL21HO7YuB-cOff9ZGvBk\",\n" +
" \"d\": \"Rj-QBeBdx85VIHkwVY1T94ZeeC_Z6Zw-cz5lk5Msw0U9QhSTWo28-d2lYjK7dhQn-E19JhTbCVE11UuUqENKZmO__yRgO1UJaj2x6vWMtgJptah7m8lI-QW0w6TnVxAHWfRPpKSEfbN4SpeufYf5PYhmmzT0A954Z2o0kqS4iHd0gwNAovOXaxriGXO1CcOQjBFEcm0BdboQZ7CKCoJ1D6S0xZpVFSJg-1AtagY5dzStyekzETO2tQSmVw4ogIoJsIbu3aYwbukmCoULQfJ36D0mPzrTG5oocEbbuCps_vH72VjZORHHAl4hwritFT_jD2bdQHSNMGukga8C0L1WQQ\",\n" +
" \"e\": \"AQAB\",\n" +
" \"use\": \"sig\",\n" +
" \"kid\": \"jap-jwk-keyid\",\n" +
" \"qi\": \"Asr5dZMDvwgquE6uFnDaBC76PY5JUzxQ5wY5oc4nhIm8UxQWwYZTWq-HOWkMB5c99fG1QxLWQKGtsguXfOXoNgnI--yHzLZcXf1XAd0siguaF1cgQIqwRUf4byofE6uJ-2ZON_ezn6Uvly8fDIlgwmKAiiwWvHI4iLqvqOReBgs\",\n" +
" \"dp\": \"oIUzuFnR6FcBqJ8z2KE0haRorUZuLy38A1UdbQz_dqmKiv--OmUw8sc8l3EkP9ctvzvZfVWqtV7TZ4M3koIa6l18A0KKEE0wFVcYlwETiaBgEWYdIm86s27mKS1Og1MuK90gz800UCQx6_DVWX41qAOEDWzbDFLY3JBxUDi-7u0\",\n" +
" \"alg\": \"RS256\",\n" +
" \"dq\": \"MpNSM0IecgapCTsatzeMlnaZsmFsTWUbBJi86CwYnPkGLMiXisoZxcS-p77osYxB3L5NZu8jDtVTZFx2PjlNmN_34ZLyujWbDBPDGaQqm2koZZSnd_GZ8Dk7GRpOULSfRebOMTlpjU3iSPPnv0rsBDkdo5sQp09pOSy5TqTuFCE\",\n" +
" \"n\": \"hj8zFdhYFi-47PO4B4HTRuOLPR_rpZJi66g4JoY4gyhb5v3Q57etSU9BnW9QQNoUMDvhCFSwkz0hgY5HqVj0zOG5s9x2a594UDIinKsm434b-pT6bueYdvM_mIUEKka5pqhy90wTTka42GvM-rBATHPTarq0kPTR1iBtYao8zX-RWmCbdumEWOkMFUGbBkUcOSJWzoLzN161WdYr2kJU5PFraUP3hG9fPpMEtvqd6IwEL-MOVx3nqc7zk3D91E6eU7EaOy8nz8echQLl6Ps34BSwEpgOhaHDD6IJzetW-KorYeC0r0okXhrl0sUVE2c71vKPVVtueJSIH6OwA3dVHQ\"\n" +
" }\n" +
" ]\n" +
"}";
String rs384JwksJson = "{\n" +
" \"keys\": [\n" +
" {\n" +
" \"p\": \"83Ql5zOr0s0xxfoXHs77rd5vwG2_gwudv_jwF-gCOeuA8LOsCyBPFMoqr5JG8ebvLvtDCbThUj7xuZ49fGFD6KCHXMlt9NRtJKguzVn7wuiVjyF1VD0W_Dk4Ay_0X6hpTa39sRM7U3mxt5PgCYOOsKd6QsexNUdqawnE6fRivxM\",\n" +
" \"kty\": \"RSA\",\n" +
" \"q\": \"w2z94Lo3ai1JnTf9jLynEy65zclB9zvAmTm7r6HMfak0oWgPrM_jfiLo-lO8eSd3CdQGaEcGucKoM8fYNLxq6cBisNcm02sk7KAfkjsIzW7JLRbT3ORjG20-fzyG0q0UbijYbEZnkX4D-3AwR-myvPiT1ywYaMUdWel3ZC2nFR8\",\n" +
" \"d\": \"uQGDQOPwYn4_vqLc5nnNyJxPdbltnpM6zAqg65K2k7E9gT7_9u8v1y-GYvrQsELgVcAxGVmURPQctjyqigiSkUvw-_4U4AY3_ZLAqUZ-CFhzkoZhhsnXlZJHjvWWtZqEUsWVzALYK8GiSfcdOpKBNeHiCIgw3d4fcCmGZUlDSAhVPT2CJosGhSIP7K2B9y9ythgDQ_R67FhrtFTZQWml9XkxXFXekhHsNtXVjIXRRmZeKcPfn94-eOnIhApVaVHgFJcJGH94Xbbh-nqs5xy4ObOm8NdGf8-ufDN2zcmr2sLdyhewdEi80JK1DGPP5gQhZfxshonX7GE3Fx5o0hovoQ\",\n" +
" \"e\": \"AQAB\",\n" +
" \"use\": \"sig\",\n" +
" \"kid\": \"jap-jwk-keyid\",\n" +
" \"qi\": \"fMFk60XjrRKxro17rI5MOtgvriNu4BDARSxOQ8c4eDRoM6Wy4g6vUwi6Bj7qM39id8z1sfUnryFJyVzxuSSzLlgawwaLM_LGwep4AkToz0l9Mmm1avl0p_oQl05ywSo0SquGatdiwHaHa80bDyzsA3ebI31w2Rrmjr81nDiUz9k\",\n" +
" \"dp\": \"PaZoAshe9p7nv865FCAuM7Vkb0JbgP_sDrUnd6ZVCf3NRSb6pkakQAuCC7vrI07ruuX97_NSK9WsuOiNgXXQEJS2MpT_t0Qj72h3kaD71Du5w-khIRfnPi_vMz9tjtvC7tzkpXbNSzJCAs77qO0bsTh4CXkwMuHG3Rw4NVahuuk\",\n" +
" \"alg\": \"RS384\",\n" +
" \"dq\": \"MsOdL0MwIeShurVQp75ZqCH7IfmlqRNcdHEK0BS3iezqPwNJDxrxfVKUMnKOAuq9gVASWgQZOyfViZ3gC9Ll8tKG0GkTLNgoP09Y5CNxpeuhVpUXc8nf9L_r_CE85H0RUYxKq9WeEa0qW6ZI5GVQiMYJoVtS--Q4O6Lp4Jv7SwE\",\n" +
" \"n\": \"udkfTpoTbOSKfQMnLqSinnFLENRY3sJqKgYD6rsKanLmDHrvWrACSQPJ9ANXiEklK5ryAVxASNygxDxGeC4R3ofPT6MlgXjLr01vxUM9DW4DHpVQsnm7H8aaxC_9J-ddcVTyylKTqVWZBIai3gBsRZOKgpExRwkf2x5RnS1lDiWtvCO_R_CJawYYopw26XiqSGC_Y9UNysGcBK8pCMx0VhROexEtqD_gl6Lr7LaH9AdftGRihcVSMeoLdqu4CVaVwUiYoNWkGvb9qv0hBnlevG94NiTui5Lyr5sHp8g9DlIqAy7ehPYuXeNdcDY5i3hkl7pLkBy8nhk4mUnNrgayTQ\"\n" +
" }\n" +
" ]\n" +
"}";
String rs512JwksJson = "{\n" +
" \"keys\": [\n" +
" {\n" +
" \"p\": \"8sGhs7HKVhhMbR_SgLWncF5rqmbyDE4vNit-rugcCExJJo5ZvGzxHJ_UQfJvDEB1V90hr2loUhmqkWNd3WLtmr89_yoenlt2MN1lDX_lFL01uEmzmjcDhdL5GgxWg-FykJeR0Os_qJd4rpcODVKQjO_VQxNQwaTAhs__hHn2QdM\",\n" +
" \"kty\": \"RSA\",\n" +
" \"q\": \"qpaibUW1G35-gXniwAoNJMa2-b_LiOMBk4_DCmEIE8m4hRy3hTvXgbqtrMv7Xbd0BR_tQujciwovDaQE9ltOnN3zlKqe9v5xqGpsRTWQ_jLUN-Pq-Ae9BAb3BmA_sJnnojVp_DkfLSI_0VIyIIrjlPnvnrVtBCuiMdQNgGd0HqE\",\n" +
" \"d\": \"c0VcVI7VfRFmdCBkeEmuNGV301cjqlDH4w3cbRQPCHh31kjFeRCbrUtLYGO4m9YBKGMEReu1Uvjz4p3orR0wDanFgqtRVis6dpZD0xIhdGU5emOpAY5K05nNOS7MNEBntoO41jLpHo3-0pGqBMyYnOFFpZ0Jgoq_n9X_cGsBZvcnjEoNM_YnnshoVVNbCrfcxa5UWwuIstl6sSm0PDxtXon-Ydv2MAxTA5ujZy1agCcjjcxXw8thpP6ZJk_graeY0KHIAydNp-6xg0xosnHbydLCA6Iiz1qlRoPr9Oo5KB-RQdWdJFRFFPteEt8pcHKtJZ_ytCe4vYBs0EeC1sLuQQ\",\n" +
" \"e\": \"AQAB\",\n" +
" \"use\": \"sig\",\n" +
" \"kid\": \"jap-jwk-keyid\",\n" +
" \"qi\": \"pCxLgf9KQiNeo8cw3BmX0s9RWaMPL9kaJcE-4kwbIzKSO1oLNd-4a43WnyFb5P7VLnbnR9AXu__DpMVKQKWL5SPkevmNGVf97pc36YkaKh379lF_tpDRgyt9Z0bpykIu_rsTi3bemIvPzcTM6Bgg7flo16k62SOFH9pc5sTurrU\",\n" +
" \"dp\": \"AvxKptEa8fAekIEBr7-MLZ-bp17Yvzn-7qWeSzxji96UT7sUc3LLjFSS4bS_lOD-EHSRw3yCYfAa3urf7qcW0P5lHsw_0CbDz1oJsh7OjHC_RmLxqIXgrzanBaD9N2YAaLLUgkNCZyplu4_0BknrqTAR6V9FcPw7uey48cImOy0\",\n" +
" \"alg\": \"RS512\",\n" +
" \"dq\": \"kUBgH4EaW8XSe_bHv1MPq__T70aDTRRV1Eq1_VFvqkG57wXrsfOpZZoJpbeuWjcKAA8WXEGhAHb0Z74AR7CpeGJ4tF6vqoovRwMPG8MnqXqoPsq_2N_l7tbrYa90q6_wjqrCivQseqbOBjLh4dnBPKmwgcfjgoiQu7LeqDXupuE\",\n" +
" \"n\": \"ocNs3Do6B0506SP6w2POw5LZmCNtmm1IrkLiduDewapB2Z7fziDLpNhPkgK2ouAFFAO7LVmPoRd4xojO1FZxCXR2-hFggI86lOtaTptoln9v-6_J6dbVrOPjk9BLiEiOKr44xJJbbnesMvt4SX2kc_sUkQ9cP7hxa1AT8dOpxeR_riw_bx2VGsDc4dWTLsqIsCxZdyllJE9CFz3UFz60ZPByW0Ai1fuhpc1RFYuxA4Z422Z5xNvev4wwEXNX52Sofnuqebl1y2DdYuLCogxUAOMT4e3fJ-KRFSFZaSwx9lDe5npqxeKNC5TDzv0_4ZZkCOwHSO7IqmugDzEtSDIfsw\"\n" +
" }\n" +
" ]\n" +
"}";
String es256JwksJson = "{\n" +
" \"keys\": [\n" +
" {\n" +
" \"kty\": \"EC\",\n" +
" \"d\": \"G0bXBYlLfzC2fIjR2Rh8YUP1BeyS37oSOTgoFNp6hDU\",\n" +
" \"use\": \"sig\",\n" +
" \"crv\": \"P-256\",\n" +
" \"kid\": \"jap-jwk-keyid\",\n" +
" \"x\": \"gaNg_BBeCHwcbvmI94F9qDwh-EloVd0GOZMDJ03hlnk\",\n" +
" \"y\": \"65NEHBW8cwIEQ1IK8x8uskaRakujNoNoC_T2KjOSVfY\",\n" +
" \"alg\": \"ES256\"\n" +
" }\n" +
" ]\n" +
"}";
String es384JwksJson = "{\n" +
" \"keys\": [\n" +
" {\n" +
" \"kty\": \"EC\",\n" +
" \"d\": \"N_5ZUo-CE-H_zkvTV4w3hrS5QLBf2i9gyGLVUQCTY-8jsY0nJoyy3VQ2Pz2DRXW1\",\n" +
" \"use\": \"sig\",\n" +
" \"crv\": \"P-384\",\n" +
" \"kid\": \"jap-jwk-keyid\",\n" +
" \"x\": \"9u6MR_5wSQIqi84EeZcO5BuHfnpDUgnz-GvFRrU3fe66Z_n6hbEERClb1UhcdVZf\",\n" +
" \"y\": \"aRrJIGToPMeDgboLGUgYa0kvx7KyjlxM8WskEg_eteipXWR-natHIjFwFFSqDb9A\",\n" +
" \"alg\": \"ES384\"\n" +
" }\n" +
" ]\n" +
"}";
String es512JwksJson = "{\n" +
" \"keys\": [\n" +
" {\n" +
" \"kty\": \"EC\",\n" +
" \"d\": \"AHWjfpnoFxayAdKwLse6dsOXDU0mUP5ZnnpyCTWFiqR8QbvCtosRv01hds4yjnLLoqQQ2EU3vsBo9GLc2Z94q5l3\",\n" +
" \"use\": \"sig\",\n" +
" \"crv\": \"P-521\",\n" +
" \"kid\": \"jap-jwk-keyid\",\n" +
" \"x\": \"AF20L_FMTzA6Rl9IEYk7Ww-V06PiiQ7-cZ6p8Hgs8xha2TSC46BoZ3kZH1-aoVJUTlJCuhS5zfWcJVBA7Szx3izd\",\n" +
" \"y\": \"ARTnAphEcl9OtHJd6kDasfjNbPZrzWjt8SJFEHoxMqlwgl4dBAkO82itFfOxpvFYpei02rGSOQtambGA2u1ZwkYc\",\n" +
" \"alg\": \"ES512\"\n" +
" }\n" +
" ]\n" +
"}";
public JwtUtilTest() {
userInfo.setId("1111");
userInfo.setUsername("rd");
}
@Test
public void createJwtTokenFromRs256() {
String jwtToken = JwtUtil.createJwtToken(clientId, userInfo, tokenExpireIn, nonce, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig().setJwksJson(rs256JwksJson)));
System.out.println(jwtToken);
}
@Test
public void parseJwtTokenFromRs256() {
String jwt = "eyJraWQiOiJqYXAtandrLWtleWlkIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwOi8vd3d3LmJhaWR1LmNvbSIsInN1YiI6IjExMTEiLCJhdWQiOiJ4eHh4eHh4IiwiZXhwIjoxNjUwMDMxMDE4LCJpYXQiOjE2MTg0OTUwMTgsIm5vbmNlIjoiYXNkYXNkIiwidXNlcm5hbWUiOiJyZCJ9.YgqeBmlrGeauzEAwPOi_WIjG7SyLieU8sbAq-2Ptqq8bDOg0CZdKnzaU9mr-3iEoOeAzTTXh02jHzEz8hhorxi2PFnjZy4H1HSgNqGZckAvwGnN5aC_tMPhx1I_8XMZ0_ZpRiCAlV1NSedveQbCm1jJVKSCoBSLUA4hCIWAQqAR__M-de08oQ-r3HfhFZkSghbzMOI8fXMLvVLtexQAxjek6hn769x-hi-AW-DVDPB_ifUojV8TUNZWZHNj2kG89rBwLgK5LsXEBFpBFvwtfkBYPJVxiSf3cGLcUPTpipQ8buvaLXojAYwE_MXIRklUm2FMAuodQKDJunExe3rzYjw";
Map<String, Object> jwtInfo = JwtUtil.parseJwtToken(jwt, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(rs256JwksJson)
));
System.out.println(jwtInfo);
}
@Test
public void createJwtTokenFromRs384() {
String jwtToken = JwtUtil.createJwtToken(clientId, userInfo, tokenExpireIn, nonce, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(rs384JwksJson)
.setTokenSigningAlg(TokenSigningAlg.RS384)
));
System.out.println(jwtToken);
}
@Test
public void parseJwtTokenFromRs384() {
String jwt = "eyJraWQiOiJqYXAtandrLWtleWlkIiwiYWxnIjoiUlMzODQifQ.eyJpc3MiOiJodHRwOi8vd3d3LmJhaWR1LmNvbSIsInN1YiI6IjExMTEiLCJhdWQiOiJ4eHh4eHh4IiwiZXhwIjoxNjUwMDMxMDYwLCJpYXQiOjE2MTg0OTUwNjAsIm5vbmNlIjoiYXNkYXNkIiwidXNlcm5hbWUiOiJyZCJ9.fqVFn5Hawa6_sDMP8qN1GDq5TTWPJ8WobxPa_Haw6eapEx36Mgqc2Nc5OaV-ZfzqKgEGRqKuWPQl4_KgxcrnkWD2qOmThGsgLlKE8U3MPNeiyCkGTVJ6fMLwXC1lCZhjP8bSJCMDN1E-RVT1oAPGuHptqVmcaVxYDjakzfy_ofFAItFah9O1L875l172LWSeVbt7tjPkShfnjE0XVHPhs1-mG5FcZL5ScpdhZ3doDK0H1Vf5LJAeapuhL1q_zNeBX2P88kCeTy1zc7bEEWaHlA_WM_qStvrXgcFoAlpuVTOAbipyJwX26vxQTi2fKvAhZHINtL03S1un1AQGuIy3FQ";
Map<String, Object> jwtInfo = JwtUtil.parseJwtToken(jwt, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(rs384JwksJson)
.setTokenSigningAlg(TokenSigningAlg.RS384)
));
System.out.println(jwtInfo);
}
@Test
public void createJwtTokenFromRs512() {
String jwtToken = JwtUtil.createJwtToken(clientId, userInfo, tokenExpireIn, nonce, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(rs512JwksJson)
.setTokenSigningAlg(TokenSigningAlg.RS512)
));
System.out.println(jwtToken);
}
@Test
public void parseJwtTokenFromRs512() {
String jwt = "eyJraWQiOiJqYXAtandrLWtleWlkIiwiYWxnIjoiUlM1MTIifQ.eyJpc3MiOiJodHRwOi8vd3d3LmJhaWR1LmNvbSIsInN1YiI6IjExMTEiLCJhdWQiOiJ4eHh4eHh4IiwiZXhwIjoxNjUwMDMxMDk3LCJpYXQiOjE2MTg0OTUwOTcsIm5vbmNlIjoiYXNkYXNkIiwidXNlcm5hbWUiOiJyZCJ9.ibVDk8CxM6jbNcDGqRc-emjqmRvx4ghhSlzvv6rScC8JDmQGgEotdcmu0N5OaxQt-YZex9daAYBF3Cx09jIvjjot0Iy-0mLf1sT4tjG3K5LQ862LRao3L9MyE3Pzqhe2am9AMaGlGimCdrrAFmufSprTADw7jgLxoVIwY5ou1ei5SAO__RjZo-Nmxp8kTW2ea9zKFuhWEkjmLWdZAi6Iet8ntcSFsa1RFc05Urytu8fbOKlqQwaVLOfiaEQw6QDmd28Q7h_BFP_rbclaHaJHOjv05WMKeH5iOEN0wCz4HlZYKOLfLPzH5pZuutMdueiDdCN5WemGLeekpOcJ0BKDfw";
Map<String, Object> jwtInfo = JwtUtil.parseJwtToken(jwt, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(rs512JwksJson)
.setTokenSigningAlg(TokenSigningAlg.RS512)
));
System.out.println(jwtInfo);
}
@Test
public void createJwtTokenFromEs256() {
String jwtToken = JwtUtil.createJwtToken(clientId, userInfo, tokenExpireIn, nonce, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(es256JwksJson)
.setTokenSigningAlg(TokenSigningAlg.ES256)
));
System.out.println(jwtToken);
}
@Test
public void parseJwtTokenFromEs256() {
String jwt = "eyJraWQiOiJqYXAtandrLWtleWlkIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOm51bGwsInN1YiI6IjExMTEiLCJhdWQiOiJ4eHh4eHh4IiwiZXhwIjoxNjUwMDMxMTIzLCJpYXQiOjE2MTg0OTUxMjMsIm5vbmNlIjoiYXNkYXNkIiwidXNlcm5hbWUiOiJyZCJ9.k1b3DEceONkjvOfyE3oBLoVnMNBafx8cMQPU8971D5Xmksn4nnrJl9v2DinvbCrCTc2MVqznClvuhiQ0c76aXg";
Map<String, Object> jwtInfo = JwtUtil.parseJwtToken(jwt, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(es256JwksJson)
.setTokenSigningAlg(TokenSigningAlg.ES256)
));
System.out.println(jwtInfo);
}
@Test
public void createJwtTokenFromEs384() {
String jwtToken = JwtUtil.createJwtToken(clientId, userInfo, tokenExpireIn, nonce, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(es384JwksJson)
.setTokenSigningAlg(TokenSigningAlg.ES384)
));
System.out.println(jwtToken);
}
@Test
public void parseJwtTokenFromEs384() {
String jwt = "eyJraWQiOiJqYXAtandrLWtleWlkIiwiYWxnIjoiRVMzODQifQ.eyJpc3MiOm51bGwsInN1YiI6IjExMTEiLCJhdWQiOiJ4eHh4eHh4IiwiZXhwIjoxNjE4NTkyMzQxLCJpYXQiOjE2MTg0OTIzNDEsIm5vbmNlIjoiYXNkYXNkIiwidXNlcm5hbWUiOm51bGx9.BhVF8JagDRjpnW8wbsNi-Fik9nVnIq8X5mpnrkpp1f6K7b1vqCuBuzmHYpy2Wwz1eVYUTa3haRay30jFC2OwzwXNMNXIdJ6X5w-KKefKtiFl_YITFQU-WDwAsPGT3ZfI";
Map<String, Object> jwtInfo = JwtUtil.parseJwtToken(jwt, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(es384JwksJson)
.setTokenSigningAlg(TokenSigningAlg.ES384)
));
System.out.println(jwtInfo);
}
@Test
public void createJwtTokenFromEs512() {
String jwtToken = JwtUtil.createJwtToken(clientId, userInfo, tokenExpireIn, nonce, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(es512JwksJson)
.setTokenSigningAlg(TokenSigningAlg.ES512)
));
System.out.println(jwtToken);
}
@Test
public void parseJwtTokenFromEs512() {
String jwt = "eyJraWQiOiJqYXAtandrLWtleWlkIiwiYWxnIjoiRVM1MTIifQ.eyJpc3MiOm51bGwsInN1YiI6IjExMTEiLCJhdWQiOiJ4eHh4eHh4IiwiZXhwIjoxNjE4NTkyNDQwLCJpYXQiOjE2MTg0OTI0NDAsIm5vbmNlIjoiYXNkYXNkIiwidXNlcm5hbWUiOiJyZCJ9.ARZRKO52FZe7MCKy_Kv02IodCIli6y1ZbSlWpe4Qn0sCBNLGzmcK5A0302TM9dZ8yu8TgLK9j7MbW9i6VaeDZmfwAA0_z1J1SBiIwJ38IdhISJItFBQL28KVf02zfFXfKHl2AZ9DU_liyuNsdkuV92Np-sEAoFMpkC_4cSlJqpM6T-oi";
Map<String, Object> jwtInfo = JwtUtil.parseJwtToken(jwt, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(es512JwksJson)
.setTokenSigningAlg(TokenSigningAlg.ES512)
));
System.out.println(jwtInfo);
}
@Test
public void validateJwtToken() {
String jwt = "eyJraWQiOiJqYXAtandrLWtleWlkIiwiYWxnIjoiUlMzODQifQ.eyJpc3MiOiJodHRwOi8vd3d3LmJhaWR1LmNvbSIsInN1YiI6IjExMTEiLCJhdWQiOiJ4eHh4eHh4IiwiZXhwIjoxNjUwMDMxMDYwLCJpYXQiOjE2MTg0OTUwNjAsIm5vbmNlIjoiYXNkYXNkIiwidXNlcm5hbWUiOiJyZCJ9.fqVFn5Hawa6_sDMP8qN1GDq5TTWPJ8WobxPa_Haw6eapEx36Mgqc2Nc5OaV-ZfzqKgEGRqKuWPQl4_KgxcrnkWD2qOmThGsgLlKE8U3MPNeiyCkGTVJ6fMLwXC1lCZhjP8bSJCMDN1E-RVT1oAPGuHptqVmcaVxYDjakzfy_ofFAItFah9O1L875l172LWSeVbt7tjPkShfnjE0XVHPhs1-mG5FcZL5ScpdhZ3doDK0H1Vf5LJAeapuhL1q_zNeBX2P88kCeTy1zc7bEEWaHlA_WM_qStvrXgcFoAlpuVTOAbipyJwX26vxQTi2fKvAhZHINtL03S1un1AQGuIy3FQ";
Map<String, Object> jwtInfo = JwtUtil.validateJwtToken(clientId, userInfo.getId(), jwt, new IdsConfig()
.setIssuer(issuer)
.setJwtConfig(new JwtConfig()
.setJwksJson(es512JwksJson)
.setTokenSigningAlg(TokenSigningAlg.ES512)
.setJwtVerificationType(JwtVerificationType.JWKS)
));
System.out.println(jwtInfo);
}
}