我解决了这个问题,通过为用户提供一个状态值:
- status=-1 ; 初始登录
- status=0 ; 停用账户
- status=1 ; 启用账户
并在security.xml
中提供了2个自定义的身份验证控制器。第一个用于检查用户名和密码,第二个用于额外的控制,如初始登录、密码过期策略。
如果是第一次登录,并且提供了正确的用户名和密码,第一个控制器(user-service-ref="jdbcUserService"
)无法对用户进行身份验证(因为用户的status=-1
),然后第二个控制器(ref="myAuthenticationController"
)捕获请求,在该控制器中抛出DisabledException
异常。
最后,你可以在AuthenticationFailureListener
的onAuthenticationFailure
方法中将用户重定向到密码更改页面。
security.xml
的部分内容:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="jdbcUserService">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
<authentication-provider ref="myAuthenticationController" />
</authentication-manager>
<beans:bean id="jdbcUserService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="rolePrefix" value="ROLE_" />
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="usersByUsernameQuery" value="SELECT user_name as userName, PASSWORD as password, STATUS as status FROM USER WHERE user_name = ? AND STATUS=1" />
<beans:property name="authoritiesByUsernameQuery" value="SELECT user_name as userName, ROLE as authority FROM USER WHERE user_name = ?" />
</beans:bean>
<beans:bean id="myAuthenticationController" class="com.test.myAuthenticationController">
<beans:property name="adminUser" value="admin" />
<beans:property name="adminPassword" value="admin" />
</beans:bean>
<beans:bean id="authSuccessHandler" class="com.test.AuthenticationSuccessListener"/>
<beans:bean id="authFailureHandler" class="com.test.AuthenticationFailureListener"/>
@Service("myAuthenticationController")
public class MyAuthenticationController extends AbstractUserDetailsAuthenticationProvider {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private WfmUserValidator userValidator;
private String username;
private String password;
@Required
public void setAdminUser(String username) {
this.username = username;
}
@Required
public void setAdminPassword(String password) {
this.password = password;
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
return;
}
@Override
protected UserDetails retrieveUser(String userName, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
String password = (String) authentication.getCredentials();
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
String userRole = "";
if (status = -1) {
throw new DisabledException("It is first login. Password change is required!");
} else if (password expired) {
throw new CredentialsExpiredException("Password is expired. Please change it!");
}
return new User(userName, password, true,
true,
true,
true,
authorities);
}
}
public class AuthenticationFailureListener implements AuthenticationFailureHandler {
private static Logger logger = Logger.getLogger(AuthenticationFailureListener.class);
private static final String BAD_CREDENTIALS_MESSAGE = "bad_credentials_message";
private static final String CREDENTIALS_EXPIRED_MESSAGE = "credentials_expired_message";
private static final String DISABLED_MESSAGE = "disabled_message";
private static final String LOCKED_MESSAGE = "locked_message";
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse res, AuthenticationException ex) throws IOException, ServletException {
String userName = req.getParameter("j_username");
logger.info("[AuthenticationFailure]:" + " [Username]:" + userName + " [Error message]:" + ex.getMessage());
if (ex instanceof BadCredentialsException) {
res.sendRedirect("../pages/login.jsf?message=" + MessageFactory.getMessageValue(BAD_CREDENTIALS_MESSAGE));
} else if (ex instanceof CredentialsExpiredException) {
res.sendRedirect("../pages/changecredentials.jsf?message=" + MessageFactory.getMessageValue(CREDENTIALS_EXPIRED_MESSAGE));
} else if (ex instanceof DisabledException) {
res.sendRedirect("../pages/changecredentials.jsf?message=" + MessageFactory.getMessageValue(DISABLED_MESSAGE));
} else if (ex instanceof LockedException) {
res.sendRedirect("../pages/login.jsf?message=" + MessageFactory.getMessageValue(LOCKED_MESSAGE));
}
}
}