minor, remove refine user cache in spring security
Project: http://git-wip-us.apache.org/repos/asf/kylin/repo Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/3280172b Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/3280172b Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/3280172b Branch: refs/heads/master Commit: 3280172b737f85479eb3432fa12c267b477674fd Parents: 22d8fae Author: Hongbin Ma <[email protected]> Authored: Fri Jun 23 19:08:41 2017 +0800 Committer: liyang-gmt8 <[email protected]> Committed: Fri Jun 23 20:26:46 2017 +0800 ---------------------------------------------------------------------- .../apache/kylin/common/KylinConfigBase.java | 10 ++- .../security/KylinAuthenticationProvider.java | 91 ++++++++++++-------- .../apache/kylin/rest/security/ManagedUser.java | 78 ++++++++--------- .../apache/kylin/rest/service/UserService.java | 10 +++ server/src/main/resources/ehcache-test.xml | 9 +- server/src/main/resources/ehcache.xml | 9 +- 6 files changed, 111 insertions(+), 96 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kylin/blob/3280172b/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java ---------------------------------------------------------------------- diff --git a/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java b/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java index 2ccf3cf..ecd5261 100644 --- a/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java +++ b/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java @@ -502,7 +502,7 @@ abstract public class KylinConfigBase implements Serializable { public Integer getSchedulerPollIntervalSecond() { return Integer.parseInt(getOptional("kylin.job.scheduler.poll-interval-second", "30")); } - + public Integer getErrorRecordThreshold() { return Integer.parseInt(getOptional("kylin.job.error-record-threshold", "0")); } @@ -1073,6 +1073,14 @@ abstract public class KylinConfigBase implements Serializable { return getOptionalIntArray("kylin.server.query-metrics-percentiles-intervals", dft); } + public int getServerUserCacheExpireSeconds() { + return Integer.valueOf(this.getOptional("kylin.server.auth-user-cache.expire-seconds", "300")); + } + + public int getServerUserCacheMaxEntries() { + return Integer.valueOf(this.getOptional("kylin.server.auth-user-cache.max-entries", "100")); + } + // ============================================================================ // WEB // ============================================================================ http://git-wip-us.apache.org/repos/asf/kylin/blob/3280172b/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java b/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java index 7322b84..dd9cbad 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java +++ b/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java @@ -18,7 +18,10 @@ package org.apache.kylin.rest.security; -import org.apache.kylin.common.util.ByteArray; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import org.apache.kylin.common.KylinConfig; import org.apache.kylin.rest.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,13 +35,12 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.util.Assert; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; -import net.sf.ehcache.Cache; -import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Element; - /** * A wrapper class for the authentication provider; Will do something more for Kylin. */ @@ -46,13 +48,21 @@ public class KylinAuthenticationProvider implements AuthenticationProvider { private static final Logger logger = LoggerFactory.getLogger(KylinAuthenticationProvider.class); + private final static com.google.common.cache.Cache<String, Authentication> userCache = CacheBuilder.newBuilder() + .maximumSize(KylinConfig.getInstanceFromEnv().getServerUserCacheMaxEntries()) + .expireAfterWrite(KylinConfig.getInstanceFromEnv().getServerUserCacheExpireSeconds(), TimeUnit.SECONDS) + .removalListener(new RemovalListener<String, Authentication>() { + @Override + public void onRemoval(RemovalNotification<String, Authentication> notification) { + KylinAuthenticationProvider.logger.debug("User cache {} is removed due to {}", + notification.getKey(), notification.getCause()); + } + }).build(); + @Autowired @Qualifier("userService") UserService userService; - @Autowired - private CacheManager cacheManager; - //Embedded authentication provider private AuthenticationProvider authenticationProvider; @@ -67,48 +77,53 @@ public class KylinAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - Authentication authed = null; - Cache userCache = cacheManager.getCache("UserCache"); + byte[] hashKey = hf.hashString(authentication.getName() + authentication.getCredentials()).asBytes(); - ByteArray userKey = new ByteArray(hashKey); + String userKey = Arrays.toString(hashKey); + + if (userService.isEvictCacheFlag()) { + userCache.invalidateAll(); + userService.setEvictCacheFlag(false); + } + Authentication authed = userCache.getIfPresent(userKey); - Element authedUser = userCache.get(userKey); - if (null != authedUser) { - authed = (Authentication) authedUser.getObjectValue(); + if (null != authed) { SecurityContextHolder.getContext().setAuthentication(authed); } else { try { authed = authenticationProvider.authenticate(authentication); - userCache.put(new Element(userKey, authed)); + + ManagedUser user; + + if (authed.getDetails() == null) { + //authed.setAuthenticated(false); + throw new UsernameNotFoundException( + "User not found in LDAP, check whether he/she has been added to the groups."); + } + + if (authed.getDetails() instanceof UserDetails) { + UserDetails details = (UserDetails) authed.getDetails(); + user = new ManagedUser(details.getUsername(), details.getPassword(), false, + details.getAuthorities()); + } else { + user = new ManagedUser(authentication.getName(), "skippped-ldap", false, authed.getAuthorities()); + } + Assert.notNull(user, "The UserDetail is null."); + + logger.debug("User {} authorities : {}", user.getUsername(), user.getAuthorities()); + if (!userService.userExists(user.getUsername())) { + userService.createUser(user); + } else { + userService.updateUser(user); + } + + userCache.put(userKey, authed); } catch (AuthenticationException e) { logger.error("Failed to auth user: " + authentication.getName(), e); throw e; } logger.debug("Authenticated user " + authed.toString()); - - ManagedUser user; - - if (authed.getDetails() == null) { - //authed.setAuthenticated(false); - throw new UsernameNotFoundException( - "User not found in LDAP, check whether he/she has been added to the groups."); - } - - if (authed.getDetails() instanceof UserDetails) { - UserDetails details = (UserDetails) authed.getDetails(); - user = new ManagedUser(details.getUsername(), details.getPassword(), false, details.getAuthorities()); - } else { - user = new ManagedUser(authentication.getName(), "skippped-ldap", false, authed.getAuthorities()); - } - Assert.notNull(user, "The UserDetail is null."); - - logger.debug("User authorities :" + user.getAuthorities()); - if (!userService.userExists(user.getUsername())) { - userService.createUser(user); - } else { - userService.updateUser(user); - } } return authed; http://git-wip-us.apache.org/repos/asf/kylin/blob/3280172b/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java b/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java index 4805d5c..280339e 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java +++ b/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java @@ -22,8 +22,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -import javax.annotation.Nullable; - import org.apache.kylin.common.persistence.RootPersistentEntity; import org.apache.kylin.rest.service.UserGrantedAuthority; import org.springframework.security.core.GrantedAuthority; @@ -31,8 +29,6 @@ import org.springframework.security.core.userdetails.UserDetails; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.Function; -import com.google.common.collect.Collections2; import com.google.common.collect.Lists; @SuppressWarnings("serial") @@ -44,7 +40,7 @@ public class ManagedUser extends RootPersistentEntity implements UserDetails { @JsonProperty private String password; @JsonProperty - private List<String> authorities = Lists.newArrayList(); + private List<UserGrantedAuthority> authorities = Lists.newArrayList(); @JsonProperty private boolean disabled = false; @JsonProperty @@ -56,24 +52,40 @@ public class ManagedUser extends RootPersistentEntity implements UserDetails { @JsonProperty private int wrongTime = 0; - private Boolean legacyCatered = false; //DISABLED_ROLE is a ancient way to represent disabled user //now we no longer support such way, however legacy metadata may still contain it private static final String DISABLED_ROLE = "--disabled--"; - //this is computed - private List<UserGrantedAuthority> grantedAuthorities = null; - public ManagedUser() { } - public ManagedUser(String username, String password, Boolean defaultPassword, String... authorities) { + public ManagedUser(@JsonProperty String username, @JsonProperty String password, + @JsonProperty List<UserGrantedAuthority> authorities, @JsonProperty boolean disabled, + @JsonProperty boolean defaultPassword, @JsonProperty boolean locked, @JsonProperty long lockedTime, + @JsonProperty int wrongTime) { + this.username = username; + this.password = password; + this.authorities = authorities; + this.disabled = disabled; + this.defaultPassword = defaultPassword; + this.locked = locked; + this.lockedTime = lockedTime; + this.wrongTime = wrongTime; + + caterLegacy(); + } + + public ManagedUser(String username, String password, Boolean defaultPassword, String... authoritiesStr) { this.username = username; this.password = password; this.setDefaultPassword(defaultPassword); - this.authorities = Lists.newArrayList(authorities); - this.grantedAuthorities = null; + this.authorities = Lists.newArrayList(); + for (String a : authoritiesStr) { + authorities.add(new UserGrantedAuthority(a)); + } + + caterLegacy(); } public ManagedUser(String username, String password, Boolean defaultPassword, @@ -83,6 +95,8 @@ public class ManagedUser extends RootPersistentEntity implements UserDetails { this.setDefaultPassword(defaultPassword); this.setGrantedAuthorities(grantedAuthorities); + + caterLegacy(); } public String getUsername() { @@ -102,45 +116,27 @@ public class ManagedUser extends RootPersistentEntity implements UserDetails { } private void caterLegacy() { - if (!legacyCatered) { - synchronized (legacyCatered) { - Iterator<String> iterator = authorities.iterator(); - while (iterator.hasNext()) { - if (DISABLED_ROLE.equals(iterator.next())) { - iterator.remove(); - this.disabled = true; - } - } - legacyCatered = true; + Iterator<UserGrantedAuthority> iterator = authorities.iterator(); + while (iterator.hasNext()) { + if (DISABLED_ROLE.equals(iterator.next().getAuthority())) { + iterator.remove(); + this.disabled = true; } } } public List<UserGrantedAuthority> getAuthorities() { - caterLegacy(); - if (grantedAuthorities == null) { - grantedAuthorities = Lists.newArrayList(); - for (String a : authorities) { - this.grantedAuthorities.add(new UserGrantedAuthority(a)); - } - } - return grantedAuthorities; + return this.authorities; } public void setGrantedAuthorities(Collection<? extends GrantedAuthority> grantedAuthorities) { - this.authorities = Lists - .newArrayList(Collections2.transform(grantedAuthorities, new Function<GrantedAuthority, String>() { - @Nullable - @Override - public String apply(@Nullable GrantedAuthority input) { - return input.getAuthority(); - } - })); - this.grantedAuthorities = null; + this.authorities = Lists.newArrayList(); + for (GrantedAuthority grantedAuthority : grantedAuthorities) { + this.authorities.add(new UserGrantedAuthority(grantedAuthority.getAuthority())); + } } public boolean isDisabled() { - caterLegacy(); return disabled; } @@ -230,6 +226,6 @@ public class ManagedUser extends RootPersistentEntity implements UserDetails { @Override public String toString() { - return "KapManagedUser [username=" + username + ", authorities=" + grantedAuthorities + "]"; + return "ManagedUser [username=" + username + ", authorities=" + authorities + "]"; } } http://git-wip-us.apache.org/repos/asf/kylin/blob/3280172b/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java b/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java index 504c035..6682c03 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java +++ b/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java @@ -54,6 +54,16 @@ public class UserService implements UserDetailsManager { public static final Serializer<ManagedUser> SERIALIZER = new JsonSerializer<>(ManagedUser.class); protected ResourceStore aclStore; + + private boolean evictCacheFlag = false; + + public boolean isEvictCacheFlag() { + return evictCacheFlag; + } + + public void setEvictCacheFlag(boolean evictCacheFlag) { + this.evictCacheFlag = evictCacheFlag; + } @PostConstruct public void init() throws IOException { http://git-wip-us.apache.org/repos/asf/kylin/blob/3280172b/server/src/main/resources/ehcache-test.xml ---------------------------------------------------------------------- diff --git a/server/src/main/resources/ehcache-test.xml b/server/src/main/resources/ehcache-test.xml index bffe27a..5bd4d13 100644 --- a/server/src/main/resources/ehcache-test.xml +++ b/server/src/main/resources/ehcache-test.xml @@ -27,11 +27,4 @@ > <persistence strategy="none"/> </cache> - <cache name="UserCache" - eternal="false" - timeToLiveSeconds="10800" - memoryStoreEvictionPolicy="LRU" - > - <persistence strategy="none"/> - </cache> -</ehcache> \ No newline at end of file +</ehcache> http://git-wip-us.apache.org/repos/asf/kylin/blob/3280172b/server/src/main/resources/ehcache.xml ---------------------------------------------------------------------- diff --git a/server/src/main/resources/ehcache.xml b/server/src/main/resources/ehcache.xml index 1d49ca6..c9efc13 100644 --- a/server/src/main/resources/ehcache.xml +++ b/server/src/main/resources/ehcache.xml @@ -27,11 +27,4 @@ > <persistence strategy="none"/> </cache> - <cache name="UserCache" - eternal="false" - timeToLiveSeconds="10800" - memoryStoreEvictionPolicy="LRU" - > - <persistence strategy="none"/> - </cache> -</ehcache> \ No newline at end of file +</ehcache>
