springsecurity默認用戶生成

本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰前端

springsecurity默認用戶生成

springboot背後默默作了不少事情:java

  • 開啓springSecurity自動化配置,開啓後,會自動建立一個名爲SpringSecurityFilterChain的過濾器,並注入到spring容器中,這個過濾器將負責全部的安全管理,包括用戶的認證,受權,重定向到登陸頁面等(springSecurityFilterChain實際上代理了SpringSecurity中的過濾器鏈)
  • 建立一個UserDetailsService實例,UserDetailsService負責提供用戶數據,默認的用戶數據是基於內存的用戶,用戶名爲user,密碼爲隨機生成的UUID字符串。
  • 給用戶生成一個默認的登陸頁面。
  • 開啓CSRF攻擊防護。
  • 開啓會話固定攻擊防護。
  • 集成X-XSS-Protection
  • 集成X-Frame-Options以防止單擊劫持。

默認用戶生成

SpringSecurity定義UserDetails接口來規範開發者自定義的用戶對象web

負責提供用戶數據源的接口是UserDetailsServicespring

springSecurity爲UserDetailsService提供了默認實現,默認是InMemoryUserDetailsManagerapache

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方法springboot

//
// 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 默認密碼是UUIDmarkdown

默認經過getOrDeducePassword方法中進行二次處理,默認encoder爲null ,就在密碼加一個{noop}前綴,咱們能夠經過配置文件添加配置來修改SecurityProperties.User類中的屬性dom

spring.security.user.name=admin
spring.security.user.password=123
spring.security.user.roles=admin,user
複製代碼
相關文章
相關標籤/搜索