Spring Security 三 Spring Security 配置


前言

hello world 程序示例中,通过集成了spring security的jar包后,编写了一个hello接口,此时通过测试发现,hello接口已经被保护了,需要进行登录认证才能访问。那么用户名和密码是在哪生成的呢?


一、UserDetailsServiceAutoConfiguration

通过控制台的日志,可以看到输出的随机密码是由UserDetailsServiceAutoConfiguration配置类生成的。
源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean(
		value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class },
		type = { "org.springframework.security.oauth2.jwt.JwtDecoder",
				"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector" })
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 InMemoryUserDetailsManager(
				User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
						.roles(StringUtils.toStringArray(roles)).build());
	}

	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;
	}

}

User的源码如下:

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 void setPassword(String password) {
			if (!StringUtils.hasLength(password)) {
				return;
			}
			this.passwordGenerated = false;
			this.password = password;
		}

	}

通过源码发现,UserDetailsServiceAutoConfiguration配置类添加具有默认用户和生成密码的InMemoryUserDetailsManager 。 这可以通过提供AuthenticationManager 、 AuthenticationProvider或UserDetailsService类型的 bean 来禁用。获取的User类为SecurityProperties的内置类,可通过配置进行赋值。User的默认用户名为 user,默认的密码为UUID.randomUUID().toString() 生成的随机密码。

二、自定义UserDetailsManager

1.InMemoryUserDetailsManager

通过UserDetailsServiceAutoConfiguration配置类,可以看到使用了InMemoryUserDetailsManager。通过查看InMemoryUserDetailsManager源码,可以看到InMemoryUserDetailsManager实现了UserDetailsManager, UserDetailsPasswordService两个接口。

public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {

其中UserDetailsPasswordService定义了一个修改密码的接口:

	UserDetails updatePassword(UserDetails user, String newPassword);

再看UserDetailsManager接口,此接口继承了
UserDetailsService,是UserDetailsService的一个拓展接口。而通过UserDetailsServiceAutoConfiguration的@Conditional系列注解,我们可以知道,通过提供UserDetailsService类型的bean,可以禁用自动配置。

2.UserDetailsManager

创建自定义UserDetailsManager

  @Bean
    public UserDetailsManager userDetailsManager() {
        return new UserDetailsManager() {
      		// 为了测试方便,其他的方法 直接空实现
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                return new User("damon","{noop}1234", Collections.emptyList());
            }
        };
    }

通过loadUserByUsername方法可以看到,传入用户名,返回UserDetails。UserDetails是一个spring security定义的用户抽象接口,包含用户名,密码,权限集合等属性。框架的内部实现类,有一个User类,通过构造函数返回User类,也可以自定义UserDetails的实现类,来扩展手机号,邮箱等字段。在 Spring Security 5.0 之前,默认的 PasswordEncoder 是 NoOpPasswordEncoder,而现在默认是BCryptPasswordEncoder,此时我们设置密码时,需要加上{noop}前缀。来告诉框架此用户使用的是纯文本密码。

3. 再次启动,登录

再次启动程序后,我们会发现控制台已经没有了随机密码的输出日志。证明了我们自定义的UserDetailsManager生效了,切关闭了默认的配置类。
此时访问 hello 接口,弹出认证表单,输入配置的用户名和密码,正常返回 hello world

总结

若想通过用数据库的方式来实现用户的管理,只需要在查询用户的逻辑改成通过调用数据库即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值