7
回答
使用ehcache+Interceptor缓存登录问题
注册华为云得mate10,2.9折抢先购!>>>   

@JFinal 你好,想跟你请教个问题:

在使用ehcache缓存登录时遇到个问题。

在使用Interceptor拦截登录时,需要取得ehcache的key,我是希望将ssid作为key来使用,但在Interceptor中如何能够取到呀~

譬如说,登录成功后,生成个ssid,然后CacheKit.put(CommonConst.CACHE_USER_SESSION, ssid, user);

但在Interceptor中,如何判断是否有用户登录了呢?

希望您能给提供一个思路~万分感谢啦~

举报
小桜
发帖于2年前 7回/646阅
共有7个答案 最后回答: 1年前

   这个 ssid 建议生成  uuid,以防重复,然后将这个值在用户登录成功的时候放入 cookie中:setCookie("sessionId", uuid, maxAgeSenconds); 

   下面是 jfinal.com 官网的相关代码片段,希望对你有帮助:

public class LoginController extends Controller {
 @Before(LoginValidator.class)
 public void doLogin() {
  boolean keepLogin = getParaToBoolean("keepLogin", false);
  String loginIp = IpKit.getRealIp(getRequest());
  Ret ret = srv.login(getPara("userName"), getPara("password"), keepLogin, loginIp);
  if (ret.isOk()) {
   String sessionId = ret.get(LoginService.sessionIdName);
   int maxAgeInSeconds = ret.get("maxAgeInSeconds");
   setCookie(LoginService.sessionIdName, sessionId, maxAgeInSeconds, true);
   setAttr("loginAccount", ret.get("loginAccount"));
   // 如果returnUrl 存在则跳过去,否则跳去首页
   ret.put("returnUrl", getPara("returnUrl", "/")); 
  }
  renderJson(ret.getData());
 }
}
   上面是 LoginController 相关代码,其中的 Ret 对象是业务层返回的对象,部分片段如下:
/**
 * 登录成功返回 sessionId 与 loginAccount,否则返回一个 msg
 */
 public Ret login(String userName, String password, boolean keepLogin, String loginIp) {
 userName = userName.toLowerCase().trim();
 password = password.trim();
 Account loginAccount = accountDao.findFirst("select * from account where userName=? limit 1", userName);
 if (loginAccount == null) {
  return Ret.error("msg", "用户名或密码不正确");
 }
 if (loginAccount.isStatusLockId()) {
  return Ret.error("msg", "账号已被锁定");
 }
 if (loginAccount.isStatusReg()) {
  return Ret.error("msg", "账号未激活,请先激活账号");
 }

 String salt = loginAccount.getSalt();
 String hashedPass = HashKit.sha256(salt + password);
	
 // 未通过密码验证
 if (loginAccount.getPassword().equals(hashedPass) == false) {
  return Ret.error("msg", "用户名或密码不正确");
 }

 // 如果用户勾选保持登录,暂定过期时间为 3 年,否则为 120 分钟,单位为秒
 long liveSeconds =  keepLogin ? 3 * 365 * 24 * 60 * 60 : 120 * 60;
 // 传递给控制层的 cookie
 int maxAgeInSeconds = ((int)(keepLogin ? liveSeconds : -1));
 // expireAt 用于设置 session 的过期时间点,需要转换成毫秒
 long expireAt = System.currentTimeMillis() + (liveSeconds * 1000);
		
 String sessionId = UuidKit.generate();
 loginAccount.removeSensitiveInfo();
 loginAccount.put("sessionId", sessionId);
 CacheKit.put("loginAccount", sessionId, loginAccount);

 createLoginLog(loginAccount.getId(), loginIp);
 return Ret.ok(sessionIdName, sessionId)
	 .put("loginAccount", loginAccount)
	 .put("maxAgeInSeconds", maxAgeInSeconds);
}


  Ret 对象方便于将业务层的返回值传给控制层,而控制层又可以将其再传给view层,源码如下:

/**
 * 返回值封装,常用于业务层需要多个返回值
 */
public class Ret {

	private static final String STATUS_OK = "isOk";
	private static final String STATUS_ERROR = "isError";
	private Map<Object, Object> data = new HashMap<Object, Object>();

	public Ret() {

	}

	public static Ret ok() {
		return new Ret().setOk();
	}

	public static Ret ok(Object key, Object value) {
		return ok().put(key, value);
	}

	public static Ret error() {
		return new Ret().setError();
	}

	public static Ret error(Object key, Object value) {
		return error().put(key, value);
	}

	public static Ret create() {
		return new Ret();
	}

	public static Ret create(Object key, Object value) {
		return new Ret().put(key, value);
	}

	public Ret setOk() {
		data.put(STATUS_OK, Boolean.TRUE);
		data.put(STATUS_ERROR, Boolean.FALSE);
		return this;
	}

	public Ret setError() {
		data.put(STATUS_OK, Boolean.FALSE);
		data.put(STATUS_ERROR, Boolean.TRUE);
		return this;
	}

	public boolean isOk() {
		Boolean isOk = (Boolean)data.get(STATUS_OK);
		return isOk != null && isOk;
	}

	public boolean isError() {
		Boolean isError = (Boolean)data.get(STATUS_ERROR);
		return isError != null && isError;
	}

	public Ret put(Object key, Object value) {
		data.put(key, value);
		return this;
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	public Ret put(Map map) {
		this.data.putAll(map);
		return this;
	}

	public Ret put(Ret ret) {
		this.data.putAll(ret.data);
		return this;
	}

	@SuppressWarnings("unchecked")
	public <T> T get(Object key) {
		return (T)data.get(key);
	}

	public boolean isEmpty() {
		return data.isEmpty();
	}

	public int size() {
		return data.size();
	}

	public Ret clear() {
		data.clear();
		return this;
	}

	public boolean equals(Ret ret) {
		return ret != null && this.data.equals(ret.data);
	}

	/**
	 * key 存在,但 value 可能为 null
	 */
	public boolean containsKey(Object key) {
		return data.containsKey(key);
	}

	public boolean containsValue(Object value) {
		return data.containsValue(value);
	}

	/**
	 * key 存在,并且 value 不为 null
	 */
	public boolean notNull(Object key) {
		return data.get(key) != null;
	}

	/**
	 * key 不存在,或者 key 存在但 value 为null
	 */
	public boolean isNull(Object key) {
		return data.get(key) == null;
	}

	/**
	 * key 存在,并且 value 为 true,则返回 true
	 */
	public boolean isTrue(Object key) {
		Object value = data.get(key);
		return (value instanceof Boolean && ((Boolean)value == true));
	}

	/**
	 * key 存在,并且 value 为 false,则返回 true
	 */
	public boolean isFalse(Object key) {
		Object value = data.get(key);
		return (value instanceof Boolean && ((Boolean)value == false));
	}

	@SuppressWarnings("unchecked")
	public <T> T remove(Object key) {
		return (T)data.remove(key);
	}

	public Map<Object, Object> getData() {
		return data;
	}
}



   这里是通过拦截器获取 session中的 uuid,然后实现自动登录的代码:

/**
 * 从 cookie 中获取 sessionId,如果获取到则根据该值使用 LoginService
 * 得到登录的 Account 对象 ---> loginAccount,供后续的流程使用
 */
public class LoginSessionInterceptor implements Interceptor {
	
	public void intercept(Invocation inv) {
		Controller c = inv.getController();
		String sessionId = c.getCookie(LoginService.sessionIdName);
		if (sessionId != null) {
			Account loginAccount = LoginService.me.getLoginAccountWithSessionId(sessionId);
			if (loginAccount == null) {
				String loginIp = IpKit.getRealIp(c.getRequest());
				loginAccount = LoginService.me.loginWithSessionId(sessionId, loginIp);
			}
			if (loginAccount != null) {
				c.setAttr("loginAccount", loginAccount);
			} else {
				c.removeCookie(LoginService.sessionIdName); // cookie 登录未成功,证明该 cookie 已经没有用处,删之
			}
		}
		
		inv.invoke();
	}
}



--- 共有 1 条评论 ---
波总学习了. 2年前 回复

@JFinal 非常感谢您的解答~

如果使用cookie或者session的话,在非浏览器环境下,譬如说app下,就无法使用登录了吧~毕竟,app无法自动处理cookie或者session呢~

jfinal是否有方法能够取得当前连接用户(未登录)的唯一识别码呢~或者,在每个连接者的url后,自动加上uuid这种?是否可行呢?

--- 共有 2 条评论 ---
小桜回复 @JFinal : 非常感谢~最近有在试着实现模拟cookie或session。。。就是通过URL传递sessionID什么的 2年前 回复
JFinal不使用cookie的 app,你可以在交互时额外传递数据,模拟cookie机制即可,自动添加url这种也是可以的 2年前 回复
顶部