我有一个场景,我必须强制用户在首次登录时重置密码。对于 这我正在使用自定义successAuthenticationHandler。 所有这个尝试做的处理程序是查看登录用户是否需要重置密码。如果是,则创建一个新的UsernamePasswordAuthenticationToken并将其设置为SecurityContext。然后重定向到resetPasswordUrl。
这是我的onAuthenticationSuccess方法:
@Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response,
final Authentication authentication) throws ServletException, IOException {
final AugmentedUser aUser = (AugmentedUser) SecurityContextHolder.getContext().getAuthentication()
.getPrincipal();
System.out.println("In password reset handler.");
if (authorizationService.isPasswordResetRequired(aUser.getUsername(), aUser.getUsertype())) {
LOG.debug("Password reset is required.");
System.out.println("Password reset is required");
final UsernamePasswordAuthenticationToken authRequest = reAssignUserWithOnlyResetPasswordRole(aUser,
request);
SecurityContextHolder.getContext().setAuthentication(authRequest);
SecurityContextHolder.getContext().getAuthentication();
System.out.println("User reassinged with only RESET_PASSWORD Authority, redirecting to resetPasswordPage");
response.sendRedirect(resetPasswordUrl);
//super.onAuthenticationSuccess(request, response, newAuthentication);
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
}
如果是,则使用与登录用户相同的凭据创建另一个UsernamePasswordAuthenticationToken,但只为他分配一个角色“RESET_PASSWORD”,这样他就无法通过点击任何其他链接/网址来访问任何内容。
private UsernamePasswordAuthenticationToken reAssignUserWithOnlyResetPasswordRole(final AugmentedUser aUser,
final HttpServletRequest request) {
final String username = aUser.getUsername();
final String password = aUser.getPassword();
final boolean isEnabled = aUser.isEnabled();
final boolean isAccountNonExpired = aUser.isAccountNonExpired();
final boolean isCredentialsNonExpired = aUser.isCredentialsNonExpired();
final boolean isAccountNonLocked = aUser.isAccountNonLocked();
LOG.debug("Re-assigning the user: " + username + " with only RESET PASSWORD AUTHORITY");
System.out.println("Re-assigning the user: " + username + "with only RESET PASSWORD AUTHORITY");
final Map<String, String> userAttributesMap = new HashMap<String, String>();
final AugmentedUser userWithResetPasswordRole = new AugmentedUser(username, password, aUser.getUsertype(),
isEnabled, isAccountNonExpired, isCredentialsNonExpired, isAccountNonLocked,
createResetPasswordGrantedAuhtority(), userAttributesMap);
final UsernamePasswordAuthenticationToken authenticationRequest = new UsernamePasswordAuthenticationToken(
userWithResetPasswordRole, userWithResetPasswordRole.getAuthorities());
//WebAuthenticationDetails are required for sessionId and ipAddress
final WebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request);
authenticationRequest.setDetails(webAuthenticationDetails);
return authenticationRequest;
}
现在我确实看到只使用RESET密码角色创建了新的UsernamePasswordAuthenticationToken。 但看起来在重定向到resetPasswordURl时,spring过滤器会进行一些检查,并且在我设置新的UsernamePasswordAuthenticationToken后,用户将得到未经身份验证。
Heres是我在日志中看到的根本原因:
doAuthentication - Authentication attempt using
org.springframework.security.authentication.dao.DaoAuthenticationProvider
2012-02-15 22:47:20,931 [http-8081-6] DEBUG org.springframework.security.web.access.ExceptionTranslationFilter org.springframework.security.web.access.ExceptionTranslationFilter handleException - Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.authentication.BadCredentialsException: Bad credentials
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:67)
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:139)
at org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:120)
我出错的任何评论?
答案 0 :(得分:1)
如果没有来自日志的更多上下文(堆栈跟踪的其余部分加上前面的日志消息),很难说,但我最好的猜测是你为{{1}使用了错误的构造函数}。由于历史原因,它需要UsernamePasswordAuthenticationToken
个参数无效。
双arg版本应该使用用户名和凭据,并创建一个未经身份验证的令牌(对于请求),从安全拦截器的角度来看,这是无效的。所以我猜测拦截器正在尝试重新验证令牌(应该从调用来自的堆栈跟踪中显而易见)并且它失败了因为凭证参数实际上是权限列表而不是密码。
所以使用:
Object
代替。
此外,您需要有一个处理new UsernamePasswordAuthenticationToken(
userWithResetPasswordRole, null, userWithResetPasswordRole.getAuthorities());
的自定义选民,因为默认配置无法识别它。或者,使用RESET_PASSWORD
前缀,即ROLE_
。