【重难点】【计算机网络 04】Token 认证、BIO、NIO、AIO分别是什么、IO多路复用、Tomcat、Netty 和 Tomcat 的区别及其特点

【重难点】【计算机网络 04】Token 认证、BIO、NIO、AIO分别是什么、IO多路复用、Tomcat、Netty 和 Tomcat 的区别及其特点

一、Token 认证

1.Token 认证的大致流程

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会前发一个 Token 并保存(缓存或数据库),再把这个 Token 发送给 客户端
  4. 客户端收到 Token 后可以把它存储起来,比如放在 Cookie 或者 LocalStorage 里
  5. 客户端每次向服务端请求的时候需要携带服务端签发的 Token
  6. 服务端收到请求,首先会验证客户端请求里面携带的 Token,验证成功,才会处理客户端的请求

2.基于 JWT 的 Token 认证

实现 Token 认证的方案有很多,JWT(JSON Web Token)是最流行的实现方案。JWT 是一个非常轻巧的规范,JWT 规定 Token 要有以下三个部分:

  • header(头部):包括 token 名称和字段 alg,alg 表明使用的加密算法(一般为 HS256,改加密算法不可逆),如果未加密,要设置为 none
  • payload(载荷):里面是 Token 的具体内容,需要什么字段添加即可
  • signature(签名):将头部和载荷里的内容用 Base64 编码后拼接起来,再加上保存在服务端的加密密码(一般为无意义的字符串,目的就是为了实现除了服务端任何人都无法伪造签名),最后使用头部里表明的加密算法进行加密

以上三部分中间用 “.” 隔开,并且都会使用 Base64 编码

3.代码实现

添加依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

JWTUtil 工具类

package com.sisyphus.utils;

import com.sisyphus.exception.MyException;
import com.sisyphus.pojo.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

public class JWTUtil {

    public static String token = "token";
    //加密密码
    public static String jwt_secret = "313yphu3";
    //有效期,时间单位为毫秒
    public static long jwt_expr = 3600*24*1000;

    //签发 token
    public static String sign(User user){
        //指定签名时使用的加密算法
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        //生成签发时间
        long nowMillis = System.currentTimeMillis();
        Date date = new Date(nowMillis);

        //创建 payload 的私有声明
        HashMap<String,Object> claims = new HashMap<>();
        claims.put("id", user.getId());
        claims.put("userName", user.getName());

        //生成签发人
        String subject = user.getName();

        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(date)
                .setSubject(subject)
                .signWith(signatureAlgorithm,jwt_secret);

        //设置过期时间
        Date exprData = new Date(nowMillis + jwt_expr);
        builder.setExpiration(exprData);
        return builder.compact();
    }

    //验证 token
    public static boolean verify(String token){
        if (StringUtils.isEmpty(token)){
            return false;
        }
        Jwts.parser().setSigningKey(jwt_secret).parseClaimsJws(token).getBody();
        return true;
    }

    //获取用户信息
    public static User getUser(String token){
        if(StringUtils.isEmpty(token)){
            throw new MyException("token 不能为空");
        }
        if(verify(token)){
            Claims claims = Jwts.parser().setSigningKey(jwt_secret).parseClaimsJws(token).getBody();
            User user = new User();
            user.setId(Integer.parseInt(claims.get("id")+""));
            user.setName(claims.get("userName")+"");
            return user;
        }else{
            throw new MyException("超时或不合法的 token");
        }
    }
}

自定义异常类

package com.sisyphus.exception;

public class MyException extends RuntimeException{

    public MyException(){}

    public MyException(String message){super(message);}
}

Token 拦截器

package com.tedu.utils;

import com.tedu.exception.MyException;
import com.tedu.pojo.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader(JWTUtil.token);
        User user = JWTUtil.getUser(token);
        if (user == null){
            throw new MyException("超时或不合法的 token");
        }
        String newToken = JWTUtil.sign(user);
        response.setHeader(JWTUtil.token, newToken);
        request.setAttribute("user", user);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

注册拦截器

package com.tedu.mvc;

import com.tedu.utils.TokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    @Bean
    public TokenInterceptor tokenInterceptor(){
        return new TokenInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor())
                .addPathPatterns("/**")             //Token 拦截器作用于所有请求
                .excludePathPatterns("/login");     //登录时不验证 token,否则无法登录了
    }
}

最后就是在用户登录的时候,为用户签发 token,随着响应信息一起返回给用户,然后在前端根据业务需要将 token 存入 Cookie 或者 LocalStorage

二、BIO、NIO、AIO分别是什么

  • BIO:同步阻塞 IO,使用 BIO 读取数据时,线程会阻塞住,并且需要线程主动去查询是否有数据可读,并且需要处理完一个 Socket 之后才能处理下一个 Socket
  • NIO:同步非阻塞 IO,使用 NIO 读取数据时,线程不会阻塞,但需要线程主动地去查询是否有 IO 事件
  • AIO:也叫 NIO2.0,异步非阻塞 IO,使用 AIO 读取数据时,线程不会阻塞,并且当有数据可读时会通知给线程,不需要线程主动去查询

三、IO多路复用

举一个例子,模拟一个 TCP 服务器处理 30 个用户 Socket。假设你是一个老师,让 30 个学生解答一道题目,然后检查学生做的是否正确,你有下面几个选择:

  1. 按顺序逐个检查:先检查 A,然后是 B,……,如果这中间有一个学生卡壳了,老师就只能等他整理思路,全班都会被耽误
    这种模式就好比用循环挨个处理 Socket,不具备并发能力
  2. 创建 30 个分身:每个分身检查一个学生的答案是否正确
    这种模式类似于为每个 Socket 创建一个进程或者线程处理连接
  3. 谁先完成谁举手:C、D 先举手,表示他们解答完毕,老师下去依次检查 C、D 的答案,然后继续回到讲台上等。然后 E、A 举手,老师再下去检查 E、A
    这就是 IO 复用模型,Linux 下的 select、poll 和 epoll 就是干这个的

IO 多路复用是一种同步 IO 模型,实现一个线程可以监视多个文件句柄。一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作。没有文件句柄就绪时会阻塞应用程序,交出 CPU。多路是指网络连接,复用指的是同一个线程

四、Tomcat

1.含义

Tomcat 是一个轻量级应用服务器,是支持运行 Servlet/JSP 应用程序的容器,运行在 JVM 上,绑定 IP 地址并监听 TCP 端口

它是由 Apache 推出的一款免费开源的 Servlet 容器,可实现 JavaWeb 程序的装载,是配置 JSP(Java Server Page)和 Java 系统必备的一款环境。它也具有传统的 Web 服务器的功能:处理 HTML 页面

Tomcat 运行时占用的系统资源小,扩展性好,支持负载均衡与邮件服务等开发应用系统常用的功能,因而深受 Java 爱好者的喜爱,并得到了部分软件开发商的认可,和 Apache 一样,早已成为主流 Web 服务器的一种

2.作用

  1. 管理 Servlet 应用的生命周期
  2. 把客户端请求的 URL 映射到对应的 Servlet
  3. 与 Servlet 程序合作处理 HTTP

五、Netty 和 Tomcat 的区别及其特点

Netty 是一个基于 NIO 的异步网络通信框架,性能高,封装了原生 NIO 编码的复杂度,开发者可以直接使用 Netty 来开发高效率的各种网络服务器,并且编码简单

Tomcat 是一个 Web 服务器,是一个 Servlet 容器,基本上 Tomcat 内部只会运行 Servlet 程序,并处理 HTTP 请求,而 Netty 封装的是底层 IO 模型,关注的是网络数据的传输,而不关心具体的协议,可定制性更高

特点

  1. 异步、NIO 的网络通信框架
  2. 高性能
  3. 高扩展、高定制性
  4. 易用性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

313YPHU3

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

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

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

打赏作者

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

抵扣说明:

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

余额充值