# spring-jwt **Repository Path**: heyuanmei/spring-jwt ## Basic Information - **Project Name**: spring-jwt - **Description**: spring boot 整合jwt - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-02-14 - **Last Updated**: 2023-05-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 1. 技术学习笔记 ## 一. jwt: 鉴权框架 #### 简介 (json web token)轻量级的鉴权框架、无状态登录(有状态:信息存在服务器端(**session**),无状态:信息存在客户端(**cookie**)) 好处是:不存储在服务端,减少服务端的压力。 > - 各方之间以JSON对象安全地传输信息。 > > - 这些信息可以通过数字签名进行验证和信任。 > > - 使用密匙(使用HMAC算法或使用RSA的公钥/私钥)对来对JWT进行签名。 #### JWT的认证流程 ![](src/main/resources/img/image-20221014102234202.png) ```consel - 用户携带用户名和密码请求访问 - 服务器校验用户凭据 - 应用提供一个token给客户端 - 客户端存储token,并且在随后的每一次请求中都带着它 - 服务器校验token并返回数据 ``` #### Token的结构 - 头部(Header) - 载荷(playload) - 签名(signature) 1. **Header** token是什么类型,采用了何种加密算法;头部有两部分信息:令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256或RSA。 ```consel { "typ": "JWT", "alg": "HS256"} ``` 2. **playload** 存放一些不敏感的用户信息,用来传递数据。官方提供的几个标准字段,同时也可以自己往里面加自定义的字段和内容。类似一个Map集合。jwt的标准定义包含五个字段: - `iss`:该JWT的签发者 - `sub `: 该JWT所面向的用户 - `aud` : 接收该JWT的一方 - `exp(expires)` : 什么时候过期,这里是一个Unix时间戳 - `iat(issued at)`: 在什么时候签发的 3. **signature** 主要是将`header`和`payload`的`base64`编码后内容用**点拼接**在一起然后进行加密生成签名。 服务端需要利用这签名来校验token是否被篡改(验签),是JWT最后一个部分。 该部分是使用了header指定的算法(如:HS256)加密后的数据,包含三个部分: - `header `(base64加密后的) - `payload `(base64加密后的) - `secret `私钥 ```java secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该泄露出去。 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。 为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。 ``` ## 二. shiro ![输入图片说明](src/main/resources/img/%E5%9B%BE%E7%89%87.png) 有状态访问的缺点: 1、服务器压力增大 通常session是存储在内存中的,每个用户通过认证之后都会将session数据保存在服务器的内存中,而当用户量增大时,服务器的压力增大。 2、CSRF跨站伪造请求攻击 session是基于cookie进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。 3、扩展性不强 如果将来搭建了多个服务器,虽然每个服务器都执行的是同样的业务逻辑,但是session数据是保存在内存中的(不是共享的),用户第一次访问的是服务器1,当用户再次请求时可能访问的是另外一台服务器2,服务器2获取不到session信息,就判定用户没有登陆过。 ## 三. spring security 安全框架 #### 定义 - Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。 - Spring 是非常流行和成功的 Java 应用开发框架,SpringSecurity 正是 Spring 家族中的成员。SpringSecurity 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。 spring Security就是由多个过滤器组成的过滤器链,多个过滤器之间各司其职,都有各自的功能。 #### 核心功能 核心功能是用户认证(Authentication)和用户授权(Authorization)两个部分。 - 用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。 - 用户授权指的是验证某个用户是否有权限执行某个操作。 image-20221011220355760 #### 原理 - 过滤器: ```consel 1.[org.springframework.security.web.context.SecurityContextPersistenceFilter] 此过滤器主要是在 SecurityContextRepository 中 保存或者更新 SecurityContext,并交给后续的过滤器操作。 而 SecurityContext 中保存了当前用户认证、权限等信息。 2.【org.springframework.security.web.csrf.CsrfFilter】 此过滤器用于防止 CSRF 攻击。Spring Security 4.0 开始,默认开启 CSRF 防护,针对 PUT、POST、DELETE 等请求进行防护。 注: CSRF 指的是 Cross Site Request Forgery,即 跨站请求伪造。 简单理解为:用户登录一个网站 A,并打开了另一个网站 B,B 网站携带恶意代码 且使用用户身份去访问 网站 A。 3.【org.springframework.security.web.authentication.logout.LogoutFilter】 匹配 URL(默认为 /logout),用于实现用户退出并清除认证信息。 4.【org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter】 匹配 URL(默认为 /login),用于实现用户登录认证操作(必须为 POST 请求)。 5.【org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter】 若没有指定登录认证界面,此过滤器会提供一个默认的界面。 【org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter】 若没有指定登出界面,此过滤器会提供一个默认的界面。 6.【org.springframework.security.web.authentication.AnonymousAuthenticationFilter】 创建一个匿名身份,用于系统的访问。(兼容游客登录模式) 7.【org.springframework.security.web.access.ExceptionTranslationFilter】 位于整个 springSecurityFilterChain 过滤链后方,用于处理链路中的异常(跳转到指定页面或者返回错误信息)。 8.【org.springframework.security.web.access.intercept.FilterSecurityInterceptor】 获取资源访问的授权信息,根据 SecurityContext 中存储的用户信息来决定操作是否有权限。 ``` #### 认证授权的决策流程 > 一个请求过来时通过各个过滤器,最后通过FilterSecurityInterceptor来判断这个请求url是否是不需要验证的,如果是就直接访问到我们的接口api。如果不是的话,再判断当前请求线程中是否有authentication的认证对象,如果有就放行,如果没有就返回登录页面(比如这里我们设置的是登录表单的方式),来到登录页面输入账号密码登录后就会来到 UsernamePasswordAuthenticationFilter,经过一系列的操作,最后验证成功就会把认证对象authentication放进securityContext中,然后FilterSecurityInterceptor判断到当前请求线程中这个认证对象就放行,返回的时候最后会通过securityContextpersistenceFilter,判断当前线程是否有securityContext,如果有就放进session,那么下次再请求这个url的时候会首先通过securityContextpersistenceFilter这个过滤器,判断session中是否有securityContextduxiiang,如果有就放进当前请求线程中,然后最后经过FilterSecurityInterceptor时再判断当前请求线程是否有认证对象,由于最前面经过securityContextpersistenceFilter,已经从session中把认证对象放进了当前请求线程中,所以FilterSecurityInterceptor会直接放行,这样就访问到我们的接口api。 ## 四. mybatis-pluse ### 1. 优势 **强大的 CRUD 操作**:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求。 **支持 Lambda 形式调用**:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错。 **支持主键自动生成**:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题。 **支持 ActiveRecord 模式**:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作 **支持自定义全局通用操作**:支持全局通用方法注入( Write once, use anywhere )。 **内置代码生成器**:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用。 **内置分页插件**:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询。 **分页插件支持多种数据库**:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库。 **内置性能分析插件**:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询。 **内置全局拦截插件**:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作。 ### 2. Service 的CRUD接口 - ### Save ```java // 插入一条记录(选择字段,策略插入) boolean save(T entity); // 插入(批量) boolean saveBatch(Collection entityList); // 插入(批量) boolean saveBatch(Collection entityList, int batchSize); ``` - ### SaveOrUpdate ```java // TableId 注解存在更新记录,否插入一条记录 boolean saveOrUpdate(T entity); // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法 boolean saveOrUpdate(T entity, Wrapper updateWrapper); // 批量修改插入 boolean saveOrUpdateBatch(Collection entityList); // 批量修改插入 boolean saveOrUpdateBatch(Collection entityList, int batchSize); ``` - ### Remove ```java // 根据 entity 条件,删除记录 boolean remove(Wrapper queryWrapper); // 根据 ID 删除 boolean removeById(Serializable id); // 根据 columnMap 条件,删除记录 boolean removeByMap(Map columnMap); // 删除(根据ID 批量删除) boolean removeByIds(Collection idList); ``` - ### Update ```java // 根据 UpdateWrapper 条件,更新记录 需要设置sqlset boolean update(Wrapper updateWrapper); // 根据 whereWrapper 条件,更新记录 boolean update(T updateEntity, Wrapper whereWrapper); // 根据 ID 选择修改 boolean updateById(T entity); // 根据ID 批量更新 boolean updateBatchById(Collection entityList); // 根据ID 批量更新 boolean updateBatchById(Collection entityList, int batchSize); ``` - ### Get ```java // 根据 ID 查询 T getById(Serializable id); // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1") T getOne(Wrapper queryWrapper); // 根据 Wrapper,查询一条记录 T getOne(Wrapper queryWrapper, boolean throwEx); // 根据 Wrapper,查询一条记录 Map getMap(Wrapper queryWrapper); // 根据 Wrapper,查询一条记录 V getObj(Wrapper queryWrapper, Function mapper); ``` - ### List ```java // 查询所有 List list(); // 查询列表 List list(Wrapper queryWrapper); // 查询(根据ID 批量查询) Collection listByIds(Collection idList); // 查询(根据 columnMap 条件) Collection listByMap(Map columnMap); // 查询所有列表 List> listMaps(); // 查询列表 List> listMaps(Wrapper queryWrapper); // 查询全部记录 List listObjs(); // 查询全部记录 List listObjs(Function mapper); // 根据 Wrapper 条件,查询全部记录 List listObjs(Wrapper queryWrapper); // 根据 Wrapper 条件,查询全部记录 List listObjs(Wrapper queryWrapper, Function mapper); ``` - ### Page ```java // 无条件分页查询 IPage page(IPage page); // 条件分页查询 IPage page(IPage page, Wrapper queryWrapper); // 无条件分页查询 IPage> pageMaps(IPage page); // 条件分页查询 IPage> pageMaps(IPage page, Wrapper queryWrapper); ``` ### 3. Mapper的CRUD与Service类似 ## 五. swagger 1. 接口文档地址:http://localhost:8080/swagger-ui/#/ 2. 接口文档肖利民设计的接口文档(比较美观):http://localhost:8080/doc.html#/ ## 六. spring boot spring boot项目换镜像:https://start.aliyun.com 创建spring boot项目速度快。