使用没有 LoadTimeWeaving 的服务自定义 UserDetails
Customizing UserDetails using a service without LoadTimeWeaving
我需要使用对象列表进行访问控制。获取该列表的查询非常复杂且冗长,因此我想在用户向服务器进行身份验证时将其缓存在用户对象中。
我决定尝试通过自定义实现 UserDetailService 和这个 WebSecurityConfig:
来解决这个问题
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
String ldapURI = "ldap://" + ldaHost + ":" + ldapPort + "/" + ldapBasis;
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapURI);
contextSource.setUserDn(ldapUser);
contextSource.setPassword(ldapPassword);
contextSource.afterPropertiesSet();
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth
.ldapAuthentication().userDetailsContextMapper(new LdapUserDetailsContextMapper());
ldapAuthenticationProviderConfigurer
.userSearchFilter("(&(criteria={0}))")
.userSearchBase("ou=usersearchbase").contextSource(contextSource);
}
}
用数据填充用户对象的 ContextMapper:
public class LdapUserDetailsContextMapper implements UserDetailsContextMapper {
private final org.slf4j.Logger log = LoggerFactory.getLogger(this.getClass());
private DataService dataService;
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<? extends GrantedAuthority> authorities) {
AppUser user = new AppUser();
user.setUsername(ctx.getStringAttribute("uid"));
user.setName(ctx.getStringAttribute("cn"));
user.setSurname(ctx.getStringAttribute("sn"));
user.setMail(ctx.getStringAttribute("mail"));
user.setRoleUser();
user.setManaged(dataService.getManagedData(user.getMail()));
return user;
}
问题是我没有办法将我的 DataService 注入到这个进程中——ContextMapper 和 @Configure class 都不是托管 bean,这使得 @Autowired 不可能。我想避免使用 LoadTimeWeaving,因为使用它会使部署变得非常困难 - 我还有其他选择吗?
我发现了这两个类似的问题:Why is my Spring @Autowired field null?在控制器class中有同样的问题,它可以变成托管bean;其他两个提到的解决方案对我都不起作用(dataService 始终为空)和
再次推荐LoadTimeWeaving。
我还发现明显的解决方案提到在 init 方法中使用 @Autowired,但我也无法让它工作(尝试使用注入的数据服务调用 ContextMapper 构造函数)
只需将其设为 spring 托管 bean。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
String ldapURI = "ldap://" + ldaHost + ":" + ldapPort + "/" + ldapBasis;
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapURI);
contextSource.setUserDn(ldapUser);
contextSource.setPassword(ldapPassword);
contextSource.afterPropertiesSet();
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth
.ldapAuthentication().userDetailsContextMapper(userDetailsContextMapper());
ldapAuthenticationProviderConfigurer
.userSearchFilter("(&(criteria={0}))")
.userSearchBase("ou=usersearchbase").contextSource(contextSource);
}
@Bean
public LdapUserDetailsContextMapper userDetailsContextMapper() {
return new LdapUserDetailsContextMapper();
}
}
并在 LdapUserDetailsContextMapper
内用 @Autowired
注释您的字段。
我需要使用对象列表进行访问控制。获取该列表的查询非常复杂且冗长,因此我想在用户向服务器进行身份验证时将其缓存在用户对象中。 我决定尝试通过自定义实现 UserDetailService 和这个 WebSecurityConfig:
来解决这个问题@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
String ldapURI = "ldap://" + ldaHost + ":" + ldapPort + "/" + ldapBasis;
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapURI);
contextSource.setUserDn(ldapUser);
contextSource.setPassword(ldapPassword);
contextSource.afterPropertiesSet();
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth
.ldapAuthentication().userDetailsContextMapper(new LdapUserDetailsContextMapper());
ldapAuthenticationProviderConfigurer
.userSearchFilter("(&(criteria={0}))")
.userSearchBase("ou=usersearchbase").contextSource(contextSource);
}
}
用数据填充用户对象的 ContextMapper:
public class LdapUserDetailsContextMapper implements UserDetailsContextMapper {
private final org.slf4j.Logger log = LoggerFactory.getLogger(this.getClass());
private DataService dataService;
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<? extends GrantedAuthority> authorities) {
AppUser user = new AppUser();
user.setUsername(ctx.getStringAttribute("uid"));
user.setName(ctx.getStringAttribute("cn"));
user.setSurname(ctx.getStringAttribute("sn"));
user.setMail(ctx.getStringAttribute("mail"));
user.setRoleUser();
user.setManaged(dataService.getManagedData(user.getMail()));
return user;
}
问题是我没有办法将我的 DataService 注入到这个进程中——ContextMapper 和 @Configure class 都不是托管 bean,这使得 @Autowired 不可能。我想避免使用 LoadTimeWeaving,因为使用它会使部署变得非常困难 - 我还有其他选择吗?
我发现了这两个类似的问题:Why is my Spring @Autowired field null?在控制器class中有同样的问题,它可以变成托管bean;其他两个提到的解决方案对我都不起作用(dataService 始终为空)和
我还发现明显的解决方案提到在 init 方法中使用 @Autowired,但我也无法让它工作(尝试使用注入的数据服务调用 ContextMapper 构造函数)
只需将其设为 spring 托管 bean。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
String ldapURI = "ldap://" + ldaHost + ":" + ldapPort + "/" + ldapBasis;
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapURI);
contextSource.setUserDn(ldapUser);
contextSource.setPassword(ldapPassword);
contextSource.afterPropertiesSet();
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth
.ldapAuthentication().userDetailsContextMapper(userDetailsContextMapper());
ldapAuthenticationProviderConfigurer
.userSearchFilter("(&(criteria={0}))")
.userSearchBase("ou=usersearchbase").contextSource(contextSource);
}
@Bean
public LdapUserDetailsContextMapper userDetailsContextMapper() {
return new LdapUserDetailsContextMapper();
}
}
并在 LdapUserDetailsContextMapper
内用 @Autowired
注释您的字段。