1. MD5加密

任何一个正式的企业应用中,都不会在数据库中使用明文来保存密码的,我们在之前的章节中都是为了方便起见没有对数据库中的用户密码进行加密,这在实际应用中是极为幼稚的做法。可以想象一下,只要有人进入数据库就可以看到所有人的密码,这是一件多么恐怖的事情,为此我们至少要对密码进行加密,这样即使数据库被攻破,也可以保证用户密码的安全。

最常用的方法是使用MD5算法对密码进行摘要加密,这是一种单项加密手段,无法通过加密后的结果反推回原来的密码明文。

首先我们要把数据库中原来保存的密码使用MD5进行加密:

<authentication-provider>
<password-encoder hash="md5"/>
<jdbc-user-service data-source-ref="dataSource"/>
</authentication-provider>

启用MD5算法。用户登录时,输入的密码是明文,需要使用password-encoder将明文转换成md5形式,然后再与数据库中的已加密密码进行比对。

这些配置对普通客户不会造成任何影响,他们只需要输入自己的密码,Spring Security会自动加以演算,将生成的结果与数据库中保存的信息进行比对,以此来判断用户是否可以登陆。

这样,我们只添加了一行配置,就为系统带来了密码加密的功能。

2. 盐值加密

上面的实例在现实使用中还存在着一个不小的问题。虽然md5算法是不可逆的,但是因为它对同一个字符串计算的结果是唯一的,所以一些人可能会使用“字典攻击”的方式来攻破md5加密的系统[5]。这虽然属于暴力解密,却十分有效,因为大多数系统的用户密码都不回很长。

实际上,大多数系统都是用admin作为默认的管理员登陆密码,所以,当我们在数据库中看到“21232f297a57a5a743894a0e4a801fc3”时,就可以意识到admin用户使用的密码了。因此,md5在处理这种常用字符串时,并不怎么奏效。

为了解决这个问题,我们可以使用盐值加密“salt-source”。

修改配置文件:

<authentication-provider>
<password-encoder hash="md5">
<salt-source user-property="username"/>
</password-encoder>
<jdbc-user-service data-source-ref="dataSource"/>
</authentication-provider>

盐值的原理非常简单,就是先把密码和盐值指定的内容合并在一起,再使用md5对合并后的内容进行演算,这样一来,就算密码是一个很常见的字符串,再加上用户名,最后算出来的md5值就没那么容易猜出来了。因为攻击者不知道盐值的值,也很难反算出密码原文。

3. 用户信息缓存

介于系统的用户信息并不会经常改变,因此使用缓存就成为了提升性能的一个非常好的选择。Spring Security内置的缓存实现是基于ehcache的,为了启用缓存功能,我们要在配置文件中添加相关的内容。

<authentication-provider>
<password-encoder hash="md5">
<salt-source user-property="username"/>
</password-encoder>
<jdbc-user-service data-source-ref="dataSource" cache-ref="userCache"/>
</authentication-provider>

我们在jdbc-user-service部分添加了对userCache的引用,它将使用这个bean作为用户权限缓存的实现。对userCache的配置如下所示:

<beans:bean id="userCache" class="org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache">
<beans:property name="cache" ref="userEhCache"/>
</beans:bean> <beans:bean id="userEhCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<beans:property name="cacheManager" ref="cacheManager"/>
<beans:property name="cacheName" value="userCache"/>
</beans:bean> <beans:bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>

EhCacheBasedUserCache是Spring Security内置的缓存实现,它将为jdbc-user-service提供缓存功能。它所引用的userEhCache来自spring提供的EhCacheFactoryBean和EhCacheManagerFactoryBean,对于userCache的缓存配置放在ehcache.xml中:

<ehcache>
<diskStore path="java.io.tmpdir"/> <defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/> <cache
name="userCache"
maxElementsInMemory="100"//内存中最多存放100个对象
eternal="false"//不是永久缓存
timeToIdleSeconds="600"//最大空闲时间是600s
timeToLiveSeconds="3600"//最大活动时间是3600s
overflowToDisk="true"//如果内存对象溢出则保存到磁盘
/>
</ehcache>

如果想了解有关ehcache的更多配置,可以访问它的官方网站http://ehcache.sf.net/

这样,我们就为用户权限信息设置好了缓存,当一个用户多次访问应用时,不需要每次去访问数据库了,ehcache会将对应的信息缓存起来,这将极大的提高系统的相应速度,同时也避免数据库符合过高的风险。

注意

cache-ref隐藏着一个陷阱,如果不看代码,我们也许会误认为cache-ref会在JdbcUserDetailsManager中设置对应的userCache,然后只要直接执行JdbcUserDetailsManager中的方法,就可以自动维护用户缓存。

可惜,cache-ref实际上是在JdbcUserDetailsManager的基础上,生成了一个CachingUserService,这个CachedUserDetailsService会拦截loadUserByUsername()方法,实现读取用户信息的缓存功能。我们在cache-ref中引用的UserCache实际上是放在CacheUserDetailsService中,而不是放到了原有的JdbcUserDetailsManager中,这就会导致JdbcUserDetailsManager中对用户缓存的操作全部失效。

4. 获取当前用户信息

如果只是想从页面上显示当前登陆的用户名,可以直接使用Spring Security提供的taglib。

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<div>username : <sec:authentication property="name"/></div>

如果想在程序中获得当前登陆用户对应的对象。

UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();

如果想获得当前登陆用户所拥有的所有权限。

Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) userDetails.getAuthorities();;
        

关于UserDetails是如何放到SecuirtyContext中去的,以及Spring Security所使用的TheadLocal模式,我们会在后面详细介绍。这里我们已经了解了如何获得当前登陆用户的信息。

spring security 控制用户信息用户加密 缓存用户信息的更多相关文章

  1. Spring Security——核心类简介——获得登录用户的相关信息

    核心类简介 目录 1.1     Authentication 1.2     SecurityContextHolder 1.3     AuthenticationManager和Authenti ...

  2. Spring Security控制权限

    Spring Security控制权限 1,配置过滤器 为了在项目中使用Spring Security控制权限,首先要在web.xml中配置过滤器,这样我们就可以控制对这个项目的每个请求了. < ...

  3. spring security控制session

    spring security控制session本文给你描述在spring security中如何控制http session.包括session超时.启用并发session以及其他高级安全配置. 创 ...

  4. Spring Security笔记:使用数据库进行用户认证(form login using database)

    在前一节,学习了如何自定义登录页,但是用户名.密码仍然是配置在xml中的,这样显然太非主流,本节将学习如何把用户名/密码/角色存储在db中,通过db来实现用户认证 一.项目结构 与前面的示例相比,因为 ...

  5. (二)spring Security 自定义登录页面与校验用户

    文章目录 配置 security 配置下 MVC 自定义登录页面 自定义一个登陆成功欢迎页面 效果图 小结: 使用 Spring Boot 的快速创建项目功能,勾选上本篇博客需要的功能:web,sec ...

  6. 使用Spring Security控制会话

    1.概述 在本文中,我们将说明Spring Security如何允许我们控制HTTP会话.此控件的范围从会话超时到启用并发会话和其他高级安全配置. 2.会话何时创建? 我们可以准确控制会话何时创建以及 ...

  7. 使用 spring security 中的BcryptPasswordEncoder对象对用户密码进行加密

    一.引入security启动器 在子工程中直接引入,不用指定版本号 二.在启动类中把BCryptPasswordEncoder对象注入到容器中 三.在service 层注入 四. 调用encode方法 ...

  8. Spring Security 入门(1-8)缓存EhCache

  9. spring security LDAP获取用户信息

    很多企业内部使用LDAP保存用户信息,这章我们来看一下如何从LDAP中获取Spring Security所需的用户信息. 首先在pom.xml中添加ldap所需的依赖. <dependency& ...

随机推荐

  1. zookeeper源码分析二FASTLEADER选举算法

    如何在zookeeper集群中选举出一个leader,zookeeper使用了三种算法,具体使用哪种算法,在配置文件中是可以配置的,对应的配置项是"electionAlg",其中1 ...

  2. AngularJS学习之模块

    1.模块定义了一个应用程序:模块是应用程序中不同部分的容器:模块是应用控制器的容器:控制器通常属于一个模块 2.创建模块:你可以通过AngularJS的angular.module函数来创建模块: & ...

  3. javascript关键字和保留字

    1 关键字breakcasecatchcontinuedefaultdeletedoelsefinallyforfunctionifininstanceofnewreturnswitchthisthr ...

  4. 【转】浅谈 C++ 中的 new/delete 和 new[]/delete[]

    在 C++ 中,你也许经常使用 new 和 delete 来动态申请和释放内存,但你可曾想过以下问题呢? new 和 delete 是函数吗? new [] 和 delete [] 又是什么?什么时候 ...

  5. js 获取系统时间

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. linux TCP Wrappers

    1. TCP Wrapper简介 (出处:http://www.softpanorama.org/Net/Network_security/TCP_wrappers/) (维基百科)TCP Wrapp ...

  7. set+几何 LA 5908 Tracking RFIDs

    题目传送门 题意:给一些传感器,范围在r内,再给一些询问点,问这些点能有几个传感器收到,当有墙隔绝时信号减弱,范围变小 分析:set存储传感器,用set的find来查找是否是传感器.因为询问点少,可以 ...

  8. 模拟 Codeforces Round #288 (Div. 2) A. Pasha and Pixels

    题目传送门 /* 模拟水题:给定n*m的空白方格,k次涂色,将(x,y)处的涂成黑色,判断第几次能形成2*2的黑色方格,若不能,输出0 很挫的判断四个方向是否OK */ #include <cs ...

  9. MFC 打开文件夹 调用其他程序 打开文件

    ShellExecute(NULL,TEXT("OPEN"),要打开的文件的路径,NULL,NULL,SW_SHOWNORMAL); ShellExecute(NULL, &quo ...

  10. Moscow Pre-Finals Workshop 2016. National Taiwan U Selection

    A. As Easy As Possible 每个点往右贪心找最近的点,可以得到一棵树,然后倍增查询即可. 时间复杂度$O((n+m)\log n)$. #include <bits/stdc+ ...