作为示例,以下代码尝试处理简单登录,使用密码编码器对db表进行身份验证。 表单帖子包括一个“客户端”,其中一个进行身份验证,一个持久的IP地址和一些URL链接后登录的URL链接信息。 (今天的单页网页应用程序的所有基本内容) 我最初使用xml配置工作,但javaConfig让我卡住了。
我不知道userDetailsService,AuthenticationManagerBuilder和PasswordEncoder如何在SecurityConfiguration中进行交互。我获得了服务的登录数据,但我不确定应用spring authenticationProvider的地点或时间,或者我甚至需要一个。
我的用户实现UserDetails并保存必填字段。 我填充那些并在我的CustomUserDetailsService中授予权限。 我如何/何时/为什么需要auth.authenticationProvider(authenticationProvider()),如果我在服务中使用登录/密码检查数据库?
我的UserDetailsService现在似乎正在执行两次。
spring如何获取提交的密码,对其进行编码并与存储在db中的密码进行比较? 如何使用与创建用户时创建/保留p / w时使用的盐相同的盐?
当authenticationProvider()也设置userDetailsService时,为什么configureGlobal()定义了auth.userDetailsService和auth.authenticationProvider?
为什么我的大脑如此之小以至于我无法理解这一点? :)
@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;
}
}
答案 0 :(得分:3)
我的用户实现UserDetails并保存必填字段。一世 在我的CustomUserDetailsService中填充那些并授予权限。 怎么/何时/为什么我需要一个 auth.authenticationProvider(authenticationProvider()),如果我检查db 在我的服务中使用登录/密码?
我认为你想要的是:
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
userDetailsService 方法是创建DaoAuthenticationProvider
bean的快捷方式!您不应该同时使用两者,只需两种不同的方式来配置相同的东西。 authenticationProvider 方法用于更多自定义设置。
spring如何获取提交的密码,对其进行编码并进行比较 存储在db中的?怎么知道使用相同的盐 在创建用户时创建/保留p / w时使用?
如果您使用BCrypt,则盐将存储在编码的密码值中。盐是第三个$(美元)符号后的前22个字符。 matches方法负责检查密码。
为什么configureGlobal()定义了auth.userDetailsService和 当authenticationProvider()也设置时,auth.authenticationProvider userDetailsService?
见上文。这可能是用户详细信息加载两次的原因。
更新:您在UserDetailsService中获取密码和其他详细信息很奇怪。这应该只根据用户名加载用户,例如:
User user = userDAO.getUserByUserLogonPassword(userLogon);
返回的User对象应包含已编码(存储)的密码,而不是输入的密码。 Spring Security会为您进行密码检查。您不应该修改UserDetailsService中的User对象。
答案 1 :(得分:1)
“我不知道userDetailsService,AuthenticationManagerBuilder和PasswordEncoder是如何”
UserDetailsService设置您可以从Spring访问的User。如果您希望在用户的上下文中存储更多用户信息,则需要实现自己的用户并使用自定义用户详细信息服务进行设置。 e.g。
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();
}
除了将其传递给您的身份验证提供程序之外,您还需要执行更多操作。
最后,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);
}