-
什么是单点登录,什么是单次登录
-
单点登录,集群环境中,在一台服务器登录了,相当于整个集群环境都登录了,可以分成有状态和无状态两种方式
-
单次登录,既一个账号只能登录一次,再次登录前面的那个被踢下线。单次登录有些变种,比如允许一类设备登录一次,手机登录QQ,电脑也能登录QQ,两个可以同事在线。原理类似。
-
-
有状态方式单点登录
-
首先明白一点,单机情况下,不存在单点登录问题,一般的session机制原理是通过 前端 带过来一个 sessionId,找到后端服务器的一个session 对象,以此识别用户。单点登录一般做法是吧session对象放到一个公共的储存区域,比如redis里面,用sessionId作为key开获取这session,实现集群环境的单点登录。
-
cookie + session 一般是 cookie里面有带有 sessionId,传递到后端识别到session。
-
cookie可能被禁用,禁用后可以通过url 重写的方式,在url地址中带上sessionId,后端以此识别session。
-
cookie+session机制一般有浏览器自动维护,简单省事。另外一种方式通过token +session的方式识别用户。
-
token+sessinId 的token里面有一个类似 sessionId的唯一标志,token由服务器端手动生成,token里面还可以带有一些其他信息,token可以做加密处理,防止里面的内容被窥视,也可以做签名处理,防止token的内容被修改。
-
服务器端拿到token后,验证token的合法性,取出里面的唯一标志,找到服务器端session对象,获取用户之前的状态。
-
因为token里面可以做加密签名的处理,所以token比sessionId可靠,因为它不是能随便生成的,也不能被随意更改。我们可以更加信赖token。所以服务器端session 可以不用长期有效,只要token合法并且没有过期,我们可以重新load 一份session对象,在要求登录时间比较久的情况下节省服务器资源。比如我们token设置过期时间一个月,session只保留30分钟,session过去了就重新load一份session对象。显然我们可以完全不用session,只用通过token里面里面的唯一标志取数据库数据,但是这种loadDB的方式,比放在内存里面的session慢,如果访问频率比较高,推荐token+session方式。
-
-
无状态的单点登录
-
http协议默认就是无状态的前面我们说到token是比sessionId更加可靠的一种方式。因为token可以签名加密,比明文纯数字的sessionId好很多。
-
我们完全可以吧用户认证,权限识别的内容做到token里面,检查token合法,并且里面有对应权限,我们就直接认为权限通过。如果还需要一些用户的零时可变信息我们可以通过token里面的唯一标志放到服务器端的一块缓存里面,这块缓存和session类似,或者你可以认为session就是一块用户信息缓存,token或者sessionId是拿这块缓存的钥匙。如果钥匙足够可靠,并且足够记录我们需要的信息只要钥匙也可以。
-
我们来思考一下用户名+密码的本质,用户名标志着资源,如果用户名不被泄漏,我们其实不需要密码。毕竟用户名和密码一样都可能被别人获取.假设我们只有用户名就能登录,然后用户名被人获取了,我们只能改变用户名,然后我们和服务器约定,我用户的前一半永远不变
-
考虑一下登录,我们一般需要用户名+ 密码,如果这个用户名足够复杂,不容易被猜到,我们认为他是安全的,比如比特币的私钥,有了私钥就拥有了对应账户里面的余额,并且这个私钥可以通过算法产生,只要你随机产生的私钥能和有余额的私钥一样,那么这个账户里面的钱就都是你的了。
-
如果你的账号不够复杂,需要有改动的情况我们认为只要账号不要密码也是安全的,两个系统之间通信如果都是公司内部系统,一个我们通过一个密钥来加密解密数据,保证交互的安全是可行的,如果泄露,两边同时更换就行了。
-
如果不是公司两个系统之间的通信,比如你和游戏公司,如果只有一个账号,那么要改动的时候,需要你和游戏公司同时改,明显游戏公司不认识你,所以这时候吧账号的一半固定,让游戏公司认识你,另外一半变化可以定期修改,这就是密码,这样一个过程就是账号密码存在的意义。和什么情况下只用一个账号也是安全的。
-
-
shiro单次登录实现
Map<String, Subject> subjectHashMap = new HashMap<>(); public void clearLogin(String userName){ Subject subject = subjectHashMap.get(userName); if( subject != null && !SecurityUtils.getSubject().getSession().getId().equals( subject.getSession().getId() ) ){ System.out.println("清除session。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。"); //securityManager.logout(subject); subject.logout(); } subjectHashMap.put(userName, SecurityUtils.getSubject() ); }
-
如果是集群环境,session是在类似redis的地方,除了记录 sessinId->session的对应关系,还应该记录userName->sessionId的关系,这样一个用户登录的时候把上一次登录的 sessionId找出来,然后这个sessionId对应的session删除掉,前面用这个账号登录的就被踢下线了。
-
userName->sessionId的关系也可以变为userName->[登录类型:sessionId,登录类型2:sessionId2],这样就可以多设备登录了。