前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文搞明白Cookie、Session与Token

一文搞明白Cookie、Session与Token

作者头像
红客突击队
发布2022-09-30 14:20:51
6690
发布2022-09-30 14:20:51
举报
文章被收录于专栏:kaydenkayden

一文搞明白Cookie、Session与Token

前言

本文旨在系统梳理总结下Cookie、Session和Token

  • Cookie:存在客户端,用来解决客户端如何保存信息的问题
  • Session:存在服务端,来解决多用户问题,即每个客户端会对应一个session
  • Token:无状态且支持跨域,有效防御CSRF,解决了session依赖于单个Web服务器的问题

一、COOKIE

1、Cookie 简介

HTTP 协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session 和 Cookie 的主要目的就是为了弥补 HTTP 的无状态特性

  • 当用户第一次访问服务器时,服务器可以在响应信息(response)中增加Set-Cookie响应头,将信息以Cookie为载体发送给浏览器
  • 浏览器接收到服务器发送来的Cookie信息,就会将他保存在浏览器的缓冲区内
  • 这样,当浏览器再次访问服务器时,就会将Cookie放在请求消息中,Web服务器就可以通过request中的用户信息来分辨此次请求是由哪个用户发起的
Cookie定义

网站向访问电脑写入的小文本,大多数是4KB,记录用户ID、密码、停留时间等信息

Cookie作用
  • 会话管理:登陆、购物车、游戏得分或者服务器应该记住的其他内容
  • 个性化:用户偏好、主题或者其他设置
  • 追踪:记录和分析用户行为
Cookie分类
  • 内存Cookie 由浏览器维护,保存在内存中,浏览器关闭就消失
  • 硬盘Cookie 保存在硬盘里,有个过期时间

2、Cookie 格式

代码语言:javascript
复制
Set-Cookie: "<name>=<value>[;domain=<domain_name>][;path=<some_path>][;expires=<date>][;<Max-Age>=<age>][;HttpOnly][;secure]"

其中name=value是必选项,其它都是可选项

Cookie的主要构成如下:

  • Set-Cookie:HTTP响应头,服务端通过此HTTP头向客户端发送Cookie
  • name:一个唯一确定的cookie名称。通常来讲cookie的名称不含分号、逗号和空格等字符,且不区分大小写
  • domain:cookie对于哪个域是有效的。所有向该域发送的请求中都会包含这个cookie信息。
  • path:表示这个cookie影响到的路径,浏览器会根据这项配置,向指定域中匹配的路径发送cookie。如果值为/,则Web服务器上所有WWW资源均可读取该Cookie。借助path和domain,可以有效控制Cookie被访问的范围
  • expires:失效时间,表示cookie何时应该被删除的时间戳(即何时应该停止向服务器发送这个cookie)。如果不设置这个时间戳,浏览器会在页面关闭时即将删除所有cookie;不过也可以自己设置删除时间。这个值是GMT时间格式,如果客户端和服务器端时间不一致,使用expires就会存在偏差。
  • max-age:与expires作用相同,用来告诉浏览器此cookie多久过期(单位是秒),而不是一个固定的时间点。正常情况下,max-age的优先级高于expires。
  • HttpOnly:告知浏览器不允许通过脚本document.cookie去更改这个值,同样这个值在document.cookie中也不可见。但在http请求张仍然会携带这个cookie。注意这个值虽然在脚本中不可获取,但仍然在浏览器安装目录中以文件形式存在。这项设置通常在服务器端设置,用来防御XSS
  • secure:安全标志,指定后,只有在使用SSL链接时候才能发送到服务器,如果是HTTP链接则不会传递该信息。就算设置了secure 属性也并不代表他人不能看到你机器本地保存的 cookie 信息,所以不要把重要信息放cookie就对了

3、Cookie 读写

Java、JavaScript、PHP、ASP.NET都可以读写Cookie

常用API

javax.servlet.http.Cookie类来封装Cookie信息,它包含有生成Cookie信息和提取Cookie信息的各个属性的方法:

  • public Cookie(String name,String value)
  • setMaxAge(int longTime)getMaxAge方法:设置和获取cookie的最大有效时长,setMaxAge(0) 表示删除磁盘上的某个cookie 注:cookie没有提供修改方法,当name一样时,覆盖原来的就算是更新了
  • setPathgetPath方法:设置或读取Cookie的作用范围
  • HttpServletResponse接口:定义了一个addCookie(Cookie cookie)方法,它用于在发送给浏览器的HTTP响应消息中增加一个Set-Cookie响应头字段
  • HttpServletRequest接口:定义了一个getCookies方法,它用于从HTTP请求消息的Cookie请求头字段中读取所有的Cookie项
  • getName方法:获取到cookie的name
  • setValue(String value)getValue方法:设置和获取cookie的value
一个模板

代码语言:javascript
复制
public class Cookiedemo1 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 创建Cookie对象,保存会话数据
        // 如果发送中文,必须先使用URLEncoder进行加密
        String name = URLEncoder.encode("张三", "utf-8");
        Cookie c1 = new Cookie("name", name);
        Cookie c2 = new Cookie("email", "chen@c.com");
        // 发送cookie
        response.addCookie(c1);
        response.addCookie(c2);
        // 浏览器下次访问获取已有的cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                // cookie的名
                String cname = cookie.getName();
                // cookie的值
                String cvalue = cookie.getValue();
                // 解密
                cvalue = URLDecoder.decode(cvalue, "utf-8");
                System.out.println(cname + "=" + cvalue);
            }
        } else {
            System.out.println("没有cookie信息!");
        }
    }
}
代码语言:javascript
复制
JavaScript 操作Cookie

操作方法

代码语言:javascript
复制
//创建一个Cookie,属性默认
document.cookie="password=123456";
//创建一个Cookie,设置属性:过期时间,path
document.cookie="attribute=pathDomain; expires=Thu, 14 Dec 2021 12:00:00 GMT; path=/";
//读取Cookie,返回name1=value1;...;namen=valuen  形式的字符串
document.cookie;
//修改Cookie,重新创建一遍,name相同会覆盖之前Cookie,修改了过期时间
document.cookie="attribute=pathDomain; expires=Thu, 14 Dec 2020 12:00:00 GMT; path=/";
//删除Cookie,可以指定过期时间为当前时间;注意:因为过期时间以浏览器的服务器时间为准,一般会有八小时时差
document.cookie="password=123; expires=" + new Date();

例子

代码语言:javascript
复制
//创建Cookie,并设置有效期(单位天)
function setCookie(cname,cvalue,exdays)
{
  var d = new Date();
  d.setTime(d.getTime()+(exdays*24*60*60*1000));
  var expires = "expires=" + d.toGMTString();
  document.cookie = cname + "=" + cvalue + "; " + expires;
}

//获取对应Cookie的值,通过字符串截取的方式
function getCookie(cname)
{
  var name = cname + "=";
  var ca = document.cookie.split(';');
  for(var i=0; i<ca.length; i++) 
  {
    var c = ca[i].trim();
    if (c.indexOf(name)==0) return c.substring(name.length,c.length);
  }
  return "";
}

//删除Cookie,过期时间提前1天,解决时差问题
function delCookie(cname)
{
  var d = new Date();
  d.setTime(d.getTime()-(24*60*60*1000));
  var expires = "expires=" + d.toGMTString();
  document.cookie = cname + "=; " + expires;
}
应用

使用cookie进行自动登录的服务器代码

代码语言:javascript
复制
     //如果cookie中有customer信息,就放到session中
     boolean checkCustomerCookie(HttpServletRequest request) throws UnsupportedEncodingException {
        Cookie[] cookies = request.getCookies();
         if (cookies != null) {
            for (Cookie cookie : cookies) {
                String cookieName = cookie.getName();
                //如果有,解密后拿cookie中的值和数据库中的值进行比较
                if (Constant.cookieCustomerKey.getName().equals(cookieName)){
                    String cookieValue = cookie.getValue();
                    String decry = EncrypUtils.Base64Util.decry(cookieValue);
                    Customer customer1 = JsonUtils.stringToObject(decry, Customer.class);
                    Customer customer2 = customerService.checkLogin(customer1.getPhoneNumber(), customer1.getPassword());
                    if (customer2 != null){
                        //放入到session中,放行
                        request.getSession().setAttribute("customer",customer2);
                        //自动登录时,更新用户的在线状态
                        Customer onlineCustomer = new Customer();
                        onlineCustomer.setId(customer2.getId());
                        onlineCustomer.setOnlineStatus(String.valueOf(Constant.ONLINESTATUS.getCode()));
                        customerService.updateById(onlineCustomer);
                        return true;
                    }
                }
            }
         }
        return false;
    }

4、Cookie 编码

cookie中存储中文会出现中文乱码,需要对value进行额外的编码

base64编码

存储:Base64.getEncoder().encodeToString(content.getBytes("utf-8"));

读取:new String(Base64.getDecoder().decode(cookie.getValue()),"utf-8")

URLEncoder类

存储:Cookie cookie = new Cookie("userName", URLEncoder.encode("你好世界", "UTF-8"));

读取:URLDecoder.decode(cookie.getValue(), "UTF-8")

二、SESSION

1、Session 简介

客户端请求服务端,服务端会为这次请求开辟一块内存空间,这个对象便是 Session 对象,存储结构为 ConcurrentHashMap

Session 弥补了 HTTP 无状态特性,服务器可以利用 Session 存储客户端在同一个会话期间的一些操作记录

为了防止服务器端的session过多导致内存溢出,web服务器默认会给每个session设置一个有效期, (30分钟)若有效期内客户端没有访问过该session,服务器就认为该客户端已离线并删除该session

Session 原理
  • 服务器第一次接收到请求时,开辟了一块 Session 空间(创建了Session对象),同时生成一个 sessionId ,并通过响应头的 Set-Cookie:JSESSIONID=XXXXXXX 命令,向客户端发送要求设置 Cookie 的响应
  • 客户端收到响应后,在本机客户端设置了一个 JSESSIONID=XXXXXXX 的 Cookie 信息,该 Cookie 的过期时间为浏览器会话结束
  • 接下来客户端每次向同一个网站发送请求时,请求头都会带上该 Cookie信息(包含 sessionId ), 然后,服务器通过读取请求头中的 Cookie 信息,获取名称为 JSESSIONID 的值,得到此次请求的 sessionId
保存sessionID的方式
  • cookie中 通过一个特殊的cookie,name为JSESSIONID,value为服务器端的sessionId 但是当浏览器禁用cookie后,session就会失效
  • url重写 当浏览器Cookie被禁时用 把sessionId附加在URL路径的后面:一种是作为URL路径的附加信息,另一种是作为查询字符串附加在URL后面。
    • response.encodeURL(String url)用于对表单action和超链接的url地址进行重写
    • response.encodeRedirectURL(String url) 用于对sendRedirect方法后的url地址进行重写

2、Session 读写

常用API
  • getId()方法:得到sessionid
  • invalidate()方法:让session立刻失效
  • getAttribute(String key):根据key获取该session中的value
  • setAttribute(String key,Object value):往session中存放key-value
  • removeAttribute(Stringkey):根据key删除session中的key-value
  • getServletContext():得到ServletContext
  • setMaxInactiveInterval(long timeout)/getMaxInactiveInterval:设置/获取session的最大有效时间
  • getCreationTime方法:获取session的创建的时间
  • getLastAccessedTime方法:获取session最后一次访问的时间
  • getSession():从HttpServletRequest中获取session
一个模板
代码语言:javascript
复制
session.setMaxInactiveInterval(2*3600);//session 保存俩小时
 
Cookie cookie=new Cookie("JSESSIONID",session.getId());//sessionid放到cookie中
 
cookie.setMaxAge(2*3600);//客户端的cookie也保存俩小时
 
cookie.setPath("/");//cookie作用范围设为整个项目
 
response.addCookie(cookie);//给浏览器返回该Cookie

三、TOKEN

1、Token 简介

Token定义

Token,可以翻译成"令牌",本质上它是一个全局唯一的字符串,用来唯一识别一个客户端 但它不像cookie和session一样是一种web规范 可以认为他是借鉴了cookie和session工作的原理,进而延伸出来的一种维持用户会话状态的机制

Token原理
  • 客户端使用用户名跟密码请求登录
  • 服务端收到请求,去验证用户名与密码
  • 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  • 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  • 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  • 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
  • 因为token是被签名的,所以我们可以认为一个可以解码认证通过的token是由我们系统发放的,其中带的信息是合法有效的
Token特点

Token的特点,是创造出这东西的原因,也是它跟 Session & Cookie 这套机制的区别:

  • 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输
  • 无状态(也称:服务端可扩展行): Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息
  • 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可
  • 去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可
  • 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多
  • CSRF: 因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范

2、JSON Web Token (JWT)

JSON Web Token(JWT),通常可以称为 Json 令牌,是RFC 7519 中定义的用于安全的将信息作为 Json 对象进行传输的一种规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息

JWT格式

一个JWT实际上就是一个字符串,它由三部分组成:头部、载荷与签名

(1)头部(Header)

  • 用于描述关于该JWT的最基本的信息:令牌的类型(即 JWT)和使用的签名算法
  • 这也可以被表示成一个JSON对象
  • 然后将其进行base64编码,得到第一部分
代码语言:javascript
复制
{
"typ": "JWT",
"alg": "HS256"
}

(2)载荷(Payload)

  • 一般添加用户的相关信息或其他业务需要的必要信息
  • 不建议添加敏感信息,因为该部分在客户端可解密
  • 进行base64编码,得到第二部分
代码语言:javascript
复制
{ "iss": "JWT Builder", 
  "iat": 1416797419, 
  "exp": 1448333419, 
  "aud": "www.example.com", 
  "sub": "aaa@example.com", 
  "Email": "aaa@example.com", 
  "Role": [ "admin", "user" ] 
}

注:针对JWT的攻击,通常是这部分受控导致

(3)签名(Signature)

  • 需要base64加密后的header和base64加密后的payload使用"."连接组成的字符串
  • 然后通过header中声明的加密方式进行加盐secret组合加密(在加密的时候,我们还需要提供一个密钥(secret),加盐secret组合加密)
  • 然后就构成了jwt的第三部分。
代码语言:javascript
复制
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

最后,将这一部分签名也拼接在被签名的字符串后面,我们就得到了完整的JWT

注意:secret就是你服务端的私钥,在任何场景都不应该流露出去,一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了

JWT作用
  • 认证(Authorization):这是使用 JWT 最常见的一种情况,一旦用户登录,后面每个请求都会包含 JWT,从而允许用户访问该令牌所允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小
  • 信息交换(Information Exchange):JWT 是能够安全传输信息的一种方式。通过使用公钥/私钥对 JWT 进行签名认证。此外,由于签名是使用 head 和 payload 计算的,因此你还可以验证内容是否遭到篡改

结语

对Cookie、Session与Token做了个归纳

红客突击队于2019年由队长k龙牵头,联合国内多位顶尖高校研究生成立。其团队从成立至今多次参加国际网络安全竞赛并取得良好成绩,积累了丰富的竞赛经验。团队现有三十多位正式成员及若干预备人员,下属联合分队数支。红客突击队始终秉承先做人后技术的宗旨,旨在打造国际顶尖网络安全团队。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-08-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 红客突击队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一文搞明白Cookie、Session与Token
  • 前言
  • 一、COOKIE
    • 1、Cookie 简介
      • 2、Cookie 格式
        • 3、Cookie 读写
          • 4、Cookie 编码
          • 二、SESSION
            • 1、Session 简介
              • 2、Session 读写
              • 三、TOKEN
                • 1、Token 简介
                  • 2、JSON Web Token (JWT)
                  • 结语
                  相关产品与服务
                  内容分发网络 CDN
                  内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档