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

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

相关推荐
进击的女IT1 分钟前
SpringBoot上传图片实现本地存储以及实现直接上传阿里云OSS
java·spring boot·后端
杨半仙儿还未成仙儿1 小时前
Spring框架:Spring Core、Spring AOP、Spring MVC、Spring Boot、Spring Cloud等组件的基本原理及使用
spring boot·spring·mvc
一 乐1 小时前
学籍管理平台|在线学籍管理平台系统|基于Springboot+VUE的在线学籍管理平台系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习
IT学长编程3 小时前
计算机毕业设计 二手图书交易系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·二手图书交易系统
艾伦~耶格尔4 小时前
Spring Boot 三层架构开发模式入门
java·spring boot·后端·架构·三层架构
man20174 小时前
基于spring boot的篮球论坛系统
java·spring boot·后端
Java探秘者5 小时前
Maven下载、安装与环境配置详解:从零开始搭建高效Java开发环境
java·开发语言·数据库·spring boot·spring cloud·maven·idea
攸攸太上5 小时前
Spring Gateway学习
java·后端·学习·spring·微服务·gateway
苹果醋35 小时前
大模型实战--FastChat一行代码实现部署和各个组件详解
java·运维·spring boot·mysql·nginx
罗曼蒂克在消亡5 小时前
graphql--快速了解graphql特点
后端·graphql