1、用户信息从数据库获取

通常我们的用户信息都不会向第一节示例中那样简单的写在配置文件中,而是从其它存储位置获取,比如数据库。根据之前的介绍我们知道用户信息是通过 UserDetailsService 获取的,要从数据库获取用户信息,我们就需要实现自己的 UserDetailsService。幸运的是像这种常用的方式 Spring Security 已经为我们做了实现了。

使用 jdbc-user-service 获取

在 Spring Security 的命名空间中在 authentication-provider 下定义了一个 jdbc-user-service 元素,通过该元素我们可以定义一个从数据库获取 UserDetails 的 UserDetailsService。jdbc-user-service 需要接收一个数据源的引用。

   <security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource"/>
</security:authentication-provider>
</security:authentication-manager>

上述配置中 dataSource 是对应数据源配置的 bean 引用。使用此种方式需要我们的数据库拥有如下表和表结构。

这是因为默认情况下 jdbc-user-service 将使用 SQL 语句 “select username, password, enabled from users where username = ?” 来获取用户信息;使用 SQL 语句 “select username, authority from authorities where username = ?” 来获取用户对应的权限;使用 SQL 语句 “select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id” 来获取用户所属组的权限。需要注意的是 jdbc-user-service 定义是不支持用户组权限的,所以使用 jdbc-user-service 时用户组相关表也是可以不定义的。如果需要使用用户组权限请使用 JdbcDaoImpl,这个在后文后讲到。

当然这只是默认配置及默认的表结构。如果我们的表名或者表结构跟 Spring Security 默认的不一样,我们可以通过以下几个属性来定义我们自己查询用户信息、用户权限和用户组权限的 SQL。

属性名 说明
users-by-username-query 指定查询用户信息的 SQL
authorities-by-username-query 指定查询用户权限的 SQL
group-authorities-by-username-query 指定查询用户组权限的 SQL

假设我们的用户表是 t_user,而不是默认的 users,则我们可以通过属性 users-by-username-query 来指定查询用户信息的时候是从用户表 t_user 查询。

   <security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service
data-source-ref="dataSource"
users-by-username-query="select username, password, enabled from t_user where username = ?" />
</security:authentication-provider>
</security:authentication-manager>

role-prefix 属性

jdbc-user-service 还有一个属性 role-prefix 可以用来指定角色的前缀。这是什么意思呢?这表示我们从库里面查询出来的权限需要加上什么样的前缀。举个例子,假设我们库里面存放的权限都是 “USER”,而我们指定了某个 URL 的访问权限 access=”ROLEUSER”,显然这是不匹配的,Spring Security 不会给我们放行,通过指定 jdbc-user-service 的 role-prefix=”ROLE\” 之后就会满足了。当 role-prefix 的值为 “none” 时表示没有前缀,当然默认也是没有的。

直接使用 JdbcDaoImpl

JdbcDaoImpl 是 UserDetailsService 的一个实现。其用法和 jdbc-user-service 类似,只是我们需要把它定义为一个 bean,然后通过 authentication-provider 的 user-service-ref 进行引用。

   <security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsService"/>
</security:authentication-manager> <bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>

如你所见,JdbcDaoImpl 同样需要一个 dataSource 的引用。如果就是上面这样配置的话我们数据库表结构也需要是标准的表结构。当然,如果我们的表结构和标准的不一样,可以通过 usersByUsernameQuery、authoritiesByUsernameQuery 和 groupAuthoritiesByUsernameQuery 属性来指定对应的查询 SQL。

用户权限和用户组权限

JdbcDaoImpl 使用 enableAuthorities 和 enableGroups 两个属性来控制权限的启用。默认启用的是 enableAuthorities,即用户权限,而 enableGroups 默认是不启用的。如果需要启用用户组权限,需要指定 enableGroups 属性值为 true。当然这两种权限是可以同时启用的。需要注意的是使用 jdbc-user-service 定义的 UserDetailsService 是不支持用户组权限的,如果需要支持用户组权限的话需要我们使用 JdbcDaoImpl。

   <security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsService"/>
</security:authentication-manager> <bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
<property name="enableGroups" value="true"/>
</bean>

2、PasswordEncoder

使用内置的 PasswordEncoder

通常我们保存的密码都不会像之前介绍的那样,保存的明文,而是加密之后的结果。为此,我们的 AuthenticationProvider 在做认证时也需要将传递的明文密码使用对应的算法加密后再与保存好的密码做比较。Spring Security 对这方面也有支持。通过在 authentication-provider 下定义一个 password-encoder 我们可以定义当前 AuthenticationProvider 需要在进行认证时需要使用的 password-encoder。password-encoder 是一个 PasswordEncoder 的实例,我们可以直接使用它,如:

   <security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsService">
<security:password-encoder hash="md5"/>
</security:authentication-provider>
</security:authentication-manager>

其属性 hash 表示我们将用来进行加密的哈希算法,系统已经为我们实现的有 plaintext、sha、sha-256、md4、md5、{sha} 和 {ssha}。它们对应的 PasswordEncoder 实现类如下:

加密算法 PasswordEncoder 实现类
plaintext PlaintextPasswordEncoder
sha ShaPasswordEncoder
sha-256 ShaPasswordEncoder,使用时new ShaPasswordEncoder(256)
md4 Md4PasswordEncoder
md5 Md5PasswordEncoder
{sha} LdapShaPasswordEncoder
{ssha} LdapShaPasswordEncoder

使用 BASE64 编码加密后的密码

使用 password-encoder 时我们还可以指定一个属性 base64,表示是否需要对加密后的密码使用 BASE64 进行编码,默认是 false。如果需要则设为 true。

加密时使用 salt 加密时使用

salt 也是很常见的需求,Spring Security 内置的 password-encoder 也对它有支持。

通过 password-encoder 元素下的子元素 salt-source,我们可以指定当前 PasswordEncoder 需要使用的 salt。

这个 salt 可以是一个常量,也可以是当前 UserDetails 的某一个属性,还可以通过实现 SaltSource 接口实现自己的获取 salt 的逻辑,

SaltSource 中只定义了如下一个方法。

public Object getSalt(UserDetails user);

下面来看几个使用 salt-source 的示例。

1.下面的配置将使用常量“abc”作为 salt。

2.下面的配置将使用 UserDetails 的 username 作为 salt。

3.下面的配置将使用自己实现的 SaltSource 获取 salt。

其中 mySaltSource 就是 SaltSource 实现类对应的 bean 的引用。 需要注意的是 AuthenticationProvider 进行认证时所使用的 PasswordEncoder,包括它们的算法和规则都应当与我们保存用户密码时是一致的。

也就是说如果 AuthenticationProvider 使用 Md5PasswordEncoder 进行认证,我们在保存用户密码时也需要使用 Md5PasswordEncoder;

如果 AuthenticationProvider 在认证时使用了 username 作为 salt,那么我们在保存用户密码时也需要使用 username 作为 salt。

如:

Md5PasswordEncoder encoder = new Md5PasswordEncoder();

encoder.setEncodeHashAsBase64(true);

System.out.println(encoder.encodePassword("user", "user"));

### 使用自定义的 PasswordEncoder

除了通过 password-encoder 使用 Spring Security 已经为我们实现了的 PasswordEncoder 之外,我们也可以实现自己的 PasswordEncoder,

然后通过 password-encoder 的 ref 属性关联到我们自己实现的 PasswordEncoder 对应的 bean 对象。

在 Spring Security 内部定义有两种类型的 PasswordEncoder,分别是 org.springframework.security.authentication.encoding.PasswordEncoder 和 org.springframework.security.crypto.password.PasswordEncoder。

直接通过 password-encoder 元素的 hash 属性指定使用内置的 PasswordEncoder 都是基于 org.springframework.security.authentication.encoding.PasswordEncoder 的实现,然而它现在已经被废弃了,Spring Security 推荐我们使用 org.springframework.security.crypto.password.PasswordEncoder,它的设计理念是为了使用随机生成的 salt。关于后者 Spring Security 也已经提供了几个实现类,更多信息请查看 Spring Security 的 API 文档。我们在通过 password-encoder 使用自定义的 PasswordEncoder 时两种 PasswordEncoder 的实现类都是支持的。

Spring Security 入门(1-4-2)Spring Security - 认证过程之AuthenticationProvider的扩展补充说明的更多相关文章

  1. Spring Security 入门(1-6-1)Spring Security - 配置文件解析和访问请求处理

    1.在pom.xml中添加maven坐标 <dependency> <groupId>org.springframework.security</groupId> ...

  2. Spring Security 入门(1-7)Spring Security - Session管理

    参考链接:https://xueliang.org/article/detail/20170302232815082 session 管理 Spring Security 通过 http 元素下的子元 ...

  3. Spring Security 入门(1-3-5)Spring Security - remember me!

    Remember-Me 功能 概述 Remember-Me 是指网站能够在 Session 之间记住登录用户的身份,具体来说就是我成功认证一次之后在一定的时间内我可以不用再输入用户名和密码进行登录了, ...

  4. Spring Security 入门(1-6-2)Spring Security - 内置的filter顺序、自定义filter、http元素和对应的filterChain

    Spring Security 的底层是通过一系列的 Filter 来管理的,每个 Filter 都有其自身的功能,而且各个 Filter 在功能上还有关联关系,所以它们的顺序也是非常重要的. 1.S ...

  5. Spring Security 入门(1-3-2)Spring Security - http元素 - intercept-url配置

    http元素下可以配置登录页面,也可以配置 url 拦截. 1.直接配置拦截url和对应的访问权限 <security:http use-expressions="false" ...

  6. Spring Security 入门(1-4-1)Spring Security - 认证过程

    理解时可结合一下这位老兄的文章:http://www.importnew.com/20612.html 1.Spring Security的认证过程 1.1.登录过程 - 如果用户直接访问登录页面 用 ...

  7. Spring Security 入门(1-3-1)Spring Security - http元素 - 默认登录和登录定制

    登录表单配置 - http 元素下的 form-login 元素是用来定义表单登录信息的.当我们什么属性都不指定的时候 Spring Security 会为我们生成一个默认的登录页面. 如果不想使用默 ...

  8. Spring Security 入门(1-2)Spring Security - 从 配置例子例子 开始我们的学习历程

    1.Spring Security 的配置文件 我们需要为 Spring Security 专门建立一个 Spring 的配置文件,该文件就专门用来作为 Spring Security 的配置. &l ...

  9. Spring Security入门(2-3)Spring Security 的运行原理 4 - 自定义登录方法和页面

    参考链接,多谢作者: http://blog.csdn.net/lee353086/article/details/52586916 http元素下的form-login元素是用来定义表单登录信息的. ...

随机推荐

  1. eclipse热部署web项目

    一.选中JavaEE视图 因为在普通的Java视图下,窗口下方没有server选项卡 二.双击Tomcat 注意:可能很多人当然包括我一开始的时候,都是喜欢右键Tomcat然后Add and remo ...

  2. 谁能用通俗的语言解释一下什么是 RPC 框架?

    转载自知乎:https://www.zhihu.com/question/25536695 知乎上很多问题的答案还是很好的,R大就经常在上面回答问题 关于RPC你的题目是RPC框架,首先了解什么叫RP ...

  3. 如何 创建一个model对象保存到LIST集合里面并取出来

    /// <summary> /// 缓存客服集合信息 /// </summary> public class model { /// <summary> /// 客 ...

  4. Hibernate之三态

    1.瞬时态 瞬时态也成为临时态或者自由态,瞬时态的实例是new命令创建.开辟内存空间的对象,不存在持久化标识OID,尚未与Hibernate Session关联,在数据库中没有任何记录,失去引用将被G ...

  5. JAVA 调用mysql存储过程

    public class Test { //连接mysql数据库 public static final String DRIVER_CLASS = "com.mysql.jdbc.Driv ...

  6. Linux-CentOS7下安装mysql5.6常见问题解决!

    第一次写博客,写的不好请大家多见谅!有遇到问题可以评论到下方,我会抽空帮大家解决!! 下载MySQL-5.6.tar安装包(包含server,client等) 首先用root用户将MySQL-5.6. ...

  7. 笔记:Spring Cloud Eureka 服务发现与消费

    服务发现与消费,其服务发现的任务是由Eureka的客户端完成,而服务的消费任务由Ribbon.JerseyClient等完成,Ribbon是一个基于HTTP和TCP的客户端负载均衡器:使用Jersey ...

  8. 学习java第一章

    本人是一名5年工作的人了,出来社会也比较早,工作经验比起刚刚出社会的大学生要和很多了,知道社会的现实与无奈,我为什么选择想学java昵,肯定受到了朋友的影响的,接下来就讲讲我学习java的过程. 1. ...

  9. java 日期格式处理

    Java 日期时间 Date类型,long类型,String类型表现形式的转换 1.java.util.Date类型转换成long类型java.util.Date dt = new Date();Sy ...

  10. 【Python】 virtualenv虚拟环境建设和管理

    [virtualenv] 用Python开发时面临的一个大问题就是每个项目需要的依赖包不一致.如果是包本身不一样倒还好,如果不同项目需要的是不同版本的包的话就会很麻烦.如果采用统一的系统Python环 ...