spring-security-learn001入门案例

本文解析了 Spring Security 中默认生成随机密码的机制,包括依赖配置、密码生成逻辑及关键类的作用,帮助理解 Spring Security 的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

依赖

 <dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-security</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>

     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-devtools</artifactId>
         <scope>runtime</scope>
         <optional>true</optional>
     </dependency>
     <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>

添加一个首页

index.html

在这里插入图片描述

写入

<h1>hello</h1>

启动应用

得到一个随机密码

Using generated security password: 76f93495-f352-4c5e-b1df-b5348d6cdc2b

访问

http://localhost:8080/

会被重定向到

http://localhost:8080/login

在这里插入图片描述

输入用户名

user

密码为刚才的随机密码

76f93495-f352-4c5e-b1df-b5348d6cdc2b

登录成功

在这里插入图片描述

分析1-打印的日志来自哪?

Using generated security password

我们搜索一下jar包里面内容,idea如何操作,请参考:

idea搜索jar包内的内容

我们通过搜索可以找到代码来自于两个地方,我们通过各自打断点可以证实这个输出来自于:UserDetailsServiceAutoConfiguration的86行(根据你的版本不同,可能有差异)。

在这里插入图片描述

通过这里可以看到 生成的密码,以及有一个encoder为null。

user是什么呢?

我们查看一下

在这里插入图片描述

从这里看出来,它写死的用户名就是user,roles的size为0,没有角色。

密码的实现

在这里插入图片描述

很显然这是通过UUID来实现的,并且这里使用的User就是一个静态内部类,处于SecurityProperties这个类当中。

其源码如下:

public static class User {

	/**
	 * Default user name.
	 */
	private String name = "user";

	/**
	 * Password for the default user name.
	 */
	private String password = UUID.randomUUID().toString();

	/**
	 * Granted roles for the default user name.
	 */
	private List<String> roles = new ArrayList<>();

	private boolean passwordGenerated = true; // 用于判断,

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return this.password;
	}

	public void setPassword(String password) {
		if (!StringUtils.hasLength(password)) {
			return;
		}
		this.passwordGenerated = false;
		this.password = password;
	}

	public List<String> getRoles() {
		return this.roles;
	}

	public void setRoles(List<String> roles) {
		this.roles = new ArrayList<>(roles);
	}

	public boolean isPasswordGenerated() {
		return this.passwordGenerated;
	}

}

密码生成的过程

private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {
	String password = user.getPassword();
	if (user.isPasswordGenerated()) {
		logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
	}
	if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
		return password;
	}
	return NOOP_PASSWORD_PREFIX + password;
}
  1. 密码默认是UUID
  2. 并且isPasswordGenerated判断必然为true,然后打印日志
  3. 如果encoder不等于null的情况下或者密码满足后面的一个匹配算法则返回密码
  4. 返回一个 {noop} + 密码

生成密码的方法用于什么?

在inMemoryUserDetailsManager方法中被调用

@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 InMemoryUserDetailsManager(
			User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
					.roles(StringUtils.toStringArray(roles)).build());
}

整体生成了一个InMemoryUserDetailsManager ,叫做内存型的用户细节管理器,见名知意了,比如有哪些用户,用户名密码都是什么之类的细节。

看看这个类的构造方法

public InMemoryUserDetailsManager(UserDetails... users) {
	for (UserDetails user : users) {
		createUser(user);
	}
}

接受不定长参数,可以放入任意的UserDetails。

所以

User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
						.roles(StringUtils.toStringArray(roles)).build()

这是在生成一个UserDetails,意味着未来我们有自定义的用户的时候也需要和UserDetails打交道。

这个构造模式做了几件事情

  • 指定用户名
  • 指定密码
  • 指定角色们

这个密码入参就是调用了上面的getOrDeducePassword方法,也就是日志打印的地方。

总结

出现了系列的重要类级别文件,肯定需要我们进一步研究

  • PasswordEncoder
  • User (spring中定义的)
  • UserDetails
  • UserDetailsManager

我们通过命名法可以绘制一个简单关系图

在这里插入图片描述

后续我们就要挨个分析他们。

源码

https://github.com/qiudaozhang/spring-security-learn001

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

singkingcho

有帮助?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值