对于初学者来说,对Token和Session的使用难免会限于困境,开发过程中知道有这个东西,但却不知道为什么要用他?更不知道其原理,今天我就带大家一起分析分析这东西。
一、我们先解释一下他的含义:
1、Token的引入
Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。
2、Token的定义
Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
3、使用Token的目的
Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
了解了Token的意义后,我们就更明确的知道为什么要用他了。
二、如何使用Token?
这是本文的重点,在这里我就介绍常用的两种方式。
1、用设备号/设备mac地址作为Token(推荐)
客户端:客户端在登录的时候获取设备的设备号/mac地址,并将其作为参数传递到服务端。
服务端:服务端接收到该参数后,便用一个变量来接收同时将其作为Token保存在数据库,并将该Token设置到session中,客户端每次请求的时候都要统一拦截,并将客户端传递的token和服务器端session中的token进行对比,如果相同则放行,不同则拒绝。
分析:此刻客户端和服务器端就统一了一个唯一的标识Token,而且保证了每一个设备拥有了一个唯一的会话。该方法的缺点是客户端需要带设备号/mac地址作为参数传递,而且服务器端还需要保存;优点是客户端不需重新登录,只要登录一次以后一直可以使用,至于超时的问题是有服务器这边来处理,如何处理?若服务器的Token超时后,服务器只需将客户端传递的Token向数据库中查询,同时并赋值给变量Token,如此,Token的超时又重新计时。
2、用session值作为Token
客户端:客户端只需携带用户名和密码登陆即可。
客户端:客户端接收到用户名和密码后并判断,如果正确了就将本地获取sessionID作为Token返回给客户端,客户端以后只需带上请求数据即可。
分析:这种方式使用的好处是方便,不用存储数据,但是缺点就是当session过期后,客户端必须重新登录才能进行访问数据。
三、使用过程中出现的问题以及解决方案?
刚才我们轻松介绍了Token的两种使用方式,但是在使用过程中我们还出现各种问题,Token第一种方法中我们隐藏了一个在网络不好或者并发请求时会导致多次重复提交数据的问题。
该问题的解决方案:将session和Token套用,如此便可解决,如何套用呢?请看这段解释:
session是一个在单人操作人员整个操作过程中,与服务器保持通信的唯一识别信息。在同一操作人员的多次请求中,session的实例始终保持不变,而不是每次请求生成不同的实例对象(request是在每次请求生成不同的实例对象),因为可以对其加锁(???)。当同一操作人员多个请求进入时,可以通过session限制只能单向通行。(???)
本文正是通过使用session以及在session中加入token,来验证同一个操作人员是否进行了并发重复的请求,在后一个请求到来时,使用session中的token验证请求中的token是否一致,当不一致时,认为是重复提交,将不准许接下来的业务访问。
这就是解决重复提交的方案。另外点这里了解使用Session防止表单重复提交
四、实现思路
1.用户登录校验,校验成功后就返回Token给客户端。
2.客户端收到数据后保存在客户端
3.客户端每次访问API是携带Token到服务器端。
4.服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码
五、思路及代码
思路1:APP登录时,输入帐号、密码。服务器在验证通过时,生成一段随机码(规律自己定,不重复即可,可以考虑UUID+一些东西),然后把帐号当作key,随机码(也就是token)当作value,放进缓存(推荐)或者数据库中,并设置过期时间。之后App每次请求都要带这个token随机码,你接收到请求第一步就是验证这个帐号做key的缓存中,有没有token值。如果没有或者过期,重新登录。
思路2:服务器端肯定可以获取到app端请求的sessionid,维护这个sessionid就可以,每次app端请求都带上这个sessionid以及用户名,服务器端写一个SessionContext的工具类来维护就可以了。(servlet2.5之后貌似不允许直接获取sessionid,以前就是这么做的)
package com.wl.util;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpSession;
public class SessionContext {
private static Map<String, HttpSession> sessionMap = new HashMap<String, HttpSession>();
public static Map<String, HttpSession> getSessionMap() {
return sessionMap;
}
public synchronized static void addSession(HttpSession session) {
if (session != null) {
sessionMap.put(session.getId(), session);
}
}
public synchronized static void delSession(HttpSession session) {
if (session != null) {
sessionMap.remove(session.getId());
}
}
public synchronized static HttpSession getSession(String sessionid) {
if (sessionid != null) {
return (HttpSession) sessionMap.get(sessionid);
} else {
return null;
}
}
public static void printCurrentSessionMapInfo() {
System.out.println(sessionMap.size());
Set<String> sessionids = sessionMap.keySet();
for (String sessionid : sessionids) {
System.out.println(sessionid);
}
}
public static void clearAllSession() {
if (sessionMap != null)
sessionMap.clear();
}
}
总结:以上是个人对开发中使用Token和session的一点总结,如有叙述不当之处请指正,我将及时改正并感谢,我知道还有更多更好的使用方式,我在这里只是抛砖引玉,希望大家将您的使用方式提出来,我们一起讨论,学习,一起进步,同时也为像我一样对这方面理解薄弱的朋友提供点帮助,谢谢。
参考
Android客户端和服务端如何使用Token和Session
关于APP 与JAVA 服务端的替代SESSION 的 TOKEN方案
app与后台的token、sessionId、RSA加密登录认证与安全解决方案
为什么 APP 要用 token 而不用 session 认证?
个人疑惑点
一、session是基于cookie的
《为什么 APP 要用 token 而不用 session 认证?》一文中用户fengchang评论说:session是基于cookie的
问题1: 如何理解这句话?
下面说一下个人见解:
问题2:Tomcat如何判断两个请求是在同一个session中的?
问题3:
以前我都是做web端的java后台接口。但最近要对接APP了。之前我们在做BS(Browser-Server,即浏览器-服务器)应用时,我们在后台只取维护用户的session,同一个浏览器的多次请求的session都是相同的(可以理解,一个用户发了多次请求,只会生成一个session对象。)我们只去看服务器中是否用这个用户的session,如果有的话,就认为登录成功过,就可以返回json数据。但是APP端的BS应用又有点不同。那么问题来了:安卓或者ios在访问后台接口时,没有addCookie();方法吗?
二、