spring-boot - Spring Boot:如何告诉客户端帐户密码已过期?
问题描述
我在控制器中有一个简单的登录表单和一个 login() 方法:
@PostMapping("/login")
public ResponseEntity<UserVO> login(@RequestBody UserVO userVO) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
userVO.getUsername(),
userVO.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication( authentication );
// ...
return ResponseEntity.ok( loggedInUser );
}
我有一个包含列的用户表:
`username`
`password`
`password_expiration`
`account_expiration`
`account_locked`
`account_disabled`
当我将 password_expiration 列的值设置为使用户密码过期时,Spring Boot 的authenticationManager.authenticate()方法在下次登录尝试时抛出AccountExpiredException异常:
package org.springframework.security.authentication;
public class AccountExpiredException extends AccountStatusException {
public AccountExpiredException(String msg) {
super(msg);
}
public AccountExpiredException(String msg, Throwable t) {
super(msg, t);
}
}
JSON响应是:
{
"timestamp":"2018-07-26T22:53:05.392+0000",
"status":401,
"error":"Unauthorized",
"message":"Unauthorized",
"path":"/login"
}
每当密码错误或 UserVO 的方法之一(反过来实现 UserDetails)返回 false 时,我都会得到相同的 JSON 响应(401 错误代码):
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
到目前为止,一切都很好。
当用户登录并且他/她的密码过期时,我想将 UI 重定向到强制密码更改页面。但是如何?我总是得到相同的 JSON 响应。
1) 由于返回的 JSON 输出始终是 HTTP 401 错误,我怎样才能获得更细粒度的响应?(如何告诉客户端代码密码已过期?)
2) 通知用户他/她的帐户被锁定/过期/禁用通常被认为是好的还是坏的做法?(良好的用户体验与向黑客泄露有关帐户状态的信息)
解决方案
也许不是最好的解决方案,但我通过设置“消息”字段解决了它:
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
String message = "Unauthorized";
if ( authException instanceof BadCredentialsException )
message = AUTH_CREDENTIALS_BAD;
else if ( authException instanceof CredentialsExpiredException )
message = AUTH_CREDENTIALS_EXPIRED;
else if ( authException instanceof LockedException )
message = AUTH_ACCOUNT_LOCKED;
else if ( authException instanceof DisabledException )
message = AUTH_ACCOUNT_DISABLED;
else if ( authException instanceof AccountExpiredException )
message = AUTH_ACCOUNT_EXPIRED;
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, message );
}
}
现在我得到这个作为回应:
{
"timestamp":"2018-07-27T12:54:53.097+0000",
"status":401,
"error":"Unauthorized",
"message":"credentials_expired",
"path":"/login"
}
推荐阅读
- flutter - Flutter:如何为按下的每个网格项导航到另一个屏幕
- amazon-web-services - 我们可以在部署到 Elastic Beanstalk 时指定 Dockerfile 路径吗?
- c# - 如何将射击分配给特定键?
- php - Laravel 在哪里或哪里
- javascript - 在页面之间发送值反应原生钩子
- html - Dompdf中表格垂直对齐中间的问题
- reactjs - 子组件中明确存在的上下文属性未定义
- c# - 为什么我不能在 input:name 中使用 @ 并在后面使用 value
- python - 在 react.js 中通过 axios 发出 POST 请求后没有得到响应
- django - 是否可以在 django 运行任何迁移命令之前创建数据库?