4.基础技术&API网关&JWT

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 4.基础技术&API网关&JWT

基础技术

自定义拦截器-HandlerInterceptor

一般情况下,对来自浏览器的请求,在该请求进行业务逻辑处理之前,我们是可以进行拦截处理的,实 现的方式有两种,分别是基于Tomcat的Filter,和基于SpringMVC的HandlerInterceptor。

  • 基于Tomcat的Filter实现拦截,可以利用SpringBoot的FilterRegistrationBean实现Filter和Spring 的整合,向Tomcat注册自定义Filter,使Filter可以在Servlet处理请求之前拦截请求。
  • 基于SpringMVC中的拦截器HandlerInterceptor实现拦截器,拦截器可以在Controller中相应的 Action方法处理请求前进行拦截
Filter和Interceptor的区别:
1. Filter直接依赖Servlet容器,而Interceptor不直接依赖于Servlet容器
2. Filter对Http请求起作用,而Interceptor只能对action请求起作用。
3. Interceptor可以访问Action的上下文值栈里的对象,即接收的参数,而Filter不能。
4. 在action的生命周期里,Interceptor可以被多次调用,而Filter针对一个请求只会被调用一次。
5. Filter在过滤是只能对request和response进行操作,而interceptor可以对request、response、handler、 modelAndView、exception进行操作

代码实现

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行了preHandle");
        return false;
    }
}
@Configuration
public class MyWebMvcConfiure implements WebMvcConfigurer {
    @Autowired
    MyInterceptor myInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry
                .addInterceptor(myInterceptor)
                .addPathPatterns("/**");
    }
}

请求的拦截流程:

TK-Mybatis

官网:https://github.com/abel533/Mapper/wik

TK-Mybatis是在Mybatis的基础之上进行了简化开发,主要针对单表的查询更方便了

使用步骤:

  • 导入依赖
<!--Tk-Mybatis相关-->
<!--Mysql连接-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>
<!--durid-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.16</version>
</dependency>
<!--tk-Mybatis-->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.1.5</version>
</dependency>
  • 数据源配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/practice?useUnicode=true&characterEncoding=utf8&useOldAliasMetadataBehavior=true&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

这里在配置的时候一定要注意带上时区的信息,否则会报错

  • 代码
  • DO类
@Data
@Table(name = "user") //设置该类对应的表名
public class User {
    @Id //标记为主键
    @KeySql(useGeneratedKeys = true) //开启插入返回主键
   public Integer id;
    @Column(name = "username") //当类中的属性名和表中的字段对不上时,使用Column注解来转换
   public String name;
   public String mobile;
}
  • 接口
public interface UserMapper extends Mapper<User> {
}
  • SpringBoot的启动类
@SpringBootApplication
@MapperScan(basePackages = "com.fh.tkmybatis.tk") //扫描mapper包配置,注意要使用tk-mybatis的
public class TkMybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(TkMybatisApplication.class, args);
    }
}
  • 使用
@Test
    public void testInsert(){
//        User user = new User();
//        user.setMobile("1234567");
//        user.setName("whwu");
//        userMapper.insert(user);
        User user = new User();
        user.setName("zl");
        userMapper.insertSelective(user);
    }
    @Test
    public void query(){
//        User user = userMapper.selectByPrimaryKey(6);
//        System.out.println(user);
//        User user = new User();
//        user.setId(7);
//        User user1 = userMapper.selectByPrimaryKey(user);
//        System.out.println(user1);  
//        Example example = new Example(User.class);
//
//         example.createCriteria().andEqualTo("name", "zl");
//
//        List<User> users = userMapper.selectByExample(example);
//
//        System.out.println(users);
        Example example = new Example(User.class);
        example.createCriteria().andLike("name","%z%");
        List<User> users = userMapper.selectByExample(example);
        System.out.println(users);
    }
    @Test
    public void  testUpdate(){
        User user = new User();
        user.setName("景天");
        Example example = new Example(User.class);
        example.createCriteria().andEqualTo("mobile","1234");
        userMapper.updateByExampleSelective(user,example);
    }
    @Test
    public void testDelete(){
        Example example = new Example(User.class);
        example.createCriteria().andEqualTo("name","景天");
        userMapper.deleteByExample(example);
    }

MapStruct

在一个成熟的工程中,尤其是现在的分布式系统中,应用与应用之间,还有单独的应用细分模块之 后, DO一般不会让外部依赖,这时候需要在提供对外接口的模块里放 DTO用于对象传输,也即 是 DO对象 对内,DTO对象对外,DTO 可以根据业务需要变更,并不需要映射 DO 的全部属性。

DTO : Data Transport Object (数据传输对象)

VO: View Object (视图解析对象)

  • 导入依赖
<!--mapstruct-->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.3.0.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.3.0.Final</version>
</dependency>
  • 定义转换器接口
@Mapper(componentModel = "spring")
public interface UserConverter {
    @Mappings({
            @Mapping(source = "name",target = "username")
    })
    UserDTO usertoUserDTO(User user);
    List<UserDTO> usersToUserDTOList(List<User> users);
}
  • 同样的,如果这里出现了DO类和DTO类的属性名称对不上,那么是可以使用@Mappings注解来进行转换的,如果定义了单个对象的转换的方法上加上了注解,那在List的方法上就不需要加上注解了
@Data
public class UserDTO {
    public Integer id;
    public String username;
}
  • 使用定义好的转换器
@Test
public void testMapStruct(){
    Example example = new Example(User.class);
    example.createCriteria().andLike("name","%h%");
    List<User> users = userMapper.selectByExample(example);
    System.out.println(users);
    List<UserDTO> userDTOS = userConverter.usersToUserDTOList(users);
    System.out.println(userDTOS);
}

SPI

SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。 目前有不少框架用 它来做接口实现类的扩展发现(Dubbo、JDBC等), 简单来说,它就是一种动态替换发现的机制, 举个例子来 说, 有个接口,想运行时动态的给它添加实现,你只需要添加一个实现。

Java SPI的机制的约定

  • 当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/目录中同时 创建一个以服务接口命名的文件。
  • 该文件里就是实现该服务接口的具体实现类。
  • 而当程序运行时,使用这个接口的实现类的时,就能通过该jar包META-INF/Services/的配置 文件找到具体的实现类名。
  • 并加载该实现类(可能有多个)并实例化 基于这样的一个约定就能很好的找到服务接口的实现类,而不需要在代码里指定。

具体的代码实现:

  • 首先定义接口
public interface Pay {
    void payAccount(Double money);
}
  • 定义接口实现类
public class AliPay implements Pay {
    @Override
    public void payAccount(Double money) {
        System.out.println("支付宝支付"+money+"元");
    }
}
public class WechatPay implements Pay {
    @Override
    public void payAccount(Double money) {
        System.out.println("微信支付"+money+"元");
    }
}

配置文件的名字需要和接口的全限定类名一致,里面的内容填写接口的实现类的全限定类名即可

com.fh.tkmybatis.spi.WechatPay
com.fh.tkmybatis.spi.AliPay
  • 使用过程
public class Test {
    public static void main(String[] args) {
        ServiceLoader<Pay> load = ServiceLoader.load(Pay.class);
        Iterator<Pay> iterator = load.iterator();
        while (iterator.hasNext()){
            Pay next = iterator.next();
            next.payAccount(200.0);
        }
    }
}

Gateway&JWT

API网关

概述

API是Application Programming Interface缩写,翻译成中文就是应用程序接口。在实际微服务中可以 理解一个个功能方法。就比如你一个用户服务的微服务,可以对外提供 API 接口为,查找用户,创建用 户等。

网关:

在计算机网络中,网关(英语:Gateway)是转发其他服务器通信数据的服务器,接收从客户端发送来的请求时,它就像自己拥有资源的源服务器一样对请求进行处理

如果没有网关,难道不行吗?功能上是可以的,我们直接调用提供的接口就可以了。那为什么还需要网 关?

因为网关的作用不仅仅是转发请求而已。我们可以试想一下,如果需要做一个请求认证功能,我们可以 接入到 API 服务中。但是倘若后续又有服务需要接入,我们又需要重复接入。这样我们不仅代码要重复 编写,而且后期也不利于维护。

由于接入网关后,网关将转发请求。所以在这一层做请求认证,天然合适。这样这需要编写一次代码, 在这一层过滤完毕,再转发给下面的 API。 所以 API 网关的通常作用是完成一些通用的功能,如请求认证,请求记录,请求限流,黑白名单判断 等。

API网关是一个服务器,是系统的唯一入口。

API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有 的非业务功能。通常,网关也是提供REST/HTTP的访问API。

架构图

JWT

简介

JWT全称 Json·Web·Token,是一个开放标准(RFC·7519),它定义了一种紧凑的,自包含的方式,用 于作为JSON对象在各方之间安全的传输信息。该信息可以被验证和信任,因为它是数字签名的。

JWT是目前最流行的跨域身份解决方案。

使用场景

下列场景中使用JWT是很有用的:

  • Information Exchange(信息交换):对于安全的在各方之间传输信息而言,Json·Web·Token 无疑是一种很好的方式。因为JWT可以被签名,我们还可以验证内容有没有被篡改。
  • 正因为数据可以基于JWT进行安全的传输,所以基于JWT,我们可以实现单点登录功能
单点登录:Single Sign On,简称为 SSO,是比较流行的企业业务整合的解决方案之一。SSO的定义是在
多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

单点登录问题及解决方案

方案1:Nginx负载均衡做iphash,同一个用户的请求永远只给到同一个后台服务器。

不足之处:

  • 一个服务有若干个用户,有的用户活跃,向后端发起请求的次数比较频繁,而有的用户不活跃,那 么基于这种iphash的解决方案的话就势必会造成负载不均衡,也就是说有的tomcat服务服务器压 力大,而有的tomcat服务器压力不大,造成资源分配不均的情况。
  • 其次就是假如有一个tomcat服务挂了,那么对于有一些用户来说,这个系统就挂了,失去了集群 的意义。

方案2:基于tomcat广播的session复制

不足之处:

  • 每一个tomcat都需要维护一个大的session,会造成内存资源紧张。
  • 大量复制session占用服务器带宽

方案3: 将session数据,以能够共享的方式存储,比如存储到Mysql,或者Redis数据库中

  • 存储session数据时,还要多访问一次数据库,或涉及到网络,或IO过程

解决方案:使用JWT能够完美的解决上述问题

基于Token的身份认证,大概流程如下:

1.客户端使用用户名、密码登陆

2.服务器接收到请求后去验证用户名和密码

3.验证成功之后服务端会签发一个token,并且将这个token发送给客户端

4.客户端接收到了token之后将他存储起来

5.客户端每次向服务器发起请求的时候都需要带着服务器签发的token

6.服务端接收到请求,然后去验证客户端中的token,如果验证成功,就返回请求数据,否则不返回

Token VS Session

在看JWT如何解决上述问题之前,我们先来看看基于Token的身份认证与基于session的身份认证。

  • 基于session的身份认证。
  1. Sessions:每次用户认证通过以后,服务器需要创建一条记录来保存用户信息,通常是在内 存中。那么随着认证通过的用户越来越多,服务器在这里的开销就会越来越大。
  2. Scalability:由于session是在内存中的,这就带来一些扩展性的问题。
  3. CSRF:用户很容易受到CSRF的攻击
CSRF:跨站请求伪造(早期的攻击方式)
假设一个网站用户Bob可能正在浏览聊天论坛,而同时另一个用户Alice也在此论坛中,并且后者
刚发布了一个具有Bob银行链接的图片信息。正常情况下,Bob点击图片链接访问银行,银行会让
Bob登录
假如Bob在此之前刚好登录过了银行,浏览器中还有cookie,那么此时银行可能认为点击这个图片访问银行是Bob发出的,所以会正常给出响应。假如这个链接是一个Alice伪造的转账的请求,那么就会产生经济损失

对比

  1. 而JWT是把用户信息保存在客户端,基于token的方式将用户状态分散到了各个客户端中,可以减 轻服务端的内存压力。
  2. session的状态存储在服务端,客户端只有sessionId,而token的状态是存储在客户端,这就使得 服务器端并未存储用户登录状态,是无状态的,因为是无状态的,所以便于集群的扩容。每次在扩容的时候就不需要关心session的复制等问题
  3. 安全
  • Token不是cookie。每次请求的时候token都会被发送,可以作为请求参数发送,可以放在请 求头里面发送,也可以放在cookie里面被发送
  • 即使在你的实现中将token存储到客户端的cookie中,这个cookie也只是一种存储机制,而 非身份认证机制。没有基于会话的信息可以操作,因为我们没有会话

JWT token的基本格式

JSON·Web·Token由三部分组成,他们之间用圆点(·)连接,这三部分分别是

  • Header
  • Payload
  • Signature

Header由两部分的信息组成:

  • type:声明类型,这里的是jwt
  • alg:声明加密的算法 通常直接使用 HMAC SHA256

Payload就是存放有效信息的地方(不强制)

iss: jwt签发者

sub: jwt所面向的用户

aud: 接收jwt的一方

exp: jwt的过期时间,这个过期时间必须要大于签发时间

nbf: 定义在什么时间之前,该jwt都是不可用的

iat: jwt的签发时间

jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

claim:jwt存放信息的地方

Signature:签名信息

因此,一个典型的JWT看起来是这个样子的: xxxxxxxx·yyyyyyyyyy·zzzzzzzzzzz

例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ3bGd6cyIsImV4cCI6MTU4Nzk3MzY1NywidXNlciI6Ijk2MkYxODkwNTVFMzRFNzVERjVGMzQ0QTgxODNCODdGIn0.APehq9dxRiilgTOGyuz9qtZxvPDIJ5QIIVUCLYeX1QE

项目中如何使用

项目已经整合好了JWT,并且给我们提供了现有的工具类进行操作

com.mall.user.utils.JwtTokenUtils

如何使用现有的工具创建JWT呢?

String token = JwtTokenUtils.builder().msg("xxx").build().creatJwtToken();

如何解析JWT呢?

String msg = JwtTokenUtils.builder().token(token).build().freeJwt();

JWT的具体的执行流程

  • 首先在登陆的时候进行用户名密码验证,如果通过了那就通过加密算法存储信息后将token给客户端
  • 客户端之后访问网站的时候只需要携带token即可,然后服务器通过预存的解密算法进行解密,如果能正常的解密出来,那就说明是正确的token,即放行请求,否则不通过
  • 即使是多台服务器,只需要进行相同的方法解密就行了

TOGyuz9qtZxvPDIJ5QIIVUCLYeX1QE

### 项目中如何使用
项目已经整合好了JWT,并且给我们提供了现有的工具类进行操作

com.mall.user.utils.JwtTokenUtils

如何使用现有的工具创建JWT呢?
```java
String token = JwtTokenUtils.builder().msg("xxx").build().creatJwtToken();

如何解析JWT呢?

String msg = JwtTokenUtils.builder().token(token).build().freeJwt();

JWT的具体的执行流程

  • 首先在登陆的时候进行用户名密码验证,如果通过了那就通过加密算法存储信息后将token给客户端
  • 客户端之后访问网站的时候只需要携带token即可,然后服务器通过预存的解密算法进行解密,如果能正常的解密出来,那就说明是正确的token,即放行请求,否则不通过
  • 即使是多台服务器,只需要进行相同的方法解密就行了
目录
相关文章
|
2月前
|
缓存 监控 API
淘宝 API 接口使用的技术要点与注意事项
在数字化商业环境中,淘宝API为开发者提供了强大的工具,用于与淘宝平台交互,获取商品信息及处理交易等。本文总结了正确使用API的关键技术要点:注册认证、理解接口文档、遵守调用限制、确保参数准确性、保护数据安全、处理异常、性能优化、版本兼容、合规性及日志监控,帮助开发者实现高效、安全的程序开发。
|
2月前
|
监控 安全 测试技术
深入理解后端技术中的API设计原则
在当今数字化时代,后端技术已成为构建高效、可扩展和安全应用程序的关键因素。本文将探讨后端开发中的API设计原则,包括RESTful架构、版本控制以及安全性等方面,旨在帮助开发者提升API设计的质量和用户体验。通过对这些原则的深入理解,可以更好地满足业务需求并提高系统的可靠性。
62 0
|
14天前
|
安全 物联网 API
API技术之身份认证
【10月更文挑战第17天】身份认证是API安全的核心,确保API可信可控。
API技术之身份认证
|
2月前
|
存储 中间件 API
ThinkPHP 集成 jwt 技术 token 验证
本文介绍了在ThinkPHP框架中集成JWT技术进行token验证的流程,包括安装JWT扩展、创建Token服务类、编写中间件进行Token校验、配置路由中间件以及测试Token验证的步骤和代码示例。
ThinkPHP 集成 jwt 技术 token 验证
|
28天前
|
JSON 前端开发 测试技术
API接口 |产品经理一定要懂的10%技术知识
作为产品经理,掌握约10%的技术知识对处理API相关工作至关重要。这包括理解API的基本概念及其作为数据交换的桥梁作用;熟悉JSON和XML两种主要数据格式及其特点;了解常见HTTP请求方法(GET、POST、PUT、DELETE)及响应状态码;关注API安全性,如认证授权和数据加密;掌握API版本管理和错误处理技巧;重视性能优化,以提升用户体验;参与API联调测试,确保稳定可靠;并与前后端团队紧密协作,选择合适的第三方API服务,推动产品高效开发。
|
1月前
|
存储 JSON 算法
JWT令牌基础教程 全方位带你剖析JWT令牌,在Springboot中使用JWT技术体系,完成拦截器的实现 Interceptor (后附源码)
文章介绍了JWT令牌的基础教程,包括其应用场景、组成部分、生成和校验方法,并在Springboot中使用JWT技术体系完成拦截器的实现。
53 0
JWT令牌基础教程 全方位带你剖析JWT令牌,在Springboot中使用JWT技术体系,完成拦截器的实现 Interceptor (后附源码)
|
2月前
|
安全 Java 数据安全/隐私保护
|
21天前
|
XML API 网络架构
API协议 的十种技术特点及适用场景
本文介绍了十种常见的API协议技术,包括REST、GraphQL、gRPC、SOAP、WebSocket、AMF和XML-RPC等,每种技术都有其特点和适用场景,如REST适用于轻量级Web服务开发,gRPC适合高性能分布式系统,而WebSocket则适用于需要低延迟交互的应用。
|
23天前
|
SQL Java API
深入探索Java的持久化技术——JPA(Java Persistence API)
【10月更文挑战第10天】深入探索Java的持久化技术——JPA(Java Persistence API)
15 0
|
23天前
|
Java API 数据库
深入探索Java的持久化技术——JPA(Java Persistence API)
【10月更文挑战第10天】深入探索Java的持久化技术——JPA(Java Persistence API)
28 0