package com.norteksoft.cas.web;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import javax.servlet.http.HttpServletRequest;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.jasig.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.binding.message.DefaultMessageContext;
import org.springframework.binding.message.Message;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.webflow.execution.RequestContext;
import cbm.norteksoft.utilSecret.java.License;
import com.norteksoft.cas.service.LoginSettngService;
import com.octo.captcha.service.CaptchaServiceException;
import com.octo.captcha.service.image.ImageCaptchaService;
public class VerificationCodeValidator {
protected final Logger log = LoggerFactory.getLogger(VerificationCodeValidator.class);
// private static final long HALF_HOUR = 30*60*1000L;
private static final String ERROR_CODE = "error.authentication.verification.code.bad";
private static final String USER_NOT_FOUND = "error.authentication.username.not.found";
private static final String LOCKED_CODE = "error.authentication.user.locked";
private static final String LICENCE_INVALIDATION_CODE = "error.authentication.licence.invalidation";
public VerificationCodeValidator(){
super();
timeoutTimer();
}
// private Map<String, Integer> failedCountMap = new HashMap<String, Integer>();
// private Map<String, Long> failedTimeMap = new HashMap<String, Long>();
private Map<String, LoginInfo> loginInfos = new HashMap<String, LoginInfo>();
private ImageCaptchaService jcaptchaService;
private LoginSettngService loginSettngService;
private String validationParameter = "_captcha_parameter";
public String validate(final RequestContext context, final Credentials credentials, final MessageContext messageContext){
String username = ((UsernamePasswordCredentials)credentials).getUsername();
Long companyId = loginSettngService.getCompanyId(username);
log.debug("user company id is : ["+companyId+"]");
if(companyId == null){
addErrorMsg(messageContext, USER_NOT_FOUND);
return "error";
}
// License
// try {
// Map licence = License.getLicense();
// String dateString = (String)licence.get("end_time");
// SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// Date settingDate = df.parse(dateString);
// Date date = loginSettngService.getLastTime(companyId);
// if(date != null && settingDate.before(date)){
// log.debug("licence validate failed. ");
// addErrorMsg(messageContext, LICENCE_INVALIDATION_CODE);
// return "error";
// }
// } catch(Exception e) {
// log.debug("validate licence error. ", e);
// addErrorMsg(messageContext, LICENCE_INVALIDATION_CODE);
// return "error";
// }
// End License
Boolean userLocked = loginSettngService.isUserLocked(username);
LoginInfo info = loginInfos.get(username);
if(userLocked == null){ // 用户名不存在
addErrorMsg(messageContext, USER_NOT_FOUND);
return "error";
}else if(userLocked){ // 数据库用户已锁定
if(info != null && !info.isLocked()){ // 已过解锁时间
info = null;
loginSettngService.unlockUser(username);
loginInfos.remove(username);
}else{
addErrorMsg(messageContext, LOCKED_CODE);
return "error";
}
}else if(info != null && info.isLocked()){ // 已手动解锁
info = null;
loginSettngService.unlockUser(username);
loginInfos.remove(username);
}
if(info != null && info.showVerificationCode()){
if(compareCaptcha(context)){
return "success";
}
addErrorMsg(messageContext, ERROR_CODE);
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
request.setAttribute("showCode", true);
return "error";
}
return "success";
}
private void addErrorMsg(MessageContext messageContext, String code){
messageContext.addMessage(new MessageBuilder().error().code(code).defaultText(code).build());
}
public void success(final RequestContext context, final Credentials credentials){
String username = ((UsernamePasswordCredentials)credentials).getUsername();
loginInfos.remove(username);
// 记录登录日志
Long companyId = loginSettngService.getCompanyId(username);
Boolean userEnabled = loginSettngService.getUserEnabled(username);
if(userEnabled){
loginSettngService.loginLog(companyId, username, WebUtils.getHttpServletRequest(context).getRemoteHost());
}
}
public void countFailed(final RequestContext context,
final Credentials credentials, final MessageContext messageContext){
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
Message[] msgs = messageContext.getAllMessages();
if(msgs.length > 0){
Message credentialsBad = getMsg(messageContext, "error.authentication.credentials.bad");
if(credentialsBad != null && credentialsBad.getText().equals(msgs[0].getText())){ // 密码错误
String username = ((UsernamePasswordCredentials)credentials).getUsername();
LoginInfo info = loginInfos.get(username);
if(info == null){ // 第一次登陆失败
info = createLoginInfo(username);
}else{ // 多次失败
info.failedCount = info.failedCount+1;
}
loginInfos.put(username, info);
if(info.failedCount >= info.count){ // 达到失败次数
if(info.afterFailed == 0){ // 显示验证码
request.setAttribute("showCode", true);
info.startTime = System.currentTimeMillis();
}else if(info.afterFailed == 1){ // 锁定用户
info.startTime = System.currentTimeMillis();
messageContext.clearMessages();
messageContext.addMessage(new MessageBuilder().error().code(LOCKED_CODE).defaultText(LOCKED_CODE).build());
//通知数据库锁定用户
loginSettngService.lockUser(username);
}
}
}
}
}
private void timeoutTimer(){
Timer timer = new Timer("login-info-timer", true);
TimerTask task = new TimerTask(){
public void run() {
try {
timeoutLoginInfo();
} catch (Exception e) {
log.debug("validate login info time out error. ", e);
}
}};
timer.schedule(task, 30*1000l, 30*1000l);
}
// 定时操作,如果是锁定的,等到锁定时间过了后移除,如果是出验证码的,等待五分钟过后移除信息
private void timeoutLoginInfo(){
log.debug(" start validate login info time out ...");
for(Map.Entry<String, LoginInfo> li : loginInfos.entrySet()){
if(li.getValue().isTimeout()){
loginInfos.remove(li.getKey());
}
}
}
private LoginInfo createLoginInfo(String username){
Map<String, Object> map = loginSettngService.getSecuritySetting(username);
LoginInfo info = new LoginInfo(username);
if(map!=null && !map.isEmpty()){
info.count = Integer.parseInt(map.get("value").toString());
info.afterFailed = Integer.parseInt(map.get("fail_set_type").toString());
info.lockTime = Long.parseLong(map.get("locked_time").toString())*60*1000L;
}
return info;
}
private Message getMsg(MessageContext messageContext, String code){
MessageSource ms = ((DefaultMessageContext)messageContext).getMessageSource();
Message msg = new MessageBuilder().error().code(code)
.defaultText(code).build().resolveMessage(
ms, LocaleContextHolder.getLocale());
return msg;
}
public boolean compareCaptcha(RequestContext context){
String captchaResponse = context.getRequestParameters().get(validationParameter);
if(captchaResponse != null){
String id = WebUtils.getHttpServletRequest(context).getSession().getId();
if(id != null){
try {
return jcaptchaService.validateResponseForID(id,captchaResponse).booleanValue();
} catch (CaptchaServiceException cse) { }
}
}
return false;
}
public void setJcaptchaService(ImageCaptchaService jcaptchaService) {
this.jcaptchaService = jcaptchaService;
}
public void setValidationParameter(String validationParameter) {
this.validationParameter = validationParameter;
}
public void setLoginSettngService(LoginSettngService loginSettngService) {
this.loginSettngService = loginSettngService;
}
static class LoginInfo{
int afterFailed = 0; // 失败后 处理类型
long startTime = System.currentTimeMillis(); // 第一次失败时间
String username; // 用户名
int count = 3;
int failedCount = 1; // 已失败次数
long lockTime = 30*60*1000L; // 锁定时间
LoginInfo(String username){
this.username = username;
}
boolean showVerificationCode(){ // 是否显示验证码
return afterFailed==0 && failedCount>=count;
}
boolean isLocked(){ // 是否锁定
return afterFailed==1 && failedCount>=count && (System.currentTimeMillis() - startTime - lockTime) < 0;
}
boolean isLockTimeout(){ // 已过锁定时间
return (System.currentTimeMillis() - startTime - lockTime) > 0;
}
boolean isShowCodeTimeout(){ // 已过显示验证码时间,默认为 5 分钟
return (System.currentTimeMillis() - startTime - 5*60000l) > 0;
}
boolean isTimeout(){
if(afterFailed == 0){
return isShowCodeTimeout();
}
//else if(afterFailed == 1){
// return isLockTimeout();
//}
return false;
}
}
}