/**
* Copyright (c) 2011-2012, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.token;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import com.jfinal.core.Const;
import com.jfinal.core.Controller;
import com.jfinal.util.StringKit;
/**
* TokenManager.
*/
public class TokenManager {
private static ITokenCache tokenCache;
private static Random random = new Random();
private TokenManager() {
}
public static void init(ITokenCache tokenCache) {
if (tokenCache == null)
return;
TokenManager.tokenCache = tokenCache;
long halfTimeOut = Const.MIN_SECONDS_OF_TOKEN_TIME_OUT * 1000 / 2; // Token最小过期时间的一半时间作为任务运行的间隔时间
new Timer().schedule(new TimerTask() {public void run() {removeTimeOutToken();}},
halfTimeOut,
halfTimeOut);
}
/**
* Create Token.
* @param Controller
* @param tokenName token name
* @param secondsOfTimeOut seconds of time out, for ITokenCache only.
*/
public static void createToken(Controller controller, String tokenName, int secondsOfTimeOut) {
if (tokenCache == null) {
String tokenId = String.valueOf(random.nextLong());
controller.setAttr(tokenName, tokenId);
controller.setSessionAttr(tokenName, tokenId);
createTokenHiddenField(controller, tokenName, tokenId);
}
else {
createTokenUseTokenIdGenerator(controller, tokenName, secondsOfTimeOut);
}
}
/**
* Use ${token!} in view for generate hidden input field.
*/
private static void createTokenHiddenField(Controller controller, String tokenName, String tokenId) {
StringBuilder sb = new StringBuilder();
sb.append("<input type='hidden' name='").append(tokenName).append("' value='" + tokenId).append("' />");
controller.setAttr("token", sb.toString());
}
private static void createTokenUseTokenIdGenerator(Controller controller, String tokenName, int secondsOfTimeOut) {
if (secondsOfTimeOut < Const.MIN_SECONDS_OF_TOKEN_TIME_OUT)
secondsOfTimeOut = Const.MIN_SECONDS_OF_TOKEN_TIME_OUT;
String tokenId = null;
Token token = null;
int safeCounter = 8;
do {
if (safeCounter-- == 0)
throw new RuntimeException("Can not create tokenId.");
tokenId = String.valueOf(random.nextLong());
token = new Token(tokenId, System.currentTimeMillis() + (secondsOfTimeOut * 1000));
} while(tokenId == null || tokenCache.contains(token));
controller.setAttr(tokenName, tokenId);
tokenCache.put(token);
createTokenHiddenField(controller, tokenName, tokenId);
}
/**
* Check token to prevent resubmit.
* @param tokenName the token name used in view's form
* @return true if token is correct
*/
public static synchronized boolean validateToken(Controller controller, String tokenName) {
String clientTokenId = controller.getPara(tokenName);
if (tokenCache == null) {
String serverTokenId = controller.getSessionAttr(tokenName);
controller.removeSessionAttr(tokenName); // important!
return StringKit.notBlank(clientTokenId) && clientTokenId.equals(serverTokenId);
}
else {
Token token = new Token(clientTokenId);
boolean result = tokenCache.contains(token);
tokenCache.remove(token);
return result;
}
}
private static void removeTimeOutToken() {
List<Token> tokenInCache = tokenCache.getAll();
if (tokenInCache == null)
return;
List<Token> timeOutTokens = new ArrayList<Token>();
long currentTime = System.currentTimeMillis();
// find and save all time out tokens
for (Token token : tokenInCache)
if (token.getExpirationTime() <= currentTime)
timeOutTokens.add(token);
// remove all time out tokens
for (Token token : timeOutTokens)
tokenCache.remove(token);
}
}