Merge pull request #3576 from stianst/KEYCLOAK-4006

KEYCLOAK-4006 Fix performance drop caused by changes to client sessio…
This commit is contained in:
Stian Thorgersen 2016-12-01 12:44:45 +01:00 committed by GitHub
commit f9db687385
2 changed files with 15 additions and 39 deletions

View file

@ -18,22 +18,16 @@
package org.keycloak.services.managers; package org.keycloak.services.managers;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel; import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientTemplateModel; import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import java.security.PublicKey;
import java.security.Signature;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -43,6 +37,8 @@ import java.util.Set;
*/ */
public class ClientSessionCode { public class ClientSessionCode {
private static final String ACTIVE_CODE = "active_code";
private static final Logger logger = Logger.getLogger(ClientSessionCode.class); private static final Logger logger = Logger.getLogger(ClientSessionCode.class);
private static final String NEXT_CODE = ClientSessionCode.class.getName() + ".nextCode"; private static final String NEXT_CODE = ClientSessionCode.class.getName() + ".nextCode";
@ -99,7 +95,7 @@ public class ClientSessionCode {
return result; return result;
} }
if (!verifyCode(code, session, realm, result.clientSession)) { if (!verifyCode(code, result.clientSession)) {
result.illegalHash = true; result.illegalHash = true;
return result; return result;
} }
@ -119,7 +115,7 @@ public class ClientSessionCode {
return null; return null;
} }
if (!verifyCode(code, session, realm, clientSession)) { if (!verifyCode(code, clientSession)) {
return null; return null;
} }
@ -215,7 +211,7 @@ public class ClientSessionCode {
public String getCode() { public String getCode() {
String nextCode = (String) session.getAttribute(NEXT_CODE + "." + clientSession.getId()); String nextCode = (String) session.getAttribute(NEXT_CODE + "." + clientSession.getId());
if (nextCode == null) { if (nextCode == null) {
nextCode = generateCode(session, realm, clientSession); nextCode = generateCode(clientSession);
session.setAttribute(NEXT_CODE + "." + clientSession.getId(), nextCode); session.setAttribute(NEXT_CODE + "." + clientSession.getId(), nextCode);
} else { } else {
logger.debug("Code already generated for session, using code from session attributes"); logger.debug("Code already generated for session, using code from session attributes");
@ -223,30 +219,18 @@ public class ClientSessionCode {
return nextCode; return nextCode;
} }
private static String generateCode(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) { private static String generateCode(ClientSessionModel clientSession) {
try { try {
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm); String actionId = KeycloakModelUtils.generateSecret();
String secret = KeycloakModelUtils.generateSecret();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(secret); sb.append(actionId);
sb.append('.'); sb.append('.');
sb.append(clientSession.getId()); sb.append(clientSession.getId());
String code = sb.toString(); String code = sb.toString();
Signature signature = RSAProvider.getSignature(Algorithm.RS256); clientSession.setNote(ACTIVE_CODE, code);
signature.initSign(keys.getPrivateKey());
signature.update(code.getBytes("utf-8"));
sb = new StringBuilder();
sb.append(Base64Url.encode(signature.sign()));
sb.append('.');
sb.append(keys.getKid());
clientSession.setNote(ClientSessionModel.ACTION_SIGNATURE, sb.toString());
return code; return code;
} catch (Exception e) { } catch (Exception e) {
@ -254,24 +238,17 @@ public class ClientSessionCode {
} }
} }
private static boolean verifyCode(String code, KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) { private static boolean verifyCode(String code, ClientSessionModel clientSession) {
try { try {
String note = clientSession.getNote(ClientSessionModel.ACTION_SIGNATURE); String activeCode = clientSession.getNote(ACTIVE_CODE);
if (note == null) { if (activeCode == null) {
logger.debug("Action signature not found in client session"); logger.debug("Active code not found in client session");
return false; return false;
} }
clientSession.removeNote(ClientSessionModel.ACTION_SIGNATURE); clientSession.removeNote(ACTIVE_CODE);
String[] signed = note.split("\\."); return code.equals(activeCode);
PublicKey publicKey = session.keys().getPublicKey(realm, signed[1]);
Signature verifier = RSAProvider.getSignature(Algorithm.RS256);
verifier.initVerify(publicKey);
verifier.update(code.getBytes("utf-8"));
return verifier.verify(Base64Url.decode(signed[0]));
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View file

@ -24,7 +24,6 @@ import java.util.Set;
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public interface ClientSessionModel { public interface ClientSessionModel {
public static final String ACTION_SIGNATURE = "action_signature";
public String getId(); public String getId();
public RealmModel getRealm(); public RealmModel getRealm();