spring security,UserDetailsService,authenticationProvider,password encoder.. i'm lost

gk7wooem  于 2023-04-10  发布在  Spring
关注(0)|答案(3)|浏览(66)

首先,我已经阅读/重读(重复10次),至少6本关于Spring和Spring安全性的书,并在Google上搜索我的大脑试图弄清楚这一切。
在w/ spring工作了10年之后,我仍然发现有太多的注解定义,注入,组件,配置注解魔法正在进行,以至于我对我应该理解我的应用程序没有信心。在线示例要么是xml-config,不完整,done n diff. ways,过于简单化,使用较旧的spring,冲突,只是没有构建来处理基本的现实用例。
作为一个例子,下面的代码试图处理一个简单的登录,使用密码编码器验证到db表. form post包括一个“客户端”,一个持久化的IP地址和一些用于深度链接post登录的url路径信息.(所有真正的基本东西对于今天的单页web应用程序)我最初使用xml config进行此工作,但javaConfig让我卡住了.
我不知道userDetailsService、AuthenticationManagerBuilder和PasswordEncoder如何在SecurityConfiguration中交互。我获取服务的登录数据,但不确定在何处或何时应用spring authenticationProvider,或者我是否需要一个。
我的用户实现UserDetails并保存所需的字段。我在我的CustomUserDetailsService中填充这些并授予权限。如果我在服务中使用logon/password检查数据库,如何/何时/为什么我需要auth.authenticationProvider(authenticationProvider())?
我的UserDetailsService现在似乎执行了两次。
spring如何获取提交的密码,对其进行编码并与存储在db中的密码进行比较?它如何知道使用与创建用户时创建/持久化p/w时使用的相同的salt?
为什么configureGlobal()同时定义auth.userDetailsService和auth.authenticationProvider,而authenticationProvider()也设置了userDetailsService?
为什么我的大脑这么小,我不能理解这一点?:)

@Service
public class CustomUserDetailsService implements UserDetailsService {

@Autowired
private ClientDAO clientDAO;
@Autowired
private UserDAO userDAO;

public UserDetails loadUserByUsername(String multipartLogon) throws UsernameNotFoundException, DataAccessException {

    Boolean canAccess = false;
    Long clientId = null;
    String userLogon = null;
    String password = null;
    String id = null;
    String entryUrl = null;
    String ipAddress = null;
    String urlParam = null;
    String[] strParts = multipartLogon.split(":");
    try {
        userLogon = strParts[0];
        password = strParts[1];
        id = strParts[2];
        entryUrl = strParts[3];
        ipAddress = strParts[4];
        urlParam = strParts[5];
    } catch(IndexOutOfBoundsException ioob) { }
    Client client = new Client();
    if (!"".equals(id)) {
        clientId = IdUtil.toLong(id);
        client = clientDAO.getClient(clientId);
    }

    //BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    //String encodedPassword = passwordEncoder.encode(password);

    //String encodedPassword = "$2a$22$6UiVlDEOv6IQWjKkLm.04uN1yZEtkepVqYQ00JxaqPCtjzwIkXDjy";

    User user = userDAO.getUserByUserLogonPassword(userLogon, password); //encodedPassword?
    user.isCredentialsNonExpired = false;
    Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
    for (UserRole userRole : userDAO.getUserRolesForUser(user)) {
        if (userRole.getRole().getActiveStatus()) {
            authorities.add(new SimpleGrantedAuthority(userRole.getRole().getRoleName()));
            user.isCredentialsNonExpired = true;
        }
    }       
    user.setAuthorities(authorities);
    user.setPassword(password); //encodedPassword?
    user.setUsername(user.getUserLogon());
    user.isAccountNonExpired = false;
    user.isAccountNonLocked = false;

    List<ClientUser> clientUsers = clientDAO.getClientUsersForUser(user);
    for (ClientUser clientUser : clientUsers) {
        if (clientUser.getClient().getClientId().equals(client.getClientId())) {
            canAccess = true;
            break;
        }
    }

    user.isEnabled = false;
    if (user.getActiveStatus() && canAccess) {
        user.isAccountNonExpired = true;
        user.isAccountNonLocked = true;
        user.isEnabled = true;

        Session session = userDAO.getSessionForUser(user);
        if (session == null) { session = new Session(); }
        session.setUser(user);
        session.setDateLogon(Calendar.getInstance().getTime());
        session.setClient(client);
        session.setEntryUrl(entryUrl);
        session.setUrlParam(urlParam);
        session.setIPAddress(ipAddress);
        session.setActive(true);
        userDAO.persistOrMergeSession(session);
    }
    return user;
}

}
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
CustomUserDetailsService customUserDetailsService;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(customUserDetailsService);
    auth.authenticationProvider(authenticationProvider());
}

@Bean
public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

@Bean
public DaoAuthenticationProvider authenticationProvider() {
    DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
    authenticationProvider.setUserDetailsService(customUserDetailsService);
    authenticationProvider.setPasswordEncoder(passwordEncoder());
    return authenticationProvider;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()

        .authorizeRequests()
            .antMatchers("/conv/a/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_COURT_ADMIN')")
            .antMatchers("/conv/u/**").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN') or hasRole('ROLE_COURT_ADMIN')")
            .antMatchers("/**").permitAll()
            .and()

        .formLogin()
            .loginPage("/conv/common/logon")
            .usernameParameter("multipartLogon")
            .loginProcessingUrl("/conv/common/logon")
            .defaultSuccessUrl("/conv/")
            .failureUrl("/conv/common/logon?error=1")
            .and()

        .logout()
            .logoutUrl("/conv/common/logout")
            .logoutSuccessUrl("/conv/")
            .permitAll()
            .and()

        .rememberMe()
            .key("conv_key")
            .rememberMeServices(rememberMeServices())
            .useSecureCookie(true);
}

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring()
        .antMatchers("/common/**")
        .antMatchers("/favicon.ico");
}

@Bean
public RememberMeServices rememberMeServices() {
    TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("conv_key", customUserDetailsService);
    rememberMeServices.setCookieName("remember_me_cookie");
    rememberMeServices.setParameter("remember_me_checkbox");
    rememberMeServices.setTokenValiditySeconds(2678400); //1month
    return rememberMeServices;
}

}
pvabu6sv

pvabu6sv1#

我的用户实现UserDetails并保存所需的字段。我在我的CustomUserDetailsService中填充这些并授予权限。如果我在服务中使用logon/password检查数据库,如何/何时/为什么我需要auth.authenticationProvider(authenticationProvider())?
我想你想要的是:

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
  • userDetailsService* 方法是创建DaoAuthenticationProvider bean的快捷方式!您不应该同时使用这两种方法,它只是两种不同的方式来配置同一件事。authenticationProvider 方法用于更多的自定义设置。

spring如何获取提交的密码,对其进行编码并与存储在db中的密码进行比较?它如何知道使用与创建用户时创建/持久化p/w时使用的相同的salt?
如果你使用的是BCrypt,salt存储在密码值中。salt是第三个$(美元)符号后的前22个字符。matches方法负责检查密码。
为什么configureGlobal()同时定义auth.userDetailsService和auth.authenticationProvider,而authenticationProvider()也设置了userDetailsService?
这可能就是为什么用户详细信息被加载两次的原因。

**更新:**你在UserDetailsService中获取密码和其他详细信息是很奇怪的。这应该只基于用户名加载用户,比如:

User user = userDAO.getUserByUserLogonPassword(userLogon);

返回的User对象应该包含编码(存储)的密码,而不是输入的密码。Spring Security会为您进行密码检查。您不应该在UserDetailsService中修改User对象。

w1e3prcc

w1e3prcc2#

好吧,问题真多。我来回答这个:
“我不知道userDetailsService、AuthenticationManagerBuilder和PasswordEncoder“
UserDetailsService设置了你可以从Spring访问的User。如果你想要更多的用户信息存储在用户的上下文中,你需要实现你自己的用户,并使用你的自定义用户详细信息服务来设置。例如。

public class CustomUser extends User implements UserDetails, CredentialsContainer {
private Long id;
private String firstName;
private String lastName;
private String emailAddress;
....

然后,在自定义UserDetailsService中,设置属性:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
DatabaseEntity databaseUser = this.userRepository.findByUsernameIgnoreCase(username);

customUser customUser = databaseUser.getUserDetails();
customUser.setId(databaseUser.getId());
customUser.setFirstName(databaseUser.getFirstname());
.....

密码编码器,是Spring用来比较纯文本密码和数据库中加密哈希的机制。你可以使用BCryptPasswordEncoder:

@Bean
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

除了将其传递给您的auth提供商之外,您还需要做更多的事情。
最后,configureGlobal是连接的地方。您可以定义Spring要使用的用户详细信息服务和身份验证提供者。
在我的例子中,我使用自定义身份验证提供程序来限制失败的登录尝试:

@Component("authenticationProvider")
public class LimitLoginAuthenticationProvider extends DaoAuthenticationProvider {

然后我把所有东西都连接起来

@Autowired
@Qualifier("authenticationProvider")
AuthenticationProvider authenticationProvider;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    LimitLoginAuthenticationProvider provider = (LimitLoginAuthenticationProvider)authenticationProvider;
    provider.setPasswordEncoder(passwordEncoder);
    auth.userDetailsService(customUserDetailsService()).passwordEncoder(passwordEncoder);
    auth.authenticationProvider(authenticationProvider);
}
yvfmudvl

yvfmudvl3#

使用任何语言创建自己的函数名和变量不要害怕探索尽可能简单不要忘记使用10行以上的代码来获得简单的结果

相关问题