From 6cad9f0ca2908aa080afb3a47143faf98617f86a Mon Sep 17 00:00:00 2001 From: MaxKey Date: Thu, 12 Aug 2021 15:02:38 +0800 Subject: [PATCH] oauth2 Approval fix & sso login history --- .../provider/approval/TokenApprovalStore.java | 24 ++++-- .../OAuth20AccessConfirmationEndpoint.java | 84 ++++++++++--------- .../provider/token/store/RedisTokenStore.java | 84 +++++++++++++------ .../main/java/org/maxkey/MaxKeyMvcConfig.java | 3 +- 4 files changed, 122 insertions(+), 73 deletions(-) diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/approval/TokenApprovalStore.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/approval/TokenApprovalStore.java index 73c604c2..36eaa1f2 100644 --- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/approval/TokenApprovalStore.java +++ b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/approval/TokenApprovalStore.java @@ -25,6 +25,8 @@ import org.maxkey.authz.oauth2.provider.OAuth2Authentication; import org.maxkey.authz.oauth2.provider.approval.Approval.ApprovalStatus; import org.maxkey.authz.oauth2.provider.token.AuthorizationServerTokenServices; import org.maxkey.authz.oauth2.provider.token.TokenStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An {@link ApprovalStore} that works with an existing {@link TokenStore}, extracting implicit {@link Approval @@ -38,6 +40,8 @@ import org.maxkey.authz.oauth2.provider.token.TokenStore; */ public class TokenApprovalStore implements ApprovalStore { + static final Logger _logger = LoggerFactory.getLogger(TokenApprovalStore.class); + private TokenStore store; /** @@ -55,6 +59,7 @@ public class TokenApprovalStore implements ApprovalStore { */ @Override public boolean addApprovals(Collection approvals) { + _logger.debug("add Approvals..."); return true; } @@ -65,6 +70,7 @@ public class TokenApprovalStore implements ApprovalStore { */ @Override public boolean revokeApprovals(Collection approvals) { + _logger.debug("revoke Approvals " + approvals); boolean success = true; for (Approval approval : approvals) { Collection tokens = store.findTokensByClientIdAndUserName(approval.getClientId(), approval.getUserId()); @@ -87,14 +93,22 @@ public class TokenApprovalStore implements ApprovalStore { */ @Override public Collection getApprovals(String userId, String clientId) { + _logger.trace("userId " + userId+" , clientId " + clientId); Collection result = new HashSet(); Collection tokens = store.findTokensByClientIdAndUserName(clientId, userId); + _logger.trace("tokens Collection " + tokens); for (OAuth2AccessToken token : tokens) { - OAuth2Authentication authentication = store.readAuthentication(token); - if (authentication != null) { - Date expiresAt = token.getExpiration(); - for (String scope : token.getScope()) { - result.add(new Approval(userId, clientId, scope, expiresAt, ApprovalStatus.APPROVED)); + _logger.trace("token " + token); + if(token != null) { + OAuth2Authentication authentication = store.readAuthentication(token); + _logger.trace("authentication " + authentication); + if (authentication != null) { + Date expiresAt = token.getExpiration(); + for (String scope : token.getScope()) { + Approval approval = new Approval(userId, clientId, scope, expiresAt, ApprovalStatus.APPROVED); + result.add(approval); + _logger.trace("add approval " + approval); + } } } } diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/approval/endpoint/OAuth20AccessConfirmationEndpoint.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/approval/endpoint/OAuth20AccessConfirmationEndpoint.java index 3fcac79d..033fef79 100644 --- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/approval/endpoint/OAuth20AccessConfirmationEndpoint.java +++ b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/approval/endpoint/OAuth20AccessConfirmationEndpoint.java @@ -77,48 +77,52 @@ public class OAuth20AccessConfirmationEndpoint { */ @RequestMapping(OAuth2Constants.ENDPOINT.ENDPOINT_APPROVAL_CONFIRM) public ModelAndView getAccessConfirmation( - @RequestParam Map model) throws Exception { - model.remove("authorizationRequest"); - - // Map model - AuthorizationRequest clientAuth = - (AuthorizationRequest) WebContext.getAttribute("authorizationRequest"); - ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); - Apps app = (Apps)WebContext.getAttribute(WebConstants.AUTHORIZE_SIGN_ON_APP); - //session中为空或者id不一致重新加载 - if (app == null || !app.getId().equalsIgnoreCase(clientAuth.getClientId())) { - app = appsService.get(clientAuth.getClientId()); - WebContext.setAttribute(WebConstants.AUTHORIZE_SIGN_ON_APP, app); - WebContext.setAttribute(app.getId(), app.getIcon()); - } - - model.put("auth_request", clientAuth); - model.put("client", client); - model.put("app", app); - model.put("oauth_version", "oauth 2.0"); - Map scopes = new LinkedHashMap(); - for (String scope : clientAuth.getScope()) { - scopes.put(OAuth2Utils.SCOPE_PREFIX + scope, "false"); - } - String principal = - ((SigninPrincipal) WebContext.getAuthentication().getPrincipal()).getUsername(); - for (Approval approval : approvalStore.getApprovals(principal, client.getClientId())) { - if (clientAuth.getScope().contains(approval.getScope())) { - scopes.put(OAuth2Utils.SCOPE_PREFIX + approval.getScope(), - approval.getStatus() == ApprovalStatus.APPROVED ? "true" : "false"); - } - } - - model.put("scopes", scopes); - - if(!model.containsKey(OAuth2Constants.PARAMETER.APPROVAL_PROMPT)) { - model.put(OAuth2Constants.PARAMETER.APPROVAL_PROMPT, client.getApprovalPrompt()); - } - + @RequestParam Map model) { + try { + model.remove("authorizationRequest"); + + // Map model + AuthorizationRequest clientAuth = + (AuthorizationRequest) WebContext.getAttribute("authorizationRequest"); + ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); + Apps app = (Apps)WebContext.getAttribute(WebConstants.AUTHORIZE_SIGN_ON_APP); + //session中为空或者id不一致重新加载 + if (app == null || !app.getId().equalsIgnoreCase(clientAuth.getClientId())) { + app = appsService.get(clientAuth.getClientId()); + WebContext.setAttribute(WebConstants.AUTHORIZE_SIGN_ON_APP, app); + WebContext.setAttribute(app.getId(), app.getIcon()); + } + + model.put("auth_request", clientAuth); + model.put("client", client); + model.put("app", app); + model.put("oauth_version", "oauth 2.0"); + Map scopes = new LinkedHashMap(); + for (String scope : clientAuth.getScope()) { + scopes.put(OAuth2Utils.SCOPE_PREFIX + scope, "false"); + } + String principal = + ((SigninPrincipal) WebContext.getAuthentication().getPrincipal()).getUsername(); + for (Approval approval : approvalStore.getApprovals(principal, client.getClientId())) { + if (clientAuth.getScope().contains(approval.getScope())) { + scopes.put(OAuth2Utils.SCOPE_PREFIX + approval.getScope(), + approval.getStatus() == ApprovalStatus.APPROVED ? "true" : "false"); + } + } + + model.put("scopes", scopes); + + if(!model.containsKey(OAuth2Constants.PARAMETER.APPROVAL_PROMPT)) { + model.put(OAuth2Constants.PARAMETER.APPROVAL_PROMPT, client.getApprovalPrompt()); + } + }catch(Exception e) { + _logger.debug("OAuth Access Confirmation process error." ,e); + } + ModelAndView modelAndView = new ModelAndView("authorize/oauth_access_confirmation"); - _logger.debug("Confirmation details "); + _logger.trace("Confirmation details "); for (Object key : model.keySet()) { - _logger.debug("key " + key +"=" + model.get(key)); + _logger.trace("key " + key +"=" + model.get(key)); } modelAndView.addObject("model", model); return modelAndView; diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/RedisTokenStore.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/RedisTokenStore.java index f30a27a0..414a1a78 100644 --- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/RedisTokenStore.java +++ b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/RedisTokenStore.java @@ -32,6 +32,8 @@ import org.maxkey.authz.oauth2.provider.token.TokenStore; import org.maxkey.persistence.redis.RedisConnection; import org.maxkey.persistence.redis.RedisConnectionFactory; import org.maxkey.util.ObjectTransformer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Date; @@ -39,16 +41,17 @@ import java.util.Date; * @author efenderbosch */ public class RedisTokenStore implements TokenStore { - - private static final String ACCESS = "REDIS_OAUTH_V20_access:"; - private static final String AUTH_TO_ACCESS = "REDIS_OAUTH_V20_auth_to_access:"; - private static final String AUTH = "REDIS_OAUTH_V20_auth:"; - private static final String REFRESH_AUTH = "REDIS_OAUTH_V20_refresh_auth:"; - private static final String ACCESS_TO_REFRESH = "REDIS_OAUTH_V20_access_to_refresh:"; - private static final String REFRESH = "REDIS_OAUTH_V20_refresh:"; - private static final String REFRESH_TO_ACCESS = "REDIS_OAUTH_V20_refresh_to_access:"; - private static final String CLIENT_ID_TO_ACCESS = "REDIS_OAUTH_V20_client_id_to_access:"; - private static final String UNAME_TO_ACCESS = "REDIS_OAUTH_V20_uname_to_access:"; + static final Logger _logger = LoggerFactory.getLogger(RedisTokenStore.class); + + private static final String ACCESS = "REDIS_OAUTH_V20_ACCESS_"; + private static final String AUTH_TO_ACCESS = "REDIS_OAUTH_V20_AUTH_TO_ACCESS_"; + private static final String AUTH = "REDIS_OAUTH_V20_AUTH_"; + private static final String REFRESH_AUTH = "REDIS_OAUTH_V20_REFRESH_AUTH_"; + private static final String ACCESS_TO_REFRESH = "REDIS_OAUTH_V20_ACCESS_TO_REFRESH_"; + private static final String REFRESH = "REDIS_OAUTH_V20_REFRESH_"; + private static final String REFRESH_TO_ACCESS = "REDIS_OAUTH_V20_REFRESH_TO_ACCESS_"; + private static final String CLIENT_ID_TO_ACCESS = "REDIS_OAUTH_V20_CLIENT_ID_TO_ACCESS_"; + private static final String UNAME_TO_ACCESS = "REDIS_OAUTH_V20_UNAME_TO_ACCESS_"; private final RedisConnectionFactory connectionFactory; private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator(); @@ -77,15 +80,19 @@ public class RedisTokenStore implements TokenStore { String key = authenticationKeyGenerator.extractKey(authentication); String serializedKey = (AUTH_TO_ACCESS + key); RedisConnection conn = getConnection(); - OAuth2AccessToken accessToken =conn.getObject(serializedKey); - if (accessToken != null - && !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(accessToken.getValue())))) { - // Keep the stores consistent (maybe the same user is - // represented by this authentication but the details have - // changed) - storeAccessToken(accessToken, authentication); + try { + OAuth2AccessToken accessToken =conn.getObject(serializedKey); + if (accessToken != null + && !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(accessToken.getValue())))) { + // Keep the stores consistent (maybe the same user is + // represented by this authentication but the details have + // changed) + storeAccessToken(accessToken, authentication); + } + return accessToken; + } finally { + conn.close(); } - return accessToken; } @Override @@ -95,9 +102,14 @@ public class RedisTokenStore implements TokenStore { @Override public OAuth2Authentication readAuthentication(String token) { + _logger.trace("read Authentication by token " + token + " , token key " + AUTH + token); RedisConnection conn = getConnection(); - OAuth2Authentication auth = conn.getObject(AUTH + token); - return auth; + try { + OAuth2Authentication auth = conn.getObject(AUTH + token); + return auth; + } finally { + conn.close(); + } } @Override @@ -122,6 +134,11 @@ public class RedisTokenStore implements TokenStore { String authToAccessKey = (AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication)); String approvalKey = (UNAME_TO_ACCESS + getApprovalKey(authentication)); String clientId = (CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId()); + _logger.trace("accessKey " + accessKey); + _logger.trace("authKey " + authKey); + _logger.trace("authToAccessKey " + authToAccessKey); + _logger.trace("approvalKey " + approvalKey); + _logger.trace("clientId " + clientId); RedisConnection conn = getConnection(); try { @@ -146,8 +163,10 @@ public class RedisTokenStore implements TokenStore { String refresh = (token.getRefreshToken().getValue()); String auth = (token.getValue()); String refreshToAccessKey = (REFRESH_TO_ACCESS + token.getRefreshToken().getValue()); + _logger.trace("refreshToAccessKey " + refreshToAccessKey); conn.set(refreshToAccessKey, auth); String accessToRefreshKey = (ACCESS_TO_REFRESH + token.getValue()); + _logger.trace("accessToRefreshKey " + accessToRefreshKey); conn.set(accessToRefreshKey, refresh); if (refreshToken instanceof ExpiringOAuth2RefreshToken) { ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken; @@ -173,7 +192,7 @@ public class RedisTokenStore implements TokenStore { } private static String getApprovalKey(String clientId, String userName) { - return clientId + (userName == null ? "" : ":" + userName); + return clientId + (userName == null ? "" : "_" + userName); } @Override @@ -184,9 +203,13 @@ public class RedisTokenStore implements TokenStore { @Override public OAuth2AccessToken readAccessToken(String tokenValue) { RedisConnection conn = getConnection(); - String key = (ACCESS + tokenValue); - OAuth2AccessToken accessToken = conn.getObject(key); - return accessToken; + try { + String key = (ACCESS + tokenValue); + OAuth2AccessToken accessToken = conn.getObject(key); + return accessToken; + } finally { + conn.close(); + } } public void removeAccessToken(String tokenValue) { @@ -251,9 +274,13 @@ public class RedisTokenStore implements TokenStore { public OAuth2RefreshToken readRefreshToken(String tokenValue) { String key = (REFRESH + tokenValue); RedisConnection conn = getConnection(); - OAuth2RefreshToken refreshToken = conn.getObject(key); - conn.close(); - return refreshToken; + try { + OAuth2RefreshToken refreshToken = conn.getObject(key); + conn.close(); + return refreshToken; + } finally { + conn.close(); + } } @Override @@ -309,6 +336,7 @@ public class RedisTokenStore implements TokenStore { @Override public Collection findTokensByClientIdAndUserName(String clientId, String userName) { String approvalKey = (UNAME_TO_ACCESS + getApprovalKey(clientId, userName)); + _logger.trace("approvalKey " + approvalKey); List stringList = null; RedisConnection conn = getConnection(); try { @@ -321,6 +349,7 @@ public class RedisTokenStore implements TokenStore { } List accessTokens = new ArrayList(stringList.size()); for (String str : stringList) { + //accessToken may expired OAuth2AccessToken accessToken = conn.getObject(str); accessTokens.add(accessToken); } @@ -330,6 +359,7 @@ public class RedisTokenStore implements TokenStore { @Override public Collection findTokensByClientId(String clientId) { String key = (CLIENT_ID_TO_ACCESS + clientId); + _logger.trace("TokensByClientId " + key); List stringList = null; RedisConnection conn = getConnection(); try { diff --git a/maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyMvcConfig.java b/maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyMvcConfig.java index f2a3dcb9..75c66298 100644 --- a/maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyMvcConfig.java +++ b/maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyMvcConfig.java @@ -211,12 +211,13 @@ public class MaxKeyMvcConfig implements WebMvcConfigurer { registry.addInterceptor(historyLoginAppAdapter) .addPathPatterns("/authz/basic/*") .addPathPatterns("/authz/ltpa/*") - .addPathPatterns("/authz/desktop/*") + .addPathPatterns("/authz/api/*") .addPathPatterns("/authz/formbased/*") .addPathPatterns("/authz/tokenbased/*") .addPathPatterns("/authz/saml20/idpinit/*") .addPathPatterns("/authz/saml20/assertion") .addPathPatterns("/authz/cas/granting") + .addPathPatterns("/authz/oauth/v20/approval_confirm") ; _logger.debug("add HistoryLoginAppAdapter"); -- GitLab