restfull加JWT TOKEN进行登录挤掉上一次的登录(为什么不用springsecurity的session)

方案1使用springSecurity中的通过springSecurity的用户帐号和sessionid来判断sessionid是否是同一个浏览器进行数据的访问,而达到访问报错。

a) 但是,我测试过,出现一个错误,每一次请求的sessionId都不一样?是不是前端的cookie给禁用了。导致sessionid返不回服务器。是每一次的请求的sessionid都不一样。

原因是:restful是无状态的

参考文章:

http://blog.csdn.net/Jmilk/article/details/50461577

目录

· 目录

· 前言

· 无状态原则

· Web服务的状态

· 基于状态的Web服务

· 基于无状态的Web服务

· 总结两者的区别

前言

在上篇RESTful基础知识中整体的介绍了RESTful架构设计思想的框架,在往后的RESTful主题博文中,我们在这个框架的基础上不断的为其填充更加深入的知识材料。 
RESTful基础知识,传送门:http://blog.csdn.net/jmilk/article/details/50452595

无状态原则

Statelessness:无状态原则是RESTful架构设计中一个非常重要的原则,无状态是相对于有状态而言的。在理解什么是无状态的交互请求之前,首先我们需要了解什么是有状态,并对两者进行比较以加深认识。

Web服务的状态

Web服务建立在Web应用程序的协议之上,如:HTTP协议。Web服务的状态指的是请求的状态,而不是资源的状态。是两个关联用户(Client与Server)进行交互操作时所留下来的公共信息(工作流、用户状态信息等数据)。这些信息可以被指定在不同的作用域中,如:page、request、session、application或全局作用域,一般由Server中的Session来保存这些信息。

基于状态的Web服务

在基于状态的Web服务中,Client与Server交互的信息(如:用户登录状态)会保存在Server的Session中。再这样的前提下,Client中的用户请求只能被保存有此用户相关状态信息的服务器所接受和理解,这也就意味着在基于状态的Web系统中的Server无法对用户请求进行负载均衡等自由的调度(一个Client请求只能由一个指定的Server处理)。同时这也会导致另外一个容错性的问题,如果指定的Server在Client的用户发出请求的过程中宕机,那么此用户最近的所有交互操作将无法被转移至别的Server上,即此请求将无效化。

基于无状态的Web服务

在无状态的Web服务中,每一个Web请求都必须是独立的,请求之间是完全分离的。Server没有保存Client的状态信息,所以Client发送的请求必须包含有能够让服务器理解请求的全部信息,包括自己的状态信息。使得一个Client的Web请求能够被任何可用的Server应答,从而将Web系统扩展到大量的Client中。

总结两者的区别

因为无状态原则的特性,让RESTful在分布式系统中得到了广泛的应用,它改善了分布式系统的可见性、可靠性以及可伸缩性,同时有效的降低了Client与Server之间的交互延迟。无状态的请求有利于实现负载均衡,在分布式web系统下,有多个可的Server,每个Server都可以处理Client发送的请求。有状态的请求的状态信息只保存在第一次接收请求的Server上,所以后来同一个Client的请求都只能由这台Server来处理,Server无法自由调度请求。无状态请求则完全没有这个限制。其次,无状态请求有较强的容错性和可伸缩性。如果一台服务器宕机,无状态请求可以透明地交由另一台可用Server来处理,而有状态的请求则会因为存储请求状态信息的Server宕机而承担状态丢失的风险。Restful风格的无状态约束要求Server不保存请求状态,如果确实需要维持用户状态,也应由Client负责。例如: 
使用Cookies通过客户端保持登陆状态 
REST中,每一个对象都是通过URL来表示,对象用户负责将状态信息打包进每一条信息内,保证对象的处理总是无状态的。在HTTP服务器中,服务器没有保存客户端的状态信息,客户端必须每次都带上自己的状态去请求服务器。客户端以URL形式提交的请求包含了cookies等带状态的数据,这些数据完全指定了所需的登录信息,而不需要其他请求的上下文或内存。 
传递User credentials是Restful,而传递SessionID是Un-Restful的,因为session信息保存在服务器端。 
无状态请求
Server不保存任何请求状态信息,Client的每一个请求都具有User credentials等所需要的全部信息,所以能被任意可用的Server应答。 
有状态请求Server保存了Client的请求状态,Server会通过Client传递的SessionID在Server中的Session作用域找到之前交互的信息,并以此来实现应答。所以Client只能由某一个Server来应答。

 

 

方案2:我的思路(简单实用)

1. 使用jwt中的token来挤掉前一个用户

2. 首先创建一个服务器域变量的map集合 static

package com.ewe.user.utils;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import com.ewe.core.utils.TokenUtil;


public class TokenCacheUtil { 
    private static Map cacheMap = new HashMap();
 
    //单实例构造方法 
    private TokenCacheUtil() { 
        super(); 
    }  
    /**
     * 根据键获取时间long
     * @param key
     * @return
     */
    public static Long getServerStartdt(String key){ 
        return (Long)cacheMap.get(key); 
    } 
    /**
     * 设置布尔值的缓存 
     * @param key
     * @param date
     * @return
     */
    public synchronized static Map setSimpleFlag(String key,Long date){ 
            cacheMap.put(key, date); 
            return cacheMap;
    } 
   

 
    /**
     * 判断是否存在一个缓存 
     * @param key
     * @return
     */
    public synchronized static boolean hasCache(String key) { 
        return cacheMap.containsKey(key); 
    } 
 
    /**
     * 清除所有缓存 
     */
    public synchronized static void clearAll() { 
        cacheMap.clear(); 
    } 
 
    /**
     * 清除指定的缓存 
     * @param key
     */
    public synchronized static void clearOnly(String key) { 
        cacheMap.remove(key); 
    } 
 
    /**
     * 获取缓存中的大小 
     * @return
     */
    public static int getCacheSize() { 
        return cacheMap.size(); 
    }
    /**
     * 根据最新的token通过用户名相同删除之前的token
     * @param token
     */
	public static void existByUserName(String token,Long expire) {
		String nowUserName=TokenUtil.getUserName(token);
        Iterator> it = cacheMap.entrySet().iterator();  
        while(it.hasNext()){  
            Entry entry=it.next();  
            String key=entry.getKey().toString();  
            String value=entry.getValue().toString();  
            if(nowUserName.equalsIgnoreCase(TokenUtil.getUserName(key))){
            	 it.remove();
            }
            // 顺路可以把过期的也移除下
            if((new Date()).getTime()-(Long.parseLong(value))>=expire){
            	 it.remove();
            }
        }
	} 
	// 过滤当前时间如果过期了就移除
	public static void deleteTokenForExpired(Long time) {
		// TODO Auto-generated method stub
		Iterator> it = cacheMap.entrySet().iterator();  
        while(it.hasNext()){  
            Entry entry=it.next();  
            String value=entry.getValue().toString();  
            // 顺路可以把过期的也移除下
            if((new Date()).getTime()-(Long.parseLong(value))>=time){
            	 it.remove();
            }
        }
	}
} 

3. 在登陆的时候,

a) map集合不允许相同的键、

b) 遍历map集合的token ==》解析token 中的用户名是否是当前登陆的用户名,如果存在,将当前的token的值设置成false并移除当前的token

    @RequestMapping(value="/login",method=RequestMethod.POST)
    public ReturnDtoInfo login(@RequestBody Users loginUser,HttpServletRequest request)throws Exception{
        LOGGER.info("Login");
        // loginUser.setUsername(loginUser.getUsername().toLowerCase());
        activUserService.checkStatus(loginUser.getId(),loginUser.getUsername());
        ReturnDtoInfo info = usersevice.login(loginUser);
        // Token type?
        if(info instanceof Token){
        	Token token = (Token) info;
        	// 根据当前的token判断用户名是否有相同的token
        	// 获取当前超时的long时间
        	Long time = expire * 60 *1000l;
        	TokenCacheUtil.existByUserName(token.getToken(),time);
        	// 将token放到缓存的数据之中
        	TokenCacheUtil.setSimpleFlag(token.getToken(), new Date().getTime());
        }
        return info;
    }

c) 把键:新的token  :true存到map集合中

4. 在每次请求数据的过滤器中做如下操作

     

a) 拿到token

遍历查询到token,根据键token得到值bool

如果token不存在map中就提示重新登陆。

 if(!TokenCacheUtil.hasCache(token)){
	     throw new CustomException("Expired or invalid JWT token", HttpStatus.UNAUTHORIZED);
      }
5.注销的时候一处内存中的token

6. 提高遍历效率

使用任务定时器定期清理map集合中令牌已经过期的token

防止用户非法关闭浏览器获取不注销

此方法跟服务器重启无关,只要有登录,并且根据令牌的有效期存在map中。


 思路大概就上面这些了

 

你可能感兴趣的:(springboot,Restful,springSecurity)