Spring Security 上下文设置身份验证对象不起作用
我有一个场景,我必须强制用户在首次登录时重置密码。为了 我正在使用自定义 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”,这样他就无法通过点击任何其他链接/url 来访问任何内容。
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;
}
现在我确实看到新的 UsernamePasswordAuthenticationToken 仅使用 RESET 密码角色创建。 但看起来在重定向到 ResetPasswordURl 时,Spring 过滤器做了一些检查,并且在我设置新的 UsernamePasswordAuthenticationToken 后,用户未经身份验证。
这是我在日志中看到的根本原因:
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)
有任何我出错的地方的评论吗?
I have a scenario where I have to force the users to reset password on first login. For
this I am using a custom successAuthenticationHandler.
All this handler trying to do is see if logged in user requires to reset password. If yes create a new UsernamePasswordAuthenticationToken and set it onto SecurityContext. And then redirect to resetPasswordUrl.
Here is my onAuthenticationSuccess method:
@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);
}
}
If yes create another UsernamePasswordAuthenticationToken with same credentials as logged in user, but just assign him a single role "RESET_PASSWORD", so that he cannot access anything alse by hitting any other link/url.
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;
}
Now I do see the new UsernamePasswordAuthenticationToken being created with just a RESET password role.
But looks like while doing redirect to the resetPasswordURl the spring filter do some checks and user is getting unauthenticated after I set my new UsernamePasswordAuthenticationToken .
Heres is the root cause that I see in the logs:
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)
Any comments where I am going wrong ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果没有日志中的更多上下文(堆栈跟踪的其余部分加上前面的日志消息),很难说,但我最好的猜测是您为
UsernamePasswordAuthenticationToken
使用了错误的构造函数。由于历史原因,它采用Object
参数,这没有帮助。两个参数的版本应该采用用户名和凭据,并创建一个未经身份验证的令牌(对于请求),从安全拦截器的角度来看,该令牌无效。所以我猜测拦截器正在尝试重新验证令牌(从调用来源的堆栈跟踪中应该很明显)并且它失败了,因为凭据参数实际上是权限列表而不是密码。
所以用:
代替。
此外,您还需要一个处理
RESET_PASSWORD
的自定义投票器,因为默认配置无法识别它。或者使用ROLE_
前缀,即ROLE_RESET_PASSWORD
。It's difficult to say without more context from the log (the rest of the stacktrace plus the preceding log messages), but my best guess is that you're using the wrong constructor for
UsernamePasswordAuthenticationToken
. For historical reasons it takesObject
arguments which doesn't help.The two-arg version is supposed to take the username and credentials, and creates an unauthenticated token (for a request) which is not valid from the security interceptor's perspective. So I'm guessing the interceptor is trying to reauthenticate the token (should be obvious from the stacktrace where the call is coming from) and it is failing because the credentials parameter is actually a list of authorities rather than a password.
So use:
instead.
Also, you will need to have a custom voter which handles
RESET_PASSWORD
as it will not be recognised by a default configuration. Alternatively use theROLE_
prefix, i.e.ROLE_RESET_PASSWORD
.