我已经能够启动并运行基于Spring Security的应用程序,直到现在它已满足我的所有要求。
我对如何在Spring Security中使用UserDetailsService
存在疑问。我有一个自定义的'UserDetailsService'实现,就像这样 -
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<UserDetailsDto> userDetailsByEmail = // Load userDetailsDto from database
if (!userDetailsByEmail.isPresent()) {
throw new UsernameNotFoundException("Username does not exists");
}
UserDetailsDto userDetailsDto = userDetailsByEmail.get();
List<Role> roles = roleService.listByEmail(username);
List<ModulePermission> modulePermissions = modulePermissionService.listByUserId(userDetailsDto.getId());
UserType userType = userTypeService.getByUserId(userDetailsDto.getId());
return new LoggedInUser(userDetailsDto, roles, modulePermissions, userType);
}
}
类LoggedInUser
是Spring Security的org.springframework.security.core.userdetails.User
类的扩展,就像这样 -
public class LoggedInUser extends User {
private static final long serialVersionUID = -1L;
private Long userId;
private boolean firstLogin;
private UserType userType;
private List<ModulePermission> modulePermissions;
private String firstName;
private String lastName;
private String contactNo;
public LoggedInUser(UserDetailsDto userDetailsDto, List<Role> roles, List<ModulePermission> modulePermissions,
UserType userType) {
super(userDetailsDto.getEmail(), userDetailsDto.getPassword(), userDetailsDto.getEnabledStatus().getValue(),
userDetailsDto.getAccountNonExpiredStatus().getValue(), true,
userDetailsDto.getAccountNonLockedStatus().getValue(),
roles.stream().map(role -> new SimpleGrantedAuthority(role.getId())).collect(Collectors.toList()));
this.modulePermissions = modulePermissions;
this.userType = userType;
this.userId = userDetailsDto.getId();
this.firstLogin = userDetailsDto.getIsFirstLoginStatus().getValue();
this.firstName = userDetailsDto.getFirstName();
this.lastName = userDetailsDto.getLastName();
this.contactNo = userDetailsDto.getContactNo();
}
public List<ModulePermission> getModulePermissions() {
return Collections.unmodifiableList(modulePermissions);
}
public UserType getUserType() {
return userType;
}
public Long getUserId() {
return userId;
}
public boolean isFirstLogin() {
return firstLogin;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getContactNo() {
return contactNo;
}
public void setFirstLogin(boolean firstLogin) {
this.firstLogin = firstLogin;
}
}
现在,要配置Spring Security以使用我的CustomUserDetailsService
,我在安全配置中执行以下操作 -
@Bean
public UserDetailsService customUserDetailsService() {
return new CustomUserDetailsService();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher(SuperAdminConstant.UrlConstant.ANT_MATCHER_PATH)
.userDetailsService(customUserDetailsService())
.formLogin(// further configuration)
}
这没有任何问题。
但请注意,在CustomUserDetailsService
中,即使在用户成功通过身份验证之前也会执行多个数据库查询(这是因为Spring Security已创建DaoAuthenticationProvider
,一个UserDetails
实现(在我的例子中,LoggedInUser
),并在从UserDetailsService
(在我的情况下,CustomUserDetailsService
)中检索到该对象后对该对象执行各种检查
考虑用户输入了正确的用户名,但密码错误。在这种情况下,高级认证流程将是 -
CustomUserDetailsService
将被称为UsernameNotFoundException
)DaoAuthenticationProvider
检查密码,发现密码错误,然后抛出BadCredentialsException
。可以看出,在认证过程完成之前执行了总共4个查询,其中只有1个在此阶段是必不可少的(第一个验证用户名的查询)。
此问题的一个解决方案可以是取消UserDetailsService
altogeather的使用,而是使用自定义AuthenticationProvider
。
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
Authentication authenticate(Authentication authentication)
throws AuthenticationException {
// Customize the authentication logic here, and retrieve
// user information only if everything is correct.
}
}
但是通过这种方法也意味着我必须复制DaoAuthenticationProvider
和AbstractUserDetailsAuthenticationProvider
提供的代码和功能,其中包括手动检查用户帐户状态标志(accountNonExpired,accountNonLocked等)并抛出异常。
所以我想知道天气可以执行身份验证逻辑,只有在身份验证成功后才能检索用户信息,并且可以重新使用Spring Security提供的大多数身份验证逻辑。
任何想法都将深受赞赏。
答案 0 :(得分:0)
您可以编写AuthenticationSuccessHandler
的实现:
@Component
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
LoggedInUser loggedInUser = (LoggedInUser)authentication.getPrincipal();
List<Role> roles = roleService.listByEmail(username);
List<ModulePermission> modulePermissions = modulePermissionService.listByUserId(userDetailsDto.getId());
UserType userType = userTypeService.getByUserId(userDetailsDto.getId());
// Set roles after authentication succeeds
loggedInUser.setRoles(roles);
loggedInUser.setModulePermissions(modulePermissions);
loggedInUser.setUserType(userType);
}
}
身份验证成功后,您可以从安全上下文中获取登录用户并设置其他属性。