Spring Security提供了一个实现了可以缓存UserDetails的UserDetailsService实现类,CachingUserDetailsService。该类的构造接收一个用于真正加载UserDetails的UserDetailsService实现类。当需要加载UserDetails时,其首先会从缓存中获取,如果缓存中没有对应的UserDetails存在,则使用持有的UserDetailsService实现类进行加载,然后将加载后的结果存放在缓存中。UserDetails与缓存的交互是通过UserCache接口来实现的。CachingUserDetailsService默认拥有UserCache的一个空实现引用,NullUserCache。以下是CachingUserDetailsService的类定义。

public class CachingUserDetailsService implements UserDetailsService {

private UserCache userCache = new NullUserCache();

private final UserDetailsService delegate;

CachingUserDetailsService(UserDetailsService delegate) {

this.delegate = delegate;

}

public UserCache getUserCache() {

return userCache;

}

public void setUserCache(UserCache userCache) {

this.userCache = userCache;

}

public UserDetails loadUserByUsername(String username) {

UserDetails user = userCache.getUserFromCache(username);

if (user == null) {

user = delegate.loadUserByUsername(username);

}

Assert.notNull(user, "UserDetailsService " + delegate + " returned null for username " + username + ". " +

"This is an interface contract violation");

userCache.putUserInCache(user);

return user;

}

}

我们可以看到当缓存中不存在对应的UserDetails时将使用引用的UserDetailsService类型的delegate进行加载。加载后再把它存放到Cache中并进行返回。除了NullUserCache之外,Spring Security还为我们提供了一个基于Ehcache的UserCache实现类,EhCacheBasedUserCache,其源码如下所示。

public class EhCacheBasedUserCache implements UserCache, InitializingBean {

private static final Log logger = LogFactory.getLog(EhCacheBasedUserCache.class);

private Ehcache cache;

public void afterPropertiesSet() throws Exception {

Assert.notNull(cache, "cache mandatory");

}

public Ehcache getCache() {

returncache;

}

public UserDetails getUserFromCache(String username) {

Element element = cache.get(username);

if (logger.isDebugEnabled()) {

logger.debug("Cache hit: " + (element != null) + "; username: " + username);

}

if (element == null) {

returnnull;

else {

return (UserDetails) element.getValue();

}

}

public void putUserInCache(UserDetails user) {

Element element = new Element(user.getUsername(), user);

if (logger.isDebugEnabled()) {

logger.debug("Cache put: " + element.getKey());

}

cache.put(element);

}

public void removeUserFromCache(UserDetails user) {

if (logger.isDebugEnabled()) {

logger.debug("Cache remove: " + user.getUsername());

}

this.removeUserFromCache(user.getUsername());

}

public void removeUserFromCache(String username) {

cache.remove(username);

}

public void setCache(Ehcache cache) {

this.cache = cache;

}

}

从上述源码我们可以看到EhCacheBasedUserCache所引用的Ehcache是空的,所以,当我们需要对UserDetails进行缓存时,我们只需要定义一个Ehcache实例,然后把它注入给EhCacheBasedUserCache就可以了。接下来我们来看一下定义一个支持缓存UserDetails的CachingUserDetailsService的示例。

<security:authentication-manager alias="authenticationManager">

<!-- 使用可以缓存UserDetails的CachingUserDetailsService -->

<security:authentication-provider

user-service-ref="cachingUserDetailsService" />

</security:authentication-manager>

<!-- 可以缓存UserDetails的UserDetailsService -->

<bean id="cachingUserDetailsService"class="org.springframework.security.config.authentication.CachingUserDetailsService">

<!-- 真正加载UserDetails的UserDetailsService -->

<constructor-arg ref="userDetailsService"/>

<!-- 缓存UserDetails的UserCache -->

<property name="userCache">

<beanclass="org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache">

<!-- 用于真正缓存的Ehcache对象 -->

<property name="cache" ref="ehcache4UserDetails"/>

</bean>

</property>

</bean>

<!-- 将使用默认的CacheManager创建一个名为ehcache4UserDetails的Ehcache对象 -->

<bean id="ehcache4UserDetails"class="org.springframework.cache.ehcache.EhCacheFactoryBean"/>

<!-- 从数据库加载UserDetails的UserDetailsService -->

<bean id="userDetailsService"

class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">

<property name="dataSource" ref="dataSource" />

</bean>

在上面的配置中,我们通过EhcacheFactoryBean定义的Ehcache bean对象采用的是默认配置,其将使用默认的CacheManager,即直接通过CacheManager.getInstance()获取当前已经存在的CacheManager对象,如不存在则使用默认配置自动创建一个,当然这可以通过cacheManager属性指定我们需要使用的CacheManager,CacheManager可以通过EhCacheManagerFactoryBean进行定义。此外,如果没有指定对应缓存的名称,默认将使用beanName,在上述配置中即为ehcache4UserDetails,可以通过cacheName属性进行指定。此外,缓存的配置信息也都是使用的默认的。更多关于Spring使用Ehcache的信息可以参考我的另一篇文章《Spring使用Cache》。

(注:本文是基于Spring Security3.1.6所写)

Spring Security(07)——缓存UserDetails的更多相关文章

  1. Spring Boot Oauth2缓存UserDetails到Ehcache

    在Spring中有一个类CachingUserDetailsService实现了UserDetailsService接口,该类使用静态代理模式为UserDetailsService提供缓存功能.该类源 ...

  2. 深入Spring Security魔幻山谷-获取认证机制核心原理讲解(新版)

    文/朱季谦 本文基于Springboot+Vue+Spring Security框架而写的原创学习笔记,demo代码参考<Spring Boot+Spring Cloud+Vue+Element ...

  3. 「快学springboot」集成Spring Security实现鉴权功能

    Spring Security介绍 Spring Security是Spring全家桶中的处理身份和权限问题的一员.Spring Security可以根据使用者的需要定制相关的角色身份和身份所具有的权 ...

  4. 4-11 Spring Security及SSO

    1. 关于用户身份认证与授权 Spring Security是用于解决认证与授权的框架. 在根项目下创建新的csmall-passport子模块,最基础的依赖项包括spring-boot-starte ...

  5. spring security 控制用户信息用户加密 缓存用户信息

    1. MD5加密 任何一个正式的企业应用中,都不会在数据库中使用明文来保存密码的,我们在之前的章节中都是为了方便起见没有对数据库中的用户密码进行加密,这在实际应用中是极为幼稚的做法.可以想象一下,只要 ...

  6. Ajax登陆,使用Spring Security缓存跳转到登陆前的链接

    Spring Security缓存的应用之登陆后跳转到登录前源地址 什么意思? 用户访问网站,打开了一个链接:(origin url)起源链接 请求发送给服务器,服务器判断用户请求了受保护的资源. 由 ...

  7. Spring Boot整合实战Spring Security JWT权限鉴权系统

    目前流行的前后端分离让Java程序员可以更加专注的做好后台业务逻辑的功能实现,提供如返回Json格式的数据接口就可以.像以前做项目的安全认证基于 session 的登录拦截,属于后端全栈式的开发的模式 ...

  8. Spring Security 教程 大牛的教程

    https://www.iteye.com/blog/elim-2247073 Spring Security 教程 Spring Security(20)——整合Cas Spring Securit ...

  9. Spring Security Architecture and Implementation(架构和实现)学习笔记

    Spring Security 关于spring-security的官网文档学习笔记,主要是第8章 Architecture and Implementation(架构和实现)内容 参考: https ...

随机推荐

  1. CodeForces 632D Longest Subsequence

    暴力. 虽然$a[i]$最大有${10^9}$,但是$m$最大只有${10^6}$,因此可以考虑暴力. 记$cnt[i]$表示数字$i$有$cnt[i]$个,记$p[i]$表示以$i$为倍数的情况下, ...

  2. [HMLY]14.对iOS开发中使用MVVM的理解和使用(初级)

    前言 MVVMDemo 之前几个月一直在学习react-native,它的组件化开发真的是很棒,控件和页面的组件化在开发中可以很好的复用,节省开发时间.在那个时候还不知道react-native开发用 ...

  3. js的eval函数解析后台返回的json数据时为什加上圆括号eval("("+data+")"),而HTML页面定义的数据不用

    一,情况如下,这是成功代码: $(function () { $.ajax({ url: "Demo.aspx", type: "post", data: { ...

  4. IOS 成员变量,全局变量,局部变量定义,static与extern的区别

    IOS 成员变量,全局变量,局部变量定义,static与extern的区别 1,先说定义 1)成员变量定义:生存与该类的生命周期,变量存活周期跟你定义的该类实体对象一样:作用域是整个实体对象:可以在h ...

  5. NGINX----源码阅读一(main函数)

    1.ngx_debug_init(); 初始化debug函数,一般为空. 2.ngx_strerror_init(): 将系统错误码+错误信息,以ngx_str_t数组保存. 3.ngx_get_op ...

  6. POJ 1952 BUY LOW, BUY LOWER

    $dp$. 一开始想了一个$dp$做法,$dp[i][j]$表示前$i$个数字,下降序列长度为$j$的方案数为$dp[i][j]$,这样做需要先离散化然后用树状数组优化,空间复杂度为${n^2}$,时 ...

  7. IIS 支持 ajax 跨域

    第一,首先要浏览器支持. Firefox和Chrome都支持了. IE10 也支持. IE8,9 需要使用其他方式才能支持 第二, 加上HTTP header. IIS 7 的方式比较容易. 就在we ...

  8. c#语言简介

    语言的历史:c,c++,java,c#, c语言最早,c++语言复杂,而java把其中难的一部分去掉,也成为c--,而c#早期类似java. c#简单高效,并且与web结合. C#的运行环境是.net ...

  9. reids配置参数详解

    转自:http://www.jb51.net/article/60627.htm reids配置参数详解 #daemonize no  默认情况下, redis 不是在后台运行的,如果需要在后台运行, ...

  10. [编码解码] Base64 编码换行和+号遍空格的处理

    Android自身带有Base64加密与解密的包,可以方便地加密密码方便传输. String base64Token = Base64.encodeToString(token.trim().getB ...