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. 如何配置VS使得可以通过域名或IP访问

    一.前言: 在平时使用Visio Studio进行网站开发,经常会遇到一个问题.既要可以使用VS的Debug一步步跟进项目的走向,又必须是外界可以访问的.这个问题会在微信开发中非常常见,就拿微信开发中 ...

  2. 元素化设计原理及规则v1.0

    一.元素设计架构 元素设计架构展示在基于元素化设计的思想下,系统各元素之间如何相互协作,并完成整个系统搭建. 架构中以Entity(数据)为中心,由Entity产生数据库表结构,并且Entity作为业 ...

  3. 兄弟连PHP培训教你提升效率的20个要点

    兄弟连PHP培训教你提升效率的20个要点 用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则 不会,注意:只有echo能这么做,它是一种可以把多个字 ...

  4. Java 面试宝典-2017

    http://www.cnblogs.com/nelson-hu/p/7190163.html Java面试宝典-2017   Java面试宝典2017版 一. Java基础部分........... ...

  5. MyBatis动态SQL小结

    6:用于实现动态sql的元素及其用法 if+set--完成更新操作 if+where --完成多条件查询 if+完成多条件查询(替代where)或完成更新操作(替代set) choose(when,o ...

  6. 关于JSP页面Property [ssid] not found on type [java.lang.String]错误的注意事项

    转发于:http://blog.csdn.net/w86440044/article/details/28277177

  7. 58 同城 iOS 客户端 iOS11 及 iPhone X 适配实践

    一.前言 前段时间 WWDC 大会上苹果推出了 iOS11 系统 和 iPhone X 新机型,相信各个 iOS 团队的开发者都已经在计划新系统和新机型的适配工作了.不得不说,新系统和新机型的发布确实 ...

  8. NVL2 这个函数,

    NVL2(expr1,expr2,expr3)     如果参数表达式expr1值为NULL,则NVL2()函数返回参数表达式expr3的值:如果参数表达式expr1值不为NULL,则NVL2()函数 ...

  9. [日常] NOIP 2017滚粗记

    突然挑了这么个滑稽的时间补了游记... (成绩日常延时再加上人太菜估计基本上就是颓废记录) 然而文化课太废可能会被强制退役QAQ所以先补了再说吧 day0 一大早被老姚交代了个开十一机房门的任务... ...

  10. Oracle之SQL优化专题01-查看SQL执行计划的方法

    在我2014年总结的"SQL Tuning 基础概述"中,其实已经介绍了一些查看SQL执行计划的方法,但是不够系统和全面,所以本次SQL优化专题,就首先要系统的介绍一下查看SQL执 ...