请你说说cookie、session和token的概念和区别


前言

http协议本身是一种无状态的协议,我们并不能知道是哪个用户发出的请求,因此产生了Web身份校验问题。cookie、session和token都是为了解决Web身份校验而产生的,初学者往往搞不清三者的概念和区别,本篇文章带大家解析这三者的区别和概念。


一、session和cookie

1.1Web发展史

1、很久以前,Web基本上就是文档的浏览而已, 既然是浏览,作为服务器, 不需要记住是谁请求的,每次请求都是一个新的HTTP协议。

2、但是随着交互式Web应用的兴起,像在线购物网站,需要登录的网站等等,需要管理会话,必须记住哪些人登录系统,哪些人往自己的购物车中放商品, 也就是说我必须把每个人区分开。

解决办法:
给大家发一个会话标识(session id), 说白了就是一个随机的字串,每个人收到的都不一样,每次大家向我发起HTTP请求的时候,把这个字符串给一并捎过来, 这样我就能区分开谁是谁了,这就是所谓的session认证。

1.2session和cookie

session :就是会话。这个就类似于你和一个人交谈,你怎么知道当前和你交谈的是张三而不是李四呢?对方肯定有某种特征(长相等)表明他就是张三。服务器就要给每个客户端分配不同的“身份标识”,然后客户端每次向服务器发请求的时候,都带上这个“身份标识”,服务器就知道这个请求来自于谁了。至于客户端怎么保存这个“身份标识”,可以有很多种方式,对于浏览器客户端,大家都默认采用 cookie 的方式。

cookie:非常具体的东西,指的就是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求会把该cookie发送给服务器。

区别:cookie数据存放在客户的浏览器上,session数据放在服务器上。将重要信息存放在Session中,其他信息如果需要保留,可以放在cookie中。

session认证流程如下:
在这里插入图片描述

1、当用户首次访问服务器的时候,服务器为每个用户单独创建一个 Session 对象,并分配一个新的 SessionID,此时 SessionID 通过 cookie 保存在用户端。

2.当用户再次访问服务器的时候,携带保存 SessionID 的 Cookie 给服务器,服务器查询是否存在这个 sessionID,如果存在,即认为用户处于登录状态,如果没有对应的 SessionID,服务器会给分配一个新的 sessionID。

关于session认证代码如下:
1.如果登录成功(账号密码验证成功),往session中存放manage键值对。

public class ControllerLogin {
    @Autowired
    LoginService loginService;

    @RequestMapping(value ="/login")
    public CommonMessage test(Manage manage, HttpSession httpSession) {
        try {
            Manage manageTemp = loginService.login(manage);

            if (manageTemp != null) {
                httpSession.setAttribute("manage", manageTemp);
                return new CommonMessage<Manage>(200, "登录成功", manageTemp);
            } else {
                return new CommonMessage(300, "登录失败",(Manage)null);
            }
        } catch (Exception e) {
            return new CommonMessage(301, "登录失败", (Manage)null);
        }
    }
  }

2.session认证代码部分,使用拦截器,请求到达之前判断是否处于登录状态(即有没有manage属性值)

public class LoginInterceptor implements HandlerInterceptor {
    /* 当请求到达控制器之前被执行 true--继续向下执行,到达下一个拦截器,或控制器 false--不会继续向下执行*/
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession httpSession=request.getSession();
        Manage manage = (Manage) httpSession.getAttribute("manage");
        if(manage !=null){
            return  true;
        }else{
            response.getWriter().println(203);
            return  false;
        }
    }
    /*控制器方法执行之后执行*/
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }
    /*整个请求结束后执行*/
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

1.3session认证缺点

1.session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
2.用户下一次请求还必须在第一次请求的服务器上(只有第一次访问的服务器保存了Session信息),在分布式的应用上,相应的限制了负载均衡器的能力。
3.cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。


二、基于token的鉴权机制

基于Token的身份验证是无状态的,我们不将用户信息存在服务器或Session中,解决了在服务端存储信息时的许多问题。

流程:
1.用户通过用户名和密码发送请求。
2.程序验证,并返回一个签名的token 给客户端。
3.客户端储存token,并且每次用于每次发送请求。
4.服务端验证token并返回数据。

在这里插入图片描述
token由三部分组成:
1.第一部分头部(header):声明加密算法(HMAC -HS256)
2.第二部分我们称其为载荷(payload ):保存用户的信息
3.第三部分是签名(signature):需要base64转码后的header和base64转码后的payload连接组成的字符串,然后通过header中声明的加密方式进行加密。

在这里插入图片描述

token令牌如何让别人伪造不了?
1.用HMAC-SHA256算法,加上密钥,对数据做一个签名,把这个签名和数据一起作为token, 由于密钥别人不知道,就无法伪造token了。

2.当客户端把这个token发过来的时候,再用同样的HMAC-SHA256算法和同样的密钥,对数据再计算一次签名, 和token中的签名做个比较, 如果相同,就知道客户端已经登录过了,如果不相同,数据部分肯定被人篡改过,则告诉客户端没有认证。
在这里插入图片描述
token认证优点:
1.无状态、可扩展:服务器只是生成token , 然后验证token,基于这种无状态和不存储Session信息,负载负载均衡器能够将用户信息从一个服务传到其他服务器上。
2.安全:作为身份认证token安全性比session好,因为每个请求都有签名。

token认证的代码如下:
1.token工具类,用来创建生成token、验证token有效性和获得token 中playload部分数据。

public class TokenUtil {
   /* 创建生成token的方法*/
    public static String token (Integer id, String account,Integer type){
        String token = "";
        try {
            //过期时间 为1970.1.1 0:0:0 至 过期时间  当前的毫秒值 + 有效时间
            Date expireDate = new Date(new Date().getTime() + 10000*1000);
            //秘钥及加密算法
            Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
            //设置头部信息
            Map<String,Object> header = new HashMap<>();
            header.put("typ","JWT");
            header.put("alg","HS256");
            //携带id,账号信息,生成签名
            token = JWT.create()
                    .withHeader(header)
                    .withClaim("id",id)
                    .withClaim("account",account)
                    .withClaim("type",type)
                    .withExpiresAt(expireDate)
                    .sign(algorithm);
        }catch (Exception e){
            e.printStackTrace();
            return  null;
        }
        return token;
    }
    /*验证token是否有效*/
    public static boolean verify(String token){
        try {
            //验签
            Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception e) {//当传过来的token如果有问题,抛出异常
            return false;
        }
    }
   /* 获得token 中playload部分数据*/
    public static DecodedJWT getTokenInfo(String token){
        return JWT.require(Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);
    }
}

2.拦截器,每次请求到达之前判断token是否有效。

public class LoginInterceptor implements HandlerInterceptor {
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String toke =request.getHeader("token");
        boolean flag=TokenUtil.verify(toke);
        if(flag){
            return  true;
        }else{
            response.getWriter().println(401);
            return  false;
        }
    }
}

3.登录控制器,如果登录成功,则把id,账号名等需要的信息存储在token中,然后放在实体manage中,前端可以直接获取到token然后存储在浏览器中,下次发请求之前把token也一起发送过来即可。

@RestController
@RequestMapping("/api/login")
public class BackLoginController {
    @Autowired
    AdminService adminService;
    @RequestMapping("/login")
    public CommonResult login(@RequestBody Manage manage){
        CommonResult commonResult=null;
        try {
            Manage manage1= adminService.login(manage);

            if (manage1!=null){
                String token= TokenUtil.token(manage1.getId(),manage1.getAccount(),manage1.getType());
                manage1.setToken(token);
                commonResult=new CommonResult(200,"登录成功",manage1);
                return commonResult;
            }else {
                commonResult=new CommonResult(201,"账号或密码错误",null);
                return commonResult;
            }

        } catch (Exception e) {
            e.printStackTrace();
            commonResult=new CommonResult(500,"服务器忙",null);
            return commonResult;
        }
    }
}

总结

理解cookie、session和token的关键在于它们三者都是为了解决web身份验证而诞生的。session保存在服务器端,cookie和token保存在客户端,从这个方面入手可以联想出很多区分点。建议不要死记硬背这三者的概念和区别,要从认证流程出发思考它们之间的关系。


  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JinziH Never Give Up

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值