写点什么

springsecurity 默认用户生成

作者:周杰伦本人
  • 2022 年 3 月 07 日
  • 本文字数:3920 字

    阅读完需:约 13 分钟

springsecurity 默认用户生成

springboot 背后默默做了很多事情:


  • 开启 springSecurity 自动化配置,开启后,会自动创建一个名为 SpringSecurityFilterChain 的过滤器,并注入到 spring 容器中,这个过滤器将负责所有的安全管理,包括用户的认证,授权,重定向到登录页面等(springSecurityFilterChain 实际上代理了 SpringSecurity 中的过滤器链)

  • 创建一个 UserDetailsService 实例,UserDetailsService 负责提供用户数据,默认的用户数据是基于内存的用户,用户名为 user,密码为随机生成的 UUID 字符串。

  • 给用户生成一个默认的登录页面。

  • 开启 CSRF 攻击防御。

  • 开启会话固定攻击防御。

  • 集成 X-XSS-Protection

  • 集成 X-Frame-Options 以防止单击劫持。

默认用户生成

SpringSecurity 定义 UserDetails 接口来规范开发者自定义的用户对象


负责提供用户数据源的接口是 UserDetailsService


springSecurity 为 UserDetailsService 提供了默认实现,默认是 InMemoryUserDetailsManager


springboot 之所以零配置使用 SpringSecurity 是因为他提供了很多自动化配置,针对 UserDetailsService 的自动化配置是 UserDetailsServiceAutoConfiguration


//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//
package org.springframework.boot.autoconfigure.security.servlet;
import java.util.List;import java.util.regex.Pattern;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.factory.ObjectProvider;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.security.SecurityProperties;import org.springframework.boot.autoconfigure.security.SecurityProperties.User;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Lazy;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.authentication.AuthenticationProvider;import org.springframework.security.config.annotation.ObjectPostProcessor;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.provisioning.InMemoryUserDetailsManager;import org.springframework.util.StringUtils;
@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);
public UserDetailsServiceAutoConfiguration() { }
@Bean @ConditionalOnMissingBean( type = {"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository"} ) @Lazy public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) { User user = properties.getUser(); List<String> roles = user.getRoles(); return new InMemoryUserDetailsManager(new UserDetails[]{org.springframework.security.core.userdetails.User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()}); }
private String getOrDeducePassword(User user, PasswordEncoder encoder) { String password = user.getPassword(); if (user.isPasswordGenerated()) { logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword())); }
return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password; }}
复制代码


从上述代码中,有两个比较重要的促使系统自动提供一个 InMemoryUserDetailsManager 的实例:


  1. 当前 classpath 下存在 AuthenticationManager

  2. 当前项目中,系统没有提供 AuthenticationManager AuthenticationProvider UserDetailsService ClientRegistrationRepository


满足以上条件,springSecurity 会创建 InMemoryUserDetailsManager 实例,从方法中可以看到,用户数据源来自 SecurityProperties#getUser 方法


//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//
package org.springframework.boot.autoconfigure.security;
import java.util.ArrayList;import java.util.Arrays;import java.util.HashSet;import java.util.List;import java.util.Set;import java.util.UUID;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.web.servlet.DispatcherType;import org.springframework.util.StringUtils;
@ConfigurationProperties( prefix = "spring.security")public class SecurityProperties { public static final int BASIC_AUTH_ORDER = 2147483642; public static final int IGNORED_ORDER = -2147483648; public static final int DEFAULT_FILTER_ORDER = -100; private final SecurityProperties.Filter filter = new SecurityProperties.Filter(); private SecurityProperties.User user = new SecurityProperties.User();
public SecurityProperties() { }
public SecurityProperties.User getUser() { return this.user; }
public SecurityProperties.Filter getFilter() { return this.filter; }
public static class User { private String name = "user"; private String password = UUID.randomUUID().toString(); private List<String> roles = new ArrayList(); private boolean passwordGenerated = true;
public User() { }
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)) { 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; } }
public static class Filter { private int order = -100; private Set<DispatcherType> dispatcherTypes;
public Filter() { this.dispatcherTypes = new HashSet(Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST)); }
public int getOrder() { return this.order; }
public void setOrder(int order) { this.order = order; }
public Set<DispatcherType> getDispatcherTypes() { return this.dispatcherTypes; }
public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) { this.dispatcherTypes = dispatcherTypes; } }}
复制代码


我们可以看到默认用户为 user 默认密码是 UUID


默认通过 getOrDeducePassword 方法中进行二次处理,默认 encoder 为 null ,就在密码加一个{noop}前缀,我们可以通过配置文件添加配置来修改 SecurityProperties.User 类中的属性


spring.security.user.name=adminspring.security.user.password=123spring.security.user.roles=admin,user
复制代码


用户头像

还未添加个人签名 2020.02.29 加入

还未添加个人简介

评论

发布
暂无评论
springsecurity默认用户生成_周杰伦本人_InfoQ写作平台