前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringSecurity6从入门到实战之初始用户如何存储到内存

SpringSecurity6从入门到实战之初始用户如何存储到内存

原创
作者头像
全干程序员demo
修改2024-06-20 09:22:55
910
修改2024-06-20 09:22:55
举报

SpringSecurity6从入门到实战之初始用户如何存储到内存

文接上回,根据登录表单的提交最终得知用户相关信息存储在内存中.那么SpringSecurity是如何在项目启动时将用户信息存储到内存中的呢?

这里我们还是先回到SpringBoot加载配置的地方

UserDetailServiceAutoConfigutation 类

在 SpringBoot 的自动装配中,默认会启动配置类 UserDetailServiceAutoConfigutation ,我们接下来进入UserDetailServiceAutoConfigutation 的源码中看看

代码语言:java
复制
@AutoConfiguration
@ConditionalOnClass({AuthenticationManager.class})
@ConditionalOnBean({ObjectPostProcessor.class})
//该配置类生效的条件
@ConditionalOnMissingBean(
    value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, AuthenticationManagerResolver.class},
    type = {"org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector", "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository", "org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository"}
)
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);

    public UserDetailsServiceAutoConfiguration() {
    }
    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {
        //这里可以看到获取bean对象的user属性,配置文件中有则获取配置文件内容没有则使用默认值
        SecurityProperties.User user = properties.getUser();
        List<String> roles = user.getRoles();
        //最终返回带user属性的InMemoryUserDetailsManager对象
        return new InMemoryUserDetailsManager(new UserDetails[]{User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});
    }

    private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {
        String password = user.getPassword();
        if (user.isPasswordGenerated()) {
            logger.warn(String.format("%n%nUsing generated security password: %s%n%nThis generated password is for development use only. Your security configuration must be updated before running your application in production.%n", user.getPassword()));
        }

        return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;
    }
}

配置类 UserDetailServiceAutoConfigutation 默认生效的条件有三种情况:

  1. 在 classpath 下存在 AuthenticationManager 类
  2. 当前应用中,存在 ObjectPostProcessor 类的实例时
  3. 当前应用中,不存在 AuthenticationManager.class、 AuthenticationProvider.class、UserDetailsService.class、     AuthenticationManagerResolver.class 的实例时

这里看到了inMemoryUserDetailsManager()将user属性封装后传到了new InMemoryUserDetailsManager()中作为参数,那么我们继续看向new InMemoryUserDetailsManager()构造方法

代码语言:java
复制
	public InMemoryUserDetailsManager(UserDetails... users) {
		for (UserDetails user : users) {
			createUser(user);
		}
	}

可以看到将传入的user对象进行了创建操作,那么继续看到createUser()方法

代码语言:java
复制
	@Override
	public void createUser(UserDetails user) {
		Assert.isTrue(!userExists(user.getUsername()), "user should not exist");
		this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));
	}

这里可以看到this.users就是直接说在内存中存放user信息的map集合,将user信息一个个存入map中.在 InMemoryUserDetailsManager 类中的 loadUserByUsername() 方法中,在 map 集合 users 中根据 username 获取用户认证信息

代码语言:java
复制
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //在这里进行获取用户认证信息
		UserDetails user = this.users.get(username.toLowerCase());
		if (user == null) {
			throw new UsernameNotFoundException(username);
		}
		return new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(),
				user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
	}

这里已经知道了SpringSecurity为我们提供的初始用户时如何存储在内存中了,那么在日常开发中肯定不会将用户认证信息存储在内存中.一定是持久化到数据库里,那么我们应该如何进行操作?

UserDetailService 接口

在 UserDetailService 接口中,loadUserByUserName() 方法用于根据用户名进行认证,默认基于内存实现,不需要有后端数据库的支持。如果想修改成数据库实现,我们只需要自定义 UserDetailService 接口的实现类,并返回 UserDetails 实例即可

代码语言:java
复制
package org.springframework.security.core.userdetails;

/**
 * Core interface which loads user-specific data.
 * <p>
 * It is used throughout the framework as a user DAO and is the strategy used by the
 * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider
 * DaoAuthenticationProvider}.
 *
 * <p>
 * The interface requires only one read-only method, which simplifies support for new
 * data-access strategies.
 *
 * @author Ben Alex
 * @see org.springframework.security.authentication.dao.DaoAuthenticationProvider
 * @see UserDetails
 */
public interface UserDetailsService {

	/**
	 * Locates the user based on the username. In the actual implementation, the search
	 * may possibly be case sensitive, or case insensitive depending on how the
	 * implementation instance is configured. In this case, the <code>UserDetails</code>
	 * object that comes back may have a username that is of a different case than what
	 * was actually requested..
	 * @param username the username identifying the user whose data is required.
	 * @return a fully populated user record (never <code>null</code>)
	 * @throws UsernameNotFoundException if the user could not be found or the user has no
	 * GrantedAuthority
	 */
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

}

实现这个接口即可

修改默认用户

在 SecurityProperties 配置类中,定义了 SpringBoot 配置文件中的内容可以自动绑定到 Bean 的属性上:

于是,我们可以在 SpringBoot 的配置文件中对内存用户和密码进行设置:

代码语言:xml
复制
spring.security.user.name=admin
spring.security.user.password=123

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SpringSecurity6从入门到实战之初始用户如何存储到内存
    • UserDetailServiceAutoConfigutation 类
      • 配置类 UserDetailServiceAutoConfigutation 默认生效的条件有三种情况:
    • UserDetailService 接口
      • 修改默认用户
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档