🎨 SSO supports maxAge of custom cookie. Decoupling fastjson.

This commit is contained in:
yadong.zhang 2021-01-21 16:14:00 +08:00
parent fa9c30c692
commit 6202f83fb3
9 changed files with 60 additions and 36 deletions

View File

@ -15,9 +15,10 @@
*/ */
package com.fujieid.jap.core; package com.fujieid.jap.core;
import com.alibaba.fastjson.JSONObject;
import com.fujieid.jap.core.exception.JapUserException; import com.fujieid.jap.core.exception.JapUserException;
import java.util.Map;
/** /**
* Abstract the user-related function interface, which is implemented by the caller business system. * Abstract the user-related function interface, which is implemented by the caller business system.
* *
@ -97,7 +98,7 @@ public interface JapUserService {
* @param userInfo The basic user information returned by the OAuth platform * @param userInfo The basic user information returned by the OAuth platform
* @return When saving successfully, return {@code JapUser}, otherwise return {@code null} * @return When saving successfully, return {@code JapUser}, otherwise return {@code null}
*/ */
default JapUser createAndGetOauth2User(String platform, JSONObject userInfo) { default JapUser createAndGetOauth2User(String platform, Map<String, Object> userInfo) {
throw new JapUserException("JapUserService#createAndGetOauth2User(JSONObject) must be overridden by subclass"); throw new JapUserException("JapUserService#createAndGetOauth2User(JSONObject) must be overridden by subclass");
} }

View File

@ -23,6 +23,12 @@
<version>${jap.version}</version> <version>${jap.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.xkcoding.json</groupId>
<artifactId>simple-json</artifactId>
<version>${simple-json.version}</version>
</dependency>
<dependency> <dependency>
<groupId>commons-cli</groupId> <groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId> <artifactId>commons-cli</artifactId>

View File

@ -20,7 +20,6 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.fujieid.jap.core.AuthenticateConfig; import com.fujieid.jap.core.AuthenticateConfig;
import com.fujieid.jap.core.JapConfig; import com.fujieid.jap.core.JapConfig;
import com.fujieid.jap.core.JapUser; import com.fujieid.jap.core.JapUser;
@ -29,13 +28,13 @@ import com.fujieid.jap.core.exception.JapException;
import com.fujieid.jap.core.exception.JapOauth2Exception; import com.fujieid.jap.core.exception.JapOauth2Exception;
import com.fujieid.jap.core.exception.JapUserException; import com.fujieid.jap.core.exception.JapUserException;
import com.fujieid.jap.core.store.JapUserStore; 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.core.strategy.AbstractJapStrategy;
import com.fujieid.jap.oauth2.pkce.PkceCodeChallengeMethod; import com.fujieid.jap.oauth2.pkce.PkceCodeChallengeMethod;
import com.fujieid.jap.oauth2.pkce.PkceParams; import com.fujieid.jap.oauth2.pkce.PkceParams;
import com.fujieid.jap.oauth2.pkce.PkceUtil; import com.fujieid.jap.oauth2.pkce.PkceUtil;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.xkcoding.json.JsonUtil;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -114,10 +113,10 @@ public class Oauth2Strategy extends AbstractJapStrategy {
protected JapUser getUserInfo(OAuthConfig oAuthConfig, String accessToken) { protected JapUser getUserInfo(OAuthConfig oAuthConfig, String accessToken) {
String userinfoResponse = HttpUtil.post(oAuthConfig.getUserinfoUrl(), ImmutableMap.of("access_token", accessToken)); String userinfoResponse = HttpUtil.post(oAuthConfig.getUserinfoUrl(), ImmutableMap.of("access_token", accessToken));
JSONObject userinfo = JSONObject.parseObject(userinfoResponse); Map<String, Object> userinfo = JsonUtil.toBean(userinfoResponse, Map.class);
if (userinfo.containsKey("error") && StrUtil.isNotBlank(userinfo.getString("error"))) { if (userinfo.containsKey("error") && ObjectUtil.isNotEmpty(userinfo.get("error"))) {
throw new JapOauth2Exception("Oauth2Strategy failed to get userinfo with accessToken." + throw new JapOauth2Exception("Oauth2Strategy failed to get userinfo with accessToken." +
userinfo.getString("error_description") + " " + userinfoResponse); userinfo.get("error_description") + " " + userinfoResponse);
} }
JapUser japUser = this.japUserService.createAndGetOauth2User(oAuthConfig.getPlatform(), userinfo); JapUser japUser = this.japUserService.createAndGetOauth2User(oAuthConfig.getPlatform(), userinfo);
if (ObjectUtil.isNull(japUser)) { if (ObjectUtil.isNull(japUser)) {
@ -141,10 +140,10 @@ public class Oauth2Strategy extends AbstractJapStrategy {
params.put(PkceParams.CODE_VERIFIER, PkceUtil.getCacheCodeVerifier()); params.put(PkceParams.CODE_VERIFIER, PkceUtil.getCacheCodeVerifier());
} }
String tokenResponse = HttpUtil.post(oAuthConfig.getTokenUrl(), params); String tokenResponse = HttpUtil.post(oAuthConfig.getTokenUrl(), params);
JSONObject accessToken = JSONObject.parseObject(tokenResponse); Map<String, Object> accessToken = JsonUtil.toBean(tokenResponse, Map.class);
if (accessToken.containsKey("error") && StrUtil.isNotBlank(accessToken.getString("error"))) { if (accessToken.containsKey("error") && ObjectUtil.isNotEmpty(accessToken.get("error"))) {
throw new JapOauth2Exception("Oauth2Strategy failed to get AccessToken." + throw new JapOauth2Exception("Oauth2Strategy failed to get AccessToken." +
accessToken.getString("error_description") + " " + tokenResponse); accessToken.get("error_description") + " " + tokenResponse);
} }
if (!accessToken.containsKey("access_token")) { if (!accessToken.containsKey("access_token")) {
throw new JapOauth2Exception("Oauth2Strategy failed to get AccessToken." + tokenResponse); throw new JapOauth2Exception("Oauth2Strategy failed to get AccessToken." + tokenResponse);
@ -158,7 +157,7 @@ public class Oauth2Strategy extends AbstractJapStrategy {
"example_parameter":"example_value" "example_parameter":"example_value"
} }
*/ */
return accessToken.getString("access_token"); return (String) accessToken.get("access_token");
} }
protected void redirectToAuthorizationEndPoint(HttpServletResponse response, OAuthConfig oAuthConfig) { protected void redirectToAuthorizationEndPoint(HttpServletResponse response, OAuthConfig oAuthConfig) {
@ -177,7 +176,7 @@ public class Oauth2Strategy extends AbstractJapStrategy {
// Pkce is only applicable to authorization code mode // Pkce is only applicable to authorization code mode
if (Oauth2ResponseType.code == oAuthConfig.getResponseType() && oAuthConfig.isEnablePkce()) { if (Oauth2ResponseType.code == oAuthConfig.getResponseType() && oAuthConfig.isEnablePkce()) {
PkceUtil.addPkceParameters(Optional.ofNullable(oAuthConfig.getCodeChallengeMethod()) PkceUtil.addPkceParameters(Optional.ofNullable(oAuthConfig.getCodeChallengeMethod())
.orElse(PkceCodeChallengeMethod.S256), params); .orElse(PkceCodeChallengeMethod.S256), params);
} }
String query = URLUtil.buildQuery(params, StandardCharsets.UTF_8); String query = URLUtil.buildQuery(params, StandardCharsets.UTF_8);
try { try {

View File

@ -49,7 +49,7 @@ public class OidcStrategy extends Oauth2Strategy {
* @param japConfig japConfig * @param japConfig japConfig
*/ */
public OidcStrategy(JapUserService japUserService, JapConfig japConfig) { public OidcStrategy(JapUserService japUserService, JapConfig japConfig) {
super(japUserService, new SessionJapUserStore(), japConfig); super(japUserService, japConfig);
} }
/** /**

View File

@ -16,11 +16,14 @@
package com.fujieid.jap.oidc; package com.fujieid.jap.oidc;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import com.fujieid.jap.core.exception.OidcException; import com.fujieid.jap.core.exception.OidcException;
import com.xkcoding.json.JsonUtil;
import java.util.Map;
/** /**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
@ -45,17 +48,17 @@ public class OidcUtil {
String discoveryUrl = issuer.concat(DISCOVERY_URL); String discoveryUrl = issuer.concat(DISCOVERY_URL);
HttpResponse httpResponse = HttpRequest.get(discoveryUrl).execute(); HttpResponse httpResponse = HttpRequest.get(discoveryUrl).execute();
JSONObject jsonObject = JSONObject.parseObject(httpResponse.body()); Map<String, Object> oidcDiscoveryInfo = JsonUtil.toBean(httpResponse.body(), Map.class);
if (CollectionUtil.isEmpty(jsonObject)) { if (CollectionUtil.isEmpty(oidcDiscoveryInfo)) {
throw new OidcException("Unable to parse IDP service discovery configuration information."); throw new OidcException("Unable to parse IDP service discovery configuration information.");
} }
return new OidcDiscoveryDto() return new OidcDiscoveryDto()
.setIssuer(jsonObject.getString(OidcDiscoveryParams.ISSUER)) .setIssuer(ObjectUtil.toString(oidcDiscoveryInfo.get(OidcDiscoveryParams.ISSUER)))
.setAuthorizationEndpoint(jsonObject.getString(OidcDiscoveryParams.AUTHORIZATION_ENDPOINT)) .setAuthorizationEndpoint(ObjectUtil.toString(oidcDiscoveryInfo.get(OidcDiscoveryParams.AUTHORIZATION_ENDPOINT)))
.setTokenEndpoint(jsonObject.getString(OidcDiscoveryParams.TOKEN_ENDPOINT)) .setTokenEndpoint(ObjectUtil.toString(oidcDiscoveryInfo.get(OidcDiscoveryParams.TOKEN_ENDPOINT)))
.setUserinfoEndpoint(jsonObject.getString(OidcDiscoveryParams.USERINFO_ENDPOINT)) .setUserinfoEndpoint(ObjectUtil.toString(oidcDiscoveryInfo.get(OidcDiscoveryParams.USERINFO_ENDPOINT)))
.setEndSessionEndpoint(jsonObject.getString(OidcDiscoveryParams.END_SESSION_ENDPOINT)) .setEndSessionEndpoint(ObjectUtil.toString(oidcDiscoveryInfo.get(OidcDiscoveryParams.END_SESSION_ENDPOINT)))
.setJwksUri(jsonObject.getString(OidcDiscoveryParams.JWKS_URI)); .setJwksUri(ObjectUtil.toString(oidcDiscoveryInfo.get(OidcDiscoveryParams.JWKS_URI)));
} }

View File

@ -15,13 +15,7 @@
Single Sign On Single Sign On
</description> </description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>kisso</artifactId> <artifactId>kisso</artifactId>

View File

@ -36,8 +36,8 @@ public class JapSsoHelper {
/** /**
* Write user information into cookie after successful login * Write user information into cookie after successful login
* *
* @param userId 当前登录用户的id * @param userId The ID of the current login user
* @param username 当前登录用户的name * @param username The name of the current login user
* @param japSsoConfig sso config * @param japSsoConfig sso config
* @param request current request * @param request current request
* @param response current response * @param response current response
@ -52,6 +52,7 @@ public class JapSsoHelper {
ssoConfig.setParamReturnUrl(japSsoConfig.getParamReturnUrl()); ssoConfig.setParamReturnUrl(japSsoConfig.getParamReturnUrl());
ssoConfig.setLoginUrl(japSsoConfig.getLoginUrl()); ssoConfig.setLoginUrl(japSsoConfig.getLoginUrl());
ssoConfig.setLogoutUrl(japSsoConfig.getLogoutUrl()); ssoConfig.setLogoutUrl(japSsoConfig.getLogoutUrl());
ssoConfig.setCookieMaxAge(japSsoConfig.getCookieMaxAge());
SSOHelper.setSsoConfig(ssoConfig); SSOHelper.setSsoConfig(ssoConfig);
// set jap cookie // set jap cookie
SSOHelper.setCookie(request, response, SSOHelper.setCookie(request, response,

View File

@ -33,6 +33,10 @@ public class JapSsoConfig {
* The domain name of the cookie. By default, it is the current access domain name. * The domain name of the cookie. By default, it is the current access domain name.
*/ */
private String cookieDomain; private String cookieDomain;
/**
* The validity of the cookie
*/
private int cookieMaxAge = Integer.MAX_VALUE;
/** /**
* Parameter name of callback url after successful login * Parameter name of callback url after successful login
*/ */
@ -86,6 +90,15 @@ public class JapSsoConfig {
return this; return this;
} }
public int getCookieMaxAge() {
return cookieMaxAge;
}
public JapSsoConfig setCookieMaxAge(int cookieMaxAge) {
this.cookieMaxAge = cookieMaxAge;
return this;
}
public String getLogoutUrl() { public String getLogoutUrl() {
return logoutUrl; return logoutUrl;
} }

19
pom.xml
View File

@ -51,7 +51,6 @@
<jap.version>1.0.0</jap.version> <jap.version>1.0.0</jap.version>
<junit.version>4.13.1</junit.version> <junit.version>4.13.1</junit.version>
<fastjson.version>1.2.73</fastjson.version>
<hutool.version>5.5.7</hutool.version> <hutool.version>5.5.7</hutool.version>
<guava.version>RELEASE</guava.version> <guava.version>RELEASE</guava.version>
<javax.servlet.version>4.0.1</javax.servlet.version> <javax.servlet.version>4.0.1</javax.servlet.version>
@ -60,6 +59,7 @@
<slf4j-api.version>1.7.30</slf4j-api.version> <slf4j-api.version>1.7.30</slf4j-api.version>
<jedis.version>3.2.0</jedis.version> <jedis.version>3.2.0</jedis.version>
<kisso.version>3.7.6</kisso.version> <kisso.version>3.7.6</kisso.version>
<simple-json.version>0.0.1</simple-json.version>
</properties> </properties>
<dependencies> <dependencies>
@ -69,11 +69,6 @@
<version>${junit.version}</version> <version>${junit.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
@ -99,6 +94,18 @@
<version>${hutool.version}</version> <version>${hutool.version}</version>
</dependency> </dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- servlet --> <!-- servlet -->
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>