1. 前言
前一篇介绍了
Spring Security
入门的基础准备。从今天开始我们来一步步窥探它是如何工作的。我们又该如何驾驭它。请多多关注公众号:
Felordcn
。本篇将通过
Spring Boot 2.x
来讲解
Spring Security
中的用户主体
UserDetails
。以及从中找点乐子。
2. Spring Boot 集成 Spring Security
这个简直老生常谈了。不过为了照顾大多数还是说一下。集成
Spring Security
只需要引入其对应的
Starter
组件。
Spring Security
不仅仅能保护
Servlet Web
应用,也可以保护
Reactive Web
应用,本文我们讲前者。我们只需要在
Spring Security
项目引入以下依赖即可:
<dependencies>
<!-- actuator 指标监控 非必须 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- spring security starter 必须 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- spring mvc servlet web 必须 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok 插件 非必须 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
复制代码
3. UserDetailsServiceAutoConfiguration
启动项目,访问
Actuator
端点
http://localhost:8080/actuator
会跳转到一个登录页面
http://localhost:8080/login
如下:
要求你输入用户名
Username
(默认值为user)和密码
Password
。密码在springboot控制台会打印出类似
Using generated security password: e1f163be-ad18-4be1-977c-88a6bcee0d37
的字样,后面的长串就是密码,当然这不是生产可用的。如果你足够细心会从控制台打印日志发现该随机密码是由
UserDetailsServiceAutoConfiguration
配置类生成的,我们就从它开始顺藤摸瓜来一探究竟。
3.1 UserDetailsService
UserDetailsService
接口。该接口只提供了一个方法:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
复制代码
该方法很容易理解: 通过用户名来加载用户 。这个方法主要用于从系统数据中查询并加载具体的用户到Spring Security中。
3.2 UserDetails
从上面
UserDetailsService
可以知道最终交给Spring Security的是
UserDetails
。该接口是提供用户信息的核心接口。该接口实现仅仅存储用户的信息。后续会将该接口提供的用户信息封装到认证对象
Authentication
中去。
UserDetails
默认提供了:
-
用户的权限集, 默认需要添加
ROLE_
前缀 -
用户的加密后的密码, 不加密会使用
{noop}
前缀 - 应用内唯一的用户名
- 账户是否过期
- 账户是否锁定
- 凭证是否过期
- 用户是否可用
如果以上的信息满足不了你使用,你可以自行实现扩展以存储更多的用户信息。比如用户的邮箱、手机号等等。通常我们使用其实现类:
org.springframework.security.core.userdetails.User
复制代码
该类内置一个建造器
UserBuilder
会很方便地帮助我们构建
UserDetails
对象,后面我们会用到它。
3.3 UserDetailsServiceAutoConfiguration
UserDetailsServiceAutoConfiguration
全限定名为:
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
复制代码
源码如下:
@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {
private static final String NOOP_PASSWORD_PREFIX = "{noop}";
private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");
private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean(
type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
@Lazy
public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties,
ObjectProvider<PasswordEncoder> passwordEncoder) {
SecurityProperties.User user = properties.getUser();
List<String> roles = user.getRoles();
return new