mirror of
https://gitee.com/fujieid/jap.git
synced 2024-11-30 02:27:34 +08:00
🎨 OAuth policy supports PKCE; Add an OIDC module that allows you to automatically adapt OIDC applications through IDP's Issuer
This commit is contained in:
parent
97bbe53eff
commit
95a8e9ac3c
@ -10,11 +10,6 @@ package com.fujieid.jap.core;
|
||||
*/
|
||||
public class JapConfig {
|
||||
|
||||
/**
|
||||
* Save login state in session, defaults to {@code true}
|
||||
*/
|
||||
private boolean session = true;
|
||||
|
||||
/**
|
||||
* After successful login, redirect to {@code successRedirect}. Default is `/`
|
||||
*/
|
||||
@ -25,6 +20,11 @@ public class JapConfig {
|
||||
*/
|
||||
private String successMessage;
|
||||
|
||||
/**
|
||||
* After logout, redirect to {@code logoutRedirect}. Default is `/`
|
||||
*/
|
||||
private String logoutRedirect = "/";
|
||||
|
||||
/**
|
||||
* After failed login, redirect to {@code failureRedirect}. Default is `/error`
|
||||
*/
|
||||
@ -40,15 +40,6 @@ public class JapConfig {
|
||||
*/
|
||||
private Object options;
|
||||
|
||||
public boolean isSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public JapConfig setSession(boolean session) {
|
||||
this.session = session;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSuccessRedirect() {
|
||||
return successRedirect;
|
||||
}
|
||||
@ -85,6 +76,15 @@ public class JapConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getLogoutRedirect() {
|
||||
return logoutRedirect;
|
||||
}
|
||||
|
||||
public JapConfig setLogoutRedirect(String logoutRedirect) {
|
||||
this.logoutRedirect = logoutRedirect;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
@ -0,0 +1,83 @@
|
||||
package com.fujieid.jap.core.exception;
|
||||
|
||||
/**
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2021/1/18 16:36
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class OidcException extends JapException {
|
||||
/**
|
||||
* Constructs a new runtime exception with {@code null} as its
|
||||
* detail message. The cause is not initialized, and may subsequently be
|
||||
* initialized by a call to {@link #initCause}.
|
||||
*/
|
||||
public OidcException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified detail message.
|
||||
* The cause is not initialized, and may subsequently be initialized by a
|
||||
* call to {@link #initCause}.
|
||||
*
|
||||
* @param message the detail message. The detail message is saved for
|
||||
* later retrieval by the {@link #getMessage()} method.
|
||||
*/
|
||||
public OidcException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified detail message and
|
||||
* cause. <p>Note that the detail message associated with
|
||||
* {@code cause} is <i>not</i> automatically incorporated in
|
||||
* this runtime exception's detail message.
|
||||
*
|
||||
* @param message the detail message (which is saved for later retrieval
|
||||
* by the {@link #getMessage()} method).
|
||||
* @param cause the cause (which is saved for later retrieval by the
|
||||
* {@link #getCause()} method). (A <tt>null</tt> value is
|
||||
* permitted, and indicates that the cause is nonexistent or
|
||||
* unknown.)
|
||||
* @since 1.4
|
||||
*/
|
||||
public OidcException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified cause and a
|
||||
* detail message of <tt>(cause==null ? null : cause.toString())</tt>
|
||||
* (which typically contains the class and detail message of
|
||||
* <tt>cause</tt>). This constructor is useful for runtime exceptions
|
||||
* that are little more than wrappers for other throwables.
|
||||
*
|
||||
* @param cause the cause (which is saved for later retrieval by the
|
||||
* {@link #getCause()} method). (A <tt>null</tt> value is
|
||||
* permitted, and indicates that the cause is nonexistent or
|
||||
* unknown.)
|
||||
* @since 1.4
|
||||
*/
|
||||
public OidcException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified detail
|
||||
* message, cause, suppression enabled or disabled, and writable
|
||||
* stack trace enabled or disabled.
|
||||
*
|
||||
* @param message the detail message.
|
||||
* @param cause the cause. (A {@code null} value is permitted,
|
||||
* and indicates that the cause is nonexistent or unknown.)
|
||||
* @param enableSuppression whether or not suppression is enabled
|
||||
* or disabled
|
||||
* @param writableStackTrace whether or not the stack trace should
|
||||
* be writable
|
||||
* @since 1.7
|
||||
*/
|
||||
public OidcException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.fujieid.jap.core.store;
|
||||
|
||||
import com.fujieid.jap.core.JapUser;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Save, delete and obtain the login user information.By default, based on local caching,
|
||||
* developers can use different caching schemes to implement the interface
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2021/1/18 18:50
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface JapUserStore {
|
||||
|
||||
/**
|
||||
* Login completed, save user information to the cache
|
||||
*
|
||||
* @param request current request
|
||||
* @param japUser User information after successful login
|
||||
* @return JapUser
|
||||
*/
|
||||
JapUser save(HttpServletRequest request, JapUser japUser);
|
||||
|
||||
/**
|
||||
* Clear user information from cache
|
||||
*
|
||||
* @param request current request
|
||||
*/
|
||||
void remove(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Get the login user information from the cache, return {@code JapUser} if it exists,
|
||||
* return {@code null} if it is not logged in or the login has expired
|
||||
*
|
||||
* @param request current request
|
||||
* @param response current response
|
||||
* @return JapUser
|
||||
*/
|
||||
JapUser get(HttpServletRequest request, HttpServletResponse response);
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package com.fujieid.jap.core.store;
|
||||
|
||||
import com.fujieid.jap.core.JapConst;
|
||||
import com.fujieid.jap.core.JapUser;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
/**
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2021/1/18 19:03
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class SessionJapUserStore implements JapUserStore {
|
||||
|
||||
/**
|
||||
* Login completed, save user information to the cache
|
||||
*
|
||||
* @param request current request
|
||||
* @param japUser User information after successful login
|
||||
* @return JapUser
|
||||
*/
|
||||
@Override
|
||||
public JapUser save(HttpServletRequest request, JapUser japUser) {
|
||||
HttpSession session = request.getSession();
|
||||
japUser.setPassword(null);
|
||||
session.setAttribute(JapConst.SESSION_USER_KEY, japUser);
|
||||
return japUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear user information from cache
|
||||
*
|
||||
* @param request current request
|
||||
*/
|
||||
@Override
|
||||
public void remove(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession();
|
||||
session.removeAttribute(JapConst.SESSION_USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the login user information from the cache, return {@code JapUser} if it exists,
|
||||
* return {@code null} if it is not logged in or the login has expired
|
||||
*
|
||||
* @param request current request
|
||||
* @param response current response
|
||||
* @return JapUser
|
||||
*/
|
||||
@Override
|
||||
public JapUser get(HttpServletRequest request, HttpServletResponse response) {
|
||||
HttpSession session = request.getSession();
|
||||
return (JapUser) session.getAttribute(JapConst.SESSION_USER_KEY);
|
||||
}
|
||||
}
|
@ -2,13 +2,17 @@ package com.fujieid.jap.core.strategy;
|
||||
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.fujieid.jap.core.*;
|
||||
import com.fujieid.jap.core.AuthenticateConfig;
|
||||
import com.fujieid.jap.core.JapConfig;
|
||||
import com.fujieid.jap.core.JapUser;
|
||||
import com.fujieid.jap.core.JapUserService;
|
||||
import com.fujieid.jap.core.exception.JapException;
|
||||
import com.fujieid.jap.core.exception.JapSocialException;
|
||||
import com.fujieid.jap.core.store.JapUserStore;
|
||||
import com.fujieid.jap.core.store.SessionJapUserStore;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@ -26,6 +30,10 @@ public abstract class AbstractJapStrategy implements JapStrategy {
|
||||
* Abstract the user-related function interface, which is implemented by the caller business system.
|
||||
*/
|
||||
protected JapUserService japUserService;
|
||||
/**
|
||||
* user store
|
||||
*/
|
||||
protected JapUserStore japUserStore;
|
||||
/**
|
||||
* Jap configuration.
|
||||
*/
|
||||
@ -37,8 +45,9 @@ public abstract class AbstractJapStrategy implements JapStrategy {
|
||||
* @param japUserService japUserService
|
||||
* @param japConfig japConfig
|
||||
*/
|
||||
public AbstractJapStrategy(JapUserService japUserService, JapConfig japConfig) {
|
||||
public AbstractJapStrategy(JapUserService japUserService, JapUserStore japUserStore, JapConfig japConfig) {
|
||||
this.japUserService = japUserService;
|
||||
this.japUserStore = null == japUserStore ? new SessionJapUserStore() : japUserStore;
|
||||
this.japConfig = japConfig;
|
||||
}
|
||||
|
||||
@ -50,27 +59,20 @@ public abstract class AbstractJapStrategy implements JapStrategy {
|
||||
* @return boolean
|
||||
*/
|
||||
protected boolean checkSession(HttpServletRequest request, HttpServletResponse response) {
|
||||
if (japConfig.isSession()) {
|
||||
HttpSession session = request.getSession();
|
||||
JapUser sessionUser = (JapUser) session.getAttribute(JapConst.SESSION_USER_KEY);
|
||||
if (null != sessionUser) {
|
||||
try {
|
||||
response.sendRedirect(japConfig.getSuccessRedirect());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
throw new JapException("JAP failed to redirect via HttpServletResponse.", e);
|
||||
}
|
||||
JapUser sessionUser = japUserStore.get(request, response);
|
||||
if (null != sessionUser) {
|
||||
try {
|
||||
response.sendRedirect(japConfig.getSuccessRedirect());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
throw new JapException("JAP failed to redirect via HttpServletResponse.", e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void loginSuccess(JapUser japUser, HttpServletRequest request, HttpServletResponse response) {
|
||||
if (japConfig.isSession()) {
|
||||
HttpSession session = request.getSession();
|
||||
japUser.setPassword(null);
|
||||
session.setAttribute(JapConst.SESSION_USER_KEY, japUser);
|
||||
}
|
||||
japUserStore.save(request, japUser);
|
||||
try {
|
||||
response.sendRedirect(japConfig.getSuccessRedirect());
|
||||
} catch (IOException e) {
|
||||
|
@ -19,11 +19,22 @@ public interface JapStrategy {
|
||||
/**
|
||||
* This function must be overridden by subclasses. In abstract form, it always throws an exception.
|
||||
*
|
||||
* @param config Jap Strategy Configs
|
||||
* @param request The request to authenticate
|
||||
* @param config Authenticate Config
|
||||
* @param request The request to authenticate
|
||||
* @param response The response to authenticate
|
||||
*/
|
||||
default void authenticate(AuthenticateConfig config, HttpServletRequest request, HttpServletResponse response) {
|
||||
throw new JapStrategyException("JapStrategy#authenticate must be overridden by subclass");
|
||||
}
|
||||
|
||||
/**
|
||||
* This function must be overridden by subclasses. In abstract form, it always throws an exception.
|
||||
*
|
||||
* @param config Authenticate Config
|
||||
* @param request The request to authenticate
|
||||
* @param response The response to authenticate
|
||||
*/
|
||||
default void logout(AuthenticateConfig config, HttpServletRequest request, HttpServletResponse response) {
|
||||
throw new JapStrategyException("JapStrategy#logout must be overridden by subclass");
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.fujieid.jap.oauth2;
|
||||
|
||||
import com.fujieid.jap.core.AuthenticateConfig;
|
||||
import com.fujieid.jap.oauth2.pkce.PkceCodeChallengeMethod;
|
||||
|
||||
/**
|
||||
* Configuration file of oauth2 module
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.fujieid.jap.oauth2;
|
||||
|
||||
import cn.hutool.cache.CacheUtil;
|
||||
import cn.hutool.cache.impl.TimedCache;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@ -15,7 +13,12 @@ import com.fujieid.jap.core.JapUserService;
|
||||
import com.fujieid.jap.core.exception.JapException;
|
||||
import com.fujieid.jap.core.exception.JapOauth2Exception;
|
||||
import com.fujieid.jap.core.exception.JapUserException;
|
||||
import com.fujieid.jap.core.store.JapUserStore;
|
||||
import com.fujieid.jap.core.store.SessionJapUserStore;
|
||||
import com.fujieid.jap.core.strategy.AbstractJapStrategy;
|
||||
import com.fujieid.jap.oauth2.pkce.PkceCodeChallengeMethod;
|
||||
import com.fujieid.jap.oauth2.pkce.PkceParams;
|
||||
import com.fujieid.jap.oauth2.pkce.PkceUtil;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@ -25,7 +28,6 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* The OAuth 2.0 authentication strategy authenticates requests using the OAuth 2.0 framework.
|
||||
@ -42,8 +44,6 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public class Oauth2Strategy extends AbstractJapStrategy {
|
||||
|
||||
private static TimedCache<String, String> timedCache = CacheUtil.newTimedCache(TimeUnit.MINUTES.toMillis(5));
|
||||
|
||||
/**
|
||||
* `Strategy` constructor.
|
||||
*
|
||||
@ -51,9 +51,18 @@ public class Oauth2Strategy extends AbstractJapStrategy {
|
||||
* @param japConfig japConfig
|
||||
*/
|
||||
public Oauth2Strategy(JapUserService japUserService, JapConfig japConfig) {
|
||||
super(japUserService, japConfig);
|
||||
super(japUserService, new SessionJapUserStore(), japConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* `Strategy` constructor.
|
||||
*
|
||||
* @param japUserService japUserService
|
||||
* @param japConfig japConfig
|
||||
*/
|
||||
public Oauth2Strategy(JapUserService japUserService, JapUserStore japUserStore, JapConfig japConfig) {
|
||||
super(japUserService, japUserStore, japConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate request by delegating to a service provider using OAuth 2.0.
|
||||
@ -88,7 +97,7 @@ public class Oauth2Strategy extends AbstractJapStrategy {
|
||||
|
||||
}
|
||||
|
||||
private JapUser getUserInfo(OAuthConfig oAuthConfig, String accessToken) {
|
||||
protected JapUser getUserInfo(OAuthConfig oAuthConfig, String accessToken) {
|
||||
String userinfoResponse = HttpUtil.post(oAuthConfig.getUserinfoUrl(), ImmutableMap.of("access_token", accessToken));
|
||||
JSONObject userinfo = JSONObject.parseObject(userinfoResponse);
|
||||
if (userinfo.containsKey("error") && StrUtil.isNotBlank(userinfo.getString("error"))) {
|
||||
@ -102,7 +111,7 @@ public class Oauth2Strategy extends AbstractJapStrategy {
|
||||
return japUser;
|
||||
}
|
||||
|
||||
private String getAccessToken(HttpServletRequest request, OAuthConfig oAuthConfig) {
|
||||
protected String getAccessToken(HttpServletRequest request, OAuthConfig oAuthConfig) {
|
||||
String code = request.getParameter("code");
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("grant_type", oAuthConfig.getGrantType());
|
||||
@ -114,7 +123,7 @@ public class Oauth2Strategy extends AbstractJapStrategy {
|
||||
}
|
||||
// pkce 仅适用于授权码模式
|
||||
if (Oauth2ResponseType.code == oAuthConfig.getResponseType() && oAuthConfig.isEnablePkce()) {
|
||||
params.put("code_verifier", timedCache.get("codeVerifier"));
|
||||
params.put(PkceParams.CODE_VERIFIER, PkceUtil.getCacheCodeVerifier());
|
||||
}
|
||||
String tokenResponse = HttpUtil.post(oAuthConfig.getTokenUrl(), params);
|
||||
JSONObject accessToken = JSONObject.parseObject(tokenResponse);
|
||||
@ -137,7 +146,7 @@ public class Oauth2Strategy extends AbstractJapStrategy {
|
||||
return accessToken.getString("access_token");
|
||||
}
|
||||
|
||||
private void redirectToAuthorizationEndPoint(HttpServletResponse response, OAuthConfig oAuthConfig) {
|
||||
protected void redirectToAuthorizationEndPoint(HttpServletResponse response, OAuthConfig oAuthConfig) {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("response_type", oAuthConfig.getResponseType());
|
||||
params.put("client_id", oAuthConfig.getClientId());
|
||||
@ -152,17 +161,8 @@ public class Oauth2Strategy extends AbstractJapStrategy {
|
||||
}
|
||||
// Pkce is only applicable to authorization code mode
|
||||
if (Oauth2ResponseType.code == oAuthConfig.getResponseType() && oAuthConfig.isEnablePkce()) {
|
||||
PkceCodeChallengeMethod codeChallengeMethod = Optional.ofNullable(oAuthConfig.getCodeChallengeMethod())
|
||||
.orElse(PkceCodeChallengeMethod.S256);
|
||||
if (PkceCodeChallengeMethod.S256 == oAuthConfig.getCodeChallengeMethod()) {
|
||||
String codeVerifier = Oauth2Util.generateCodeVerifier();
|
||||
String codeChallenge = Oauth2Util.generateCodeChallenge(codeChallengeMethod, codeVerifier);
|
||||
params.put("code_challenge", codeChallenge);
|
||||
params.put("code_challenge_method", codeChallengeMethod);
|
||||
// FIXME 需要考虑分布式环境,例如使用 Redis 缓存
|
||||
timedCache.put("codeVerifier", codeVerifier);
|
||||
|
||||
}
|
||||
PkceUtil.addPkceParameters(Optional.ofNullable(oAuthConfig.getCodeChallengeMethod())
|
||||
.orElse(PkceCodeChallengeMethod.S256), params);
|
||||
}
|
||||
String query = URLUtil.buildQuery(params, StandardCharsets.UTF_8);
|
||||
try {
|
||||
|
@ -3,6 +3,7 @@ package com.fujieid.jap.oauth2;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import com.fujieid.jap.oauth2.pkce.PkceCodeChallengeMethod;
|
||||
import org.jose4j.base64url.Base64Url;
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.fujieid.jap.oauth2;
|
||||
package com.fujieid.jap.oauth2.pkce;
|
||||
|
||||
/**
|
||||
* Encryption method of pkce challenge code
|
@ -0,0 +1,29 @@
|
||||
package com.fujieid.jap.oauth2.pkce;
|
||||
|
||||
/**
|
||||
* OAuth PKCE Parameters Registry
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2021/1/18 16:49
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7636#section-6.1" target="_blank">6.1. OAuth Parameters Registry</a>
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface PkceParams {
|
||||
|
||||
/**
|
||||
* {@code code_challenge} - used in Authorization Request.
|
||||
*/
|
||||
String CODE_CHALLENGE = "code_challenge";
|
||||
|
||||
/**
|
||||
* {@code code_challenge_method} - used in Authorization Request.
|
||||
*/
|
||||
String CODE_CHALLENGE_METHOD = "code_challenge_method";
|
||||
|
||||
/**
|
||||
* {@code code_verifier} - used in Token Request.
|
||||
*/
|
||||
String CODE_VERIFIER = "code_verifier";
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.fujieid.jap.oauth2.pkce;
|
||||
|
||||
import cn.hutool.cache.CacheUtil;
|
||||
import cn.hutool.cache.impl.TimedCache;
|
||||
import com.fujieid.jap.oauth2.Oauth2Util;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Proof Key for Code Exchange by OAuth Public Client
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2021/1/18 16:52
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7636" target="_blank">Proof Key for Code Exchange by OAuth Public Clients</a>
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class PkceUtil {
|
||||
|
||||
private static final TimedCache<String, String> timedCache = CacheUtil.newTimedCache(TimeUnit.MINUTES.toMillis(5));
|
||||
|
||||
/**
|
||||
* Create the parameters required by PKCE
|
||||
*
|
||||
* @param pkceCodeChallengeMethod After the pkce enhancement protocol is enabled, the generation method of challenge
|
||||
* code derived from the code verifier sent in the authorization request is `s256` by default
|
||||
* @param params oauth request params
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7636#section-1.1" target="_blank">1.1. Protocol Flow</a>
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7636#section-4.1" target="_blank">4.1. Client Creates a Code Verifier</a>
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7636#section-4.2" target="_blank">4.2. Client Creates the Code Challenge</a>
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7636#section-4.3" target="_blank"> Client Sends the Code Challenge with the Authorization Request</a>
|
||||
*/
|
||||
public static void addPkceParameters(PkceCodeChallengeMethod pkceCodeChallengeMethod, Map<String, Object> params) {
|
||||
if (PkceCodeChallengeMethod.S256 == pkceCodeChallengeMethod) {
|
||||
String codeVerifier = Oauth2Util.generateCodeVerifier();
|
||||
String codeChallenge = Oauth2Util.generateCodeChallenge(pkceCodeChallengeMethod, codeVerifier);
|
||||
params.put(PkceParams.CODE_CHALLENGE, codeChallenge);
|
||||
params.put(PkceParams.CODE_CHALLENGE_METHOD, pkceCodeChallengeMethod);
|
||||
// FIXME 需要考虑分布式环境,例如使用 Redis 缓存
|
||||
timedCache.put(PkceParams.CODE_VERIFIER, codeVerifier);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@code code_verifier} in the cache
|
||||
*
|
||||
* @return {@code code_verifier}
|
||||
*/
|
||||
public static String getCacheCodeVerifier() {
|
||||
return timedCache.get(PkceParams.CODE_VERIFIER);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.fujieid.jap.oauth2;
|
||||
|
||||
import com.fujieid.jap.oauth2.pkce.PkceCodeChallengeMethod;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
26
jap-oidc/pom.xml
Normal file
26
jap-oidc/pom.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>jap</artifactId>
|
||||
<groupId>com.fujieid</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>jap-oidc</artifactId>
|
||||
<name>jap-oidc</name>
|
||||
<description>
|
||||
OpenID Connect
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fujieid</groupId>
|
||||
<artifactId>jap-oauth2</artifactId>
|
||||
<version>${jap.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
32
jap-oidc/src/main/java/com/fujieid/jap/oidc/OidcConfig.java
Normal file
32
jap-oidc/src/main/java/com/fujieid/jap/oidc/OidcConfig.java
Normal file
@ -0,0 +1,32 @@
|
||||
package com.fujieid.jap.oidc;
|
||||
|
||||
import com.fujieid.jap.oauth2.OAuthConfig;
|
||||
|
||||
/**
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2021/1/18 16:23
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class OidcConfig extends OAuthConfig {
|
||||
private String issuer;
|
||||
private String userNameAttribute;
|
||||
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public OidcConfig setIssuer(String issuer) {
|
||||
this.issuer = issuer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUserNameAttribute() {
|
||||
return userNameAttribute;
|
||||
}
|
||||
|
||||
public OidcConfig setUserNameAttribute(String userNameAttribute) {
|
||||
this.userNameAttribute = userNameAttribute;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package com.fujieid.jap.oidc;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* OpenID Provider Issuer discovery is the process of determining the location of the OpenID Provider.
|
||||
* <p>
|
||||
* For the properties defined by this class, please refer to [3. OpenID Provider Metadata]
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2020/10/26 14:47
|
||||
* @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#ProviderMetadata" target="_blank">3. OpenID Provider Metadata</a>
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class OidcDiscoveryDto implements Serializable {
|
||||
|
||||
/**
|
||||
* Identity provider URL
|
||||
*/
|
||||
private String issuer;
|
||||
/**
|
||||
* URL of the OP's OAuth 2.0 Authorization Endpoint
|
||||
*/
|
||||
private String authorizationEndpoint;
|
||||
/**
|
||||
* URL of the OP's OAuth 2.0 Token Endpoint
|
||||
*/
|
||||
private String tokenEndpoint;
|
||||
/**
|
||||
* URL of the OP's UserInfo Endpoint
|
||||
*/
|
||||
private String userinfoEndpoint;
|
||||
/**
|
||||
* URL of the OP's Logout Endpoint
|
||||
*/
|
||||
private String endSessionEndpoint;
|
||||
/**
|
||||
* URL of the OP's JSON Web Key Set [JWK] document
|
||||
*
|
||||
* @see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#JWK" target="_blank">JWK</a>
|
||||
*/
|
||||
private String jwksUri;
|
||||
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public OidcDiscoveryDto setIssuer(String issuer) {
|
||||
this.issuer = issuer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAuthorizationEndpoint() {
|
||||
return authorizationEndpoint;
|
||||
}
|
||||
|
||||
public OidcDiscoveryDto setAuthorizationEndpoint(String authorizationEndpoint) {
|
||||
this.authorizationEndpoint = authorizationEndpoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTokenEndpoint() {
|
||||
return tokenEndpoint;
|
||||
}
|
||||
|
||||
public OidcDiscoveryDto setTokenEndpoint(String tokenEndpoint) {
|
||||
this.tokenEndpoint = tokenEndpoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUserinfoEndpoint() {
|
||||
return userinfoEndpoint;
|
||||
}
|
||||
|
||||
public OidcDiscoveryDto setUserinfoEndpoint(String userinfoEndpoint) {
|
||||
this.userinfoEndpoint = userinfoEndpoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getEndSessionEndpoint() {
|
||||
return endSessionEndpoint;
|
||||
}
|
||||
|
||||
public OidcDiscoveryDto setEndSessionEndpoint(String endSessionEndpoint) {
|
||||
this.endSessionEndpoint = endSessionEndpoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getJwksUri() {
|
||||
return jwksUri;
|
||||
}
|
||||
|
||||
public OidcDiscoveryDto setJwksUri(String jwksUri) {
|
||||
this.jwksUri = jwksUri;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.fujieid.jap.oidc;
|
||||
|
||||
/**
|
||||
* Property name of IDP service discovery
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2021/1/18 17:12
|
||||
* @see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata" target="_blank">3. OpenID Provider Metadata</a>
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface OidcDiscoveryParams {
|
||||
|
||||
/**
|
||||
* Identity provider URL
|
||||
*/
|
||||
String ISSUER = "issuer";
|
||||
/**
|
||||
* URL of the OP's OAuth 2.0 Authorization Endpoint
|
||||
*/
|
||||
String AUTHORIZATION_ENDPOINT = "authorization_endpoint";
|
||||
/**
|
||||
* URL of the OP's OAuth 2.0 Token Endpoint
|
||||
*/
|
||||
String TOKEN_ENDPOINT = "token_endpoint";
|
||||
/**
|
||||
* URL of the OP's UserInfo Endpoint
|
||||
*/
|
||||
String USERINFO_ENDPOINT = "userinfo_endpoint";
|
||||
/**
|
||||
* URL of the OP's Logout Endpoint
|
||||
*/
|
||||
String END_SESSION_ENDPOINT = "end_session_endpoint";
|
||||
/**
|
||||
* URL of the OP's JSON Web Key Set [JWK] document
|
||||
*
|
||||
* @see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#JWK" target="_blank">JWK</a>
|
||||
*/
|
||||
String JWKS_URI = "jwks_uri";
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package com.fujieid.jap.oidc;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.fujieid.jap.core.AuthenticateConfig;
|
||||
import com.fujieid.jap.core.JapConfig;
|
||||
import com.fujieid.jap.core.JapUserService;
|
||||
import com.fujieid.jap.core.exception.JapOauth2Exception;
|
||||
import com.fujieid.jap.core.store.JapUserStore;
|
||||
import com.fujieid.jap.core.store.SessionJapUserStore;
|
||||
import com.fujieid.jap.oauth2.OAuthConfig;
|
||||
import com.fujieid.jap.oauth2.Oauth2Strategy;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol.
|
||||
* It enables Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server,
|
||||
* as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2021/1/18 16:27
|
||||
* @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>
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class OidcStrategy extends Oauth2Strategy {
|
||||
|
||||
/**
|
||||
* `Strategy` constructor.
|
||||
*
|
||||
* @param japUserService japUserService
|
||||
* @param japConfig japConfig
|
||||
*/
|
||||
public OidcStrategy(JapUserService japUserService, JapConfig japConfig) {
|
||||
super(japUserService, new SessionJapUserStore(), japConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* `Strategy` constructor.
|
||||
*
|
||||
* @param japUserService japUserService
|
||||
* @param japConfig japConfig
|
||||
*/
|
||||
public OidcStrategy(JapUserService japUserService, JapUserStore japUserStore, JapConfig japConfig) {
|
||||
super(japUserService, japUserStore, japConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate request by delegating to a service provider using OAuth 2.0.
|
||||
*
|
||||
* @param config OAuthConfig
|
||||
* @param request The request to authenticate
|
||||
* @param response The response to authenticate
|
||||
*/
|
||||
@Override
|
||||
public void authenticate(AuthenticateConfig config, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
this.checkAuthenticateConfig(config, OidcConfig.class);
|
||||
OidcConfig oidcConfig = (OidcConfig) config;
|
||||
this.checkOidcConfig(oidcConfig);
|
||||
|
||||
String issuer = oidcConfig.getIssuer();
|
||||
|
||||
OidcDiscoveryDto discoveryDto = OidcUtil.getOidcDiscovery(issuer);
|
||||
|
||||
oidcConfig.setAuthorizationUrl(discoveryDto.getAuthorizationEndpoint())
|
||||
.setTokenUrl(discoveryDto.getTokenEndpoint())
|
||||
.setUserinfoUrl(discoveryDto.getUserinfoEndpoint());
|
||||
|
||||
OAuthConfig oAuthConfig = BeanUtil.copyProperties(oidcConfig, OAuthConfig.class);
|
||||
super.authenticate(oAuthConfig, request, response);
|
||||
}
|
||||
|
||||
private void checkOidcConfig(OidcConfig oidcConfig) {
|
||||
if (ObjectUtil.isNull(oidcConfig.getIssuer())) {
|
||||
throw new JapOauth2Exception("OidcStrategy requires a issuer option");
|
||||
}
|
||||
}
|
||||
}
|
47
jap-oidc/src/main/java/com/fujieid/jap/oidc/OidcUtil.java
Normal file
47
jap-oidc/src/main/java/com/fujieid/jap/oidc/OidcUtil.java
Normal file
@ -0,0 +1,47 @@
|
||||
package com.fujieid.jap.oidc;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fujieid.jap.core.exception.OidcException;
|
||||
|
||||
/**
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @date 2020/12/3 14:13
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class OidcUtil {
|
||||
|
||||
private static final String DISCOVERY_URL = "/.well-known/openid-configuration";
|
||||
|
||||
/**
|
||||
* Get the IDP service configuration
|
||||
*
|
||||
* @param issuer IDP identity providers, such as `https://sign.fujieid.com`
|
||||
* @return OidcDiscoveryDto
|
||||
*/
|
||||
public static OidcDiscoveryDto getOidcDiscovery(String issuer) {
|
||||
if (StrUtil.isBlank(issuer)) {
|
||||
throw new OidcException("Missing IDP Discovery Url.");
|
||||
}
|
||||
String discoveryUrl = issuer.concat(DISCOVERY_URL);
|
||||
|
||||
HttpResponse httpResponse = HttpRequest.get(discoveryUrl).execute();
|
||||
JSONObject jsonObject = JSONObject.parseObject(httpResponse.body());
|
||||
if (CollectionUtil.isEmpty(jsonObject)) {
|
||||
throw new OidcException("Unable to parse IDP service discovery configuration information.");
|
||||
}
|
||||
return new OidcDiscoveryDto()
|
||||
.setIssuer(jsonObject.getString(OidcDiscoveryParams.ISSUER))
|
||||
.setAuthorizationEndpoint(jsonObject.getString(OidcDiscoveryParams.AUTHORIZATION_ENDPOINT))
|
||||
.setTokenEndpoint(jsonObject.getString(OidcDiscoveryParams.TOKEN_ENDPOINT))
|
||||
.setUserinfoEndpoint(jsonObject.getString(OidcDiscoveryParams.USERINFO_ENDPOINT))
|
||||
.setEndSessionEndpoint(jsonObject.getString(OidcDiscoveryParams.END_SESSION_ENDPOINT))
|
||||
.setJwksUri(jsonObject.getString(OidcDiscoveryParams.JWKS_URI));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol.
|
||||
* It enables Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server,
|
||||
* as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @date 2021/1/18 16:19
|
||||
* @version 1.0.0
|
||||
* @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>
|
||||
* @since 1.0.0
|
||||
*/
|
||||
package com.fujieid.jap.oidc;
|
@ -5,6 +5,8 @@ import com.fujieid.jap.core.JapConfig;
|
||||
import com.fujieid.jap.core.JapUser;
|
||||
import com.fujieid.jap.core.JapUserService;
|
||||
import com.fujieid.jap.core.exception.JapUserException;
|
||||
import com.fujieid.jap.core.store.JapUserStore;
|
||||
import com.fujieid.jap.core.store.SessionJapUserStore;
|
||||
import com.fujieid.jap.core.strategy.AbstractJapStrategy;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@ -22,12 +24,23 @@ import javax.servlet.http.HttpServletResponse;
|
||||
public class SimpleStrategy extends AbstractJapStrategy {
|
||||
|
||||
/**
|
||||
* Initialization strategy
|
||||
* `Strategy` constructor.
|
||||
*
|
||||
* @param japUserService Required, implement user operations
|
||||
* @param japUserService japUserService
|
||||
* @param japConfig japConfig
|
||||
*/
|
||||
public SimpleStrategy(JapUserService japUserService, JapConfig japConfig) {
|
||||
super(japUserService, japConfig);
|
||||
super(japUserService, new SessionJapUserStore(), japConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* `Strategy` constructor.
|
||||
*
|
||||
* @param japUserService japUserService
|
||||
* @param japConfig japConfig
|
||||
*/
|
||||
public SimpleStrategy(JapUserService japUserService, JapUserStore japUserStore, JapConfig japConfig) {
|
||||
super(japUserService, japUserStore, japConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,10 +4,15 @@ import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fujieid.jap.core.*;
|
||||
import com.fujieid.jap.core.AuthenticateConfig;
|
||||
import com.fujieid.jap.core.JapConfig;
|
||||
import com.fujieid.jap.core.JapUser;
|
||||
import com.fujieid.jap.core.JapUserService;
|
||||
import com.fujieid.jap.core.exception.JapException;
|
||||
import com.fujieid.jap.core.exception.JapSocialException;
|
||||
import com.fujieid.jap.core.exception.JapUserException;
|
||||
import com.fujieid.jap.core.store.JapUserStore;
|
||||
import com.fujieid.jap.core.store.SessionJapUserStore;
|
||||
import com.fujieid.jap.core.strategy.AbstractJapStrategy;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
@ -16,11 +21,9 @@ import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.request.AuthRequest;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
@ -43,12 +46,23 @@ public class SocialStrategy extends AbstractJapStrategy {
|
||||
private AuthStateCache authStateCache;
|
||||
|
||||
/**
|
||||
* Initialization strategy
|
||||
* `Strategy` constructor.
|
||||
*
|
||||
* @param japUserService Required, implement user operations
|
||||
* @param japUserService japUserService
|
||||
* @param japConfig japConfig
|
||||
*/
|
||||
public SocialStrategy(JapUserService japUserService, JapConfig japConfig) {
|
||||
super(japUserService, japConfig);
|
||||
super(japUserService, new SessionJapUserStore(), japConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* `Strategy` constructor.
|
||||
*
|
||||
* @param japUserService japUserService
|
||||
* @param japConfig japConfig
|
||||
*/
|
||||
public SocialStrategy(JapUserService japUserService, JapUserStore japUserStore, JapConfig japConfig) {
|
||||
super(japUserService, japUserStore, japConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,8 +73,8 @@ public class SocialStrategy extends AbstractJapStrategy {
|
||||
* @param japUserService Required, implement user operations
|
||||
* @param authStateCache Optional, custom cache implementation class
|
||||
*/
|
||||
public SocialStrategy(JapUserService japUserService, JapConfig japConfig, AuthStateCache authStateCache) {
|
||||
this(japUserService, japConfig);
|
||||
public SocialStrategy(JapUserService japUserService, JapUserStore japUserStore, JapConfig japConfig, AuthStateCache authStateCache) {
|
||||
this(japUserService, japUserStore, japConfig);
|
||||
this.authStateCache = authStateCache;
|
||||
}
|
||||
|
||||
@ -110,13 +124,13 @@ public class SocialStrategy extends AbstractJapStrategy {
|
||||
* @param authCallback Parse the parameters obtained by the third party callback request
|
||||
*/
|
||||
private void login(HttpServletRequest request, HttpServletResponse response, String source, AuthRequest authRequest, AuthCallback authCallback) {
|
||||
AuthResponse<AuthUser> authUserAuthResponse = authRequest.login(authCallback);
|
||||
AuthResponse<?> authUserAuthResponse = authRequest.login(authCallback);
|
||||
if (!authUserAuthResponse.ok() || ObjectUtil.isNull(authUserAuthResponse.getData())) {
|
||||
throw new JapUserException("Third party login of `" + source + "` cannot obtain user information. "
|
||||
+ authUserAuthResponse.getMsg());
|
||||
}
|
||||
|
||||
AuthUser socialUser = authUserAuthResponse.getData();
|
||||
AuthUser socialUser = (AuthUser) authUserAuthResponse.getData();
|
||||
JapUser japUser = japUserService.getByPlatformAndUid(source, socialUser.getUuid());
|
||||
if (ObjectUtil.isNull(japUser)) {
|
||||
japUser = japUserService.createAndGetSocialUser(socialUser);
|
||||
|
4
pom.xml
4
pom.xml
@ -31,6 +31,8 @@
|
||||
<module>jap-simple</module>
|
||||
<module>jap-social</module>
|
||||
<module>jap-oauth2</module>
|
||||
<module>jap-sso</module>
|
||||
<module>jap-oidc</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
@ -55,6 +57,8 @@
|
||||
<javax.servlet.version>4.0.1</javax.servlet.version>
|
||||
<justauth.version>1.15.9</justauth.version>
|
||||
<jose4j.version>0.7.1</jose4j.version>
|
||||
<slf4j-api.version>1.7.30</slf4j-api.version>
|
||||
<jedis.version>3.2.0</jedis.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
Loading…
Reference in New Issue
Block a user