先说下背景,项目包含一个管理系统(web)和门户网站(web),还有一个手机APP(包括Android和IOS),三个系统共用一个后端,在后端使用shiro进行登录认证和权限控制。好的,那么问题来了:
先说web端
1.因为一般网页主需要记住7天密码(或者稍微更长)的功能就可以了,可以使用cookie实现,而且shiro也提供了记住密码的功能,在服务器端session不需要保存过长时间。
再说app端
2.因为APP免密码登录时间需要较长(在用户不主动退出的时候,应该一直保持登录状态),这样子在服务器端就得把session保存很长时间,给服务器内存和性能上造成较大的挑战,存在的矛盾是:APP需要较长时间的免密码登录,而服务器不能保存过长时间的session。
解决办法:
- APP第一次登录,使用用户名和密码,如果登录成功,将cookie保存在APP本地(比如sharepreference),后台将cookie值保存到user表里面
- APP访问服务器,APP将cookie添加在heade里面,服务器session依然存在,可以正常访问
- APP访问服务器,APP将cookie添加在heade里面,服务器session过期,访问失败,由APP自动带着保存在本地的cookie去服务器登录,服务器可以根据cookie和用户名进行登录,这样服务器又有session,会生成新的cookie返回给APP,APP更新本地cookie,又可以正常访问
- 用户手动退出APP,删除APP本次存储的cookie,下次登录使用用户名和密码登录
这种方法存在的问题:
- cookie保存在APP本地,安全性较低,可以通过加密cookie增加安全性
- 每次服务器session失效之后,得由APP再次发起登录请求(虽然用户是不知道的),但是这样本身就会增加访问次数,好在请求数量并不是很大,不过这种方式会使cookie经常更新,反而增加了安全性。
这里给出另外一种实现方式:
实现自己的SessionDao,将session保存在数据库,这样子的好处是,session不会大量堆积在内存中,就不需要考虑session的过期时间了,对于APP这种需要长期保存session的情况来说,就可以无限期的保存session了,也就不用APP在每次session过期之后重新发送登录请求了。实现方式如下:
2. 实体类
1. 数据库设计 (需要的话自己扩展)
import java.io.Serializable;
import java.util.Date;
/**
* 用户session
*
* @author flyingTiger
* @email gaofeihu95@163.com
* @date 2018-03-05 15:44:08
*/
public class SysUserSessionEntity implements Serializable {
private static final long serialVersionUID = 1L;
//id
private String id;
//session
private String session;
//cookie
private String cookie;
//user_id
private Long userId;
//创建时间
private Date createTime;
//最后更新时间
private Date lastUpTime;
//状态
private String status;
/**
* 设置:id
*/
public void setId(String id) {
this.id = id;
}
/**
* 获取:id
*/
public String getId() {
return id;
}
/**
* 设置:session
*/
public void setSession(String session) {
this.session = session;
}
/**
* 获取:session
*/
public String getSession() {
return session;
}
/**
* 设置:cookie
*/
public void setCookie(String cookie) {
this.cookie = cookie;
}
/**
* 获取:cookie
*/
public String getCookie() {
return cookie;
}
/**
* 设置:user_id
*/
public void setUserId(Long userId) {
this.userId = userId;
}
/**
* 获取:user_id
*/
public Long getUserId() {
return userId;
}
/**
* 设置:创建时间
*/
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
/**
* 获取:创建时间
*/
public Date getCreateTime() {
return createTime;
}
/**
* 设置:最后更新时间
*/
public void setLastUpTime(Date lastUpTime) {
this.lastUpTime = lastUpTime;
}
/**
* 获取:最后更新时间
*/
public Date getLastUpTime() {
return lastUpTime;
}
/**
* 设置:状态
*/
public void setStatus(String status) {
this.status = status;
}
/**
* 获取:状态
*/
public String getStatus() {
return status;
}
}
3. 在此之前我已经实现过sessionDao ,是将session放到redis里面
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
/**
* shiro session dao
*
* @author flyingTiger
* @email gaofeihu95@163.com
* @date 2017/9/27 21:35
*/
&