上一个项目采用的是session存储uid的方式来确认API调用方的合法身份,这次的app则打算采用token的方式进行身份校验,此二者的优缺点有很多博客进行了论述,这里就不谈了。

本文中的代码以及部分内容参考自
SpringBoot整合Token
原创lamarsan
————————————————
版权声明:本文为CSDN博主「lamarsan」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_16410733/article/details/96625801

这里对JWT进行简单的分析,并解释token中存储的具体内容。

JWT消息构成

一个token分3部分,按顺序为

  • 头部(header)
  • 载荷(payload)
  • 签名(signature)

三部分之间用“.”号做分隔。例如
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

header头部

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

(如果有需要的话可以自己实现算法并修改com.auth0.jwt的源码以提高安全性)

payload荷载

载荷就是存放有效信息的地方。jwt提供了一部分预设的属性,也可以向claim中添加自定义属性。
其中标准提供的数据为

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
  • 自定义属性存放在claim中

体现在代码中:

        token= JWT.create().withClaim("userId",user.getUserId())          //自定义属性.withExpiresAt(DateUtil.rollMon(new Date(),1))  //一个月后过期.sign(Algorithm.HMAC256(SECRET));                //指定加密算法并传入密钥

签名signature

jwt的第三部分是一个签证信息,这个签证信息算法如下:

  1. 头部、荷载分别进行base64加密后,以”.”连接获得content

  2. 按照指定的加密算法(是携带密钥的)将content进行加密得到signatureBytes

  3. signatureBytes再进行base64加密 获得signature

  4. 将content和signature以”.”连接获得token。

    分析一下:
    token的三段明文是可以直接base64解密的,如:

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 .eyJhdWQiOiIxMjMiLCJwYXNzd29yZCI6IjEyMyIsInVzZXJJZCI6MjMzfQ .0Piz_kzhriwwcoHI7o5A7evyeDMDGBXStK5PxTtmadQ
    

第一段解密可知第三段的加密算法类型。

第二段解密可知所有默认字段以及自定义字段,所以这里绝对不能明文传输一些敏感信息。

第三段解密后将得到signatureBytes。

如果不知道密钥,是无法通过header中指定的加密算法将token前两段映射到第三段的。
也就是说,如果不知道这个密钥,那么即使截获了token,得知前两段的内容,都没有办法伪造出能够通过服务器token校验的signature,JWT就是通过这一特性来确保签名的有效性。

如果校验通过了,说明这个签名确实是我们的服务器下发给客户端的,携带该token的用户具有合法的身份,根据payload中的信息,就能确认他的登陆身份。所以我们只需要在token的payload中放置一个userId即可(必要的话还要添加一个过期时间)。

下面上代码

依赖:`

    <dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency>`

创建token的工具类

public class TokenUtil {public static String SECRET="你的密钥";public static String getToken(User user) {String token="";token= JWT.create().withClaim("userId",user.getUserId())           //自定义属性.withExpiresAt(DateUtil.rollMon(new Date(),1))  //一个月后过期.sign(Algorithm.HMAC256(SECRET));                //指定加密算法并传入密钥return token;}
}

自定义注解—跳过token验证

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {boolean required() default true;
}

自定义注解—需要token验证

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {boolean required() default true;
}

拦截器

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.poorteam.poorguy.webservice.common.anotations.PassToken;
import com.poorteam.poorguy.webservice.common.anotations.UserLoginToken;
import com.poorteam.poorguy.webservice.repository.datasourse1.UserMapper;
import com.poorteam.poorguy.webservice.utils.TokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;public class AuthenticationInterceptor implements HandlerInterceptor {@AutowiredUserMapper userMapper;@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token// 如果不是映射到方法直接通过if (!(object instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) object;Method method = handlerMethod.getMethod();//检查是否有passtoken注释,有则跳过认证if (method.isAnnotationPresent(PassToken.class)) {PassToken passToken = method.getAnnotation(PassToken.class);if (passToken.required()) {return true;}}//检查有没有需要用户权限的注解if (method.isAnnotationPresent(UserLoginToken.class)) {UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);if (userLoginToken.required()) {// 执行认证if (token == null) {throw new RuntimeException("无token,请重新登录");}// 获取 token 中的 user idString userId;try {userId = JWT.decode(token).getClaim("userId").asString();} catch (JWTDecodeException j) {throw new RuntimeException("401");}if(userId==null||userId.length()==0){throw new RuntimeException("userId为空");}// 验证 tokenJWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(TokenUtil.SECRET)).build();try {jwtVerifier.verify(token);      //这里一旦出现异常就说明校验不通过//把uid放到request中去httpServletRequest.setAttribute("userId",Long.valueOf(userId));} catch (JWTVerificationException e) {throw new RuntimeException("401");}return true;}}return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Object o, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Object o, Exception e) throws Exception {}
}

配置拦截器

/*** 配置拦截器*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");}@Beanpublic AuthenticationInterceptor authenticationInterceptor() {return new AuthenticationInterceptor();}
}

在controller层根据需求给接口添加注解

 @PassToken  //注册接口 跳过token验证@RequestMapping(value = "sendRegisterValidateCode", method = RequestMethod.POST)BaseResponse sendRegisterValidateCode(@RequestBody JSONObject jsonObject) {//获取参数String phone = jsonObject.getString("phone");   //手机号//校验参数if(!phone.matches("^[1](([3][0-9])|([4][5-9])|([5][0-3,5-9])|([6][5,6])|([7][0-8])|([8][0-9])|([9][1,8,9]))[0-9]{8}")){return BaseResponse.createByErrorMessage("手机号无效");}return loginServiceImpl.sendRegisterValidateCode(phone);}

收工!

SpringBoot整合JWT实现API身份校验相关推荐

  1. SpringBoot 整合JWT实现基于自定义注解的-登录请求验证拦截(保姆级教学,附:源码)

    学习目标: Spring Boot 整合JWT实现基于自定义注解的 登录请求接口拦截 例: 一篇掌握 JWT 入门知识  1.1 在学习SpringBoot 整合JWT之前,我们先来说说JWT进行用户 ...

  2. springboot整合jwt

    JWT(Json Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准.JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取 ...

  3. SpringBoot整合JWT实现登录认证

    目录 什么是JWT 为什么使用JWT JWT的使用场景 JWT的结构 JWT的请求流程 SpringBoot整合JWT 引入依赖 编写配置文件 token生成与验证工具类 拦截器拦截token 设置拦 ...

  4. 教你 Shiro + SpringBoot 整合 JWT

    本篇文章将教大家在 shiro + springBoot 的基础上整合 JWT (JSON Web Token) 如果对 shiro 如何整合 springBoot 还不了解的可以先去看我的上一篇文章 ...

  5. SpringBoot 整合 JWT 实现 Token 验证

    前言 在Spring Security整合oauth2实现认证token也不满足实际生产需求的时候,可以整合Jwt实现token认证,完全手写获取token,认证token的方法. Maven依赖包 ...

  6. Springboot整合ES8(Java API Client)

    在 Elasticsearch7.15版本之后,Elasticsearch官方将它的高级客户端 RestHighLevelClient标记为弃用状态.同时推出了全新的 Java API客户端 Elas ...

  7. 使用JWT进行用户身份校验(基于token)

    jwt的其他资料: https://baijiahao.baidu.com/s?id=1608021814182894637&wfr=spider&for=pc https://www ...

  8. Springboot整合shiro基于url身份认证和授权认证

    你还不会shiro吗? 前奏 shiro核心配置文件(rolesFilter可选). 身份认证 多表登录源如何操作? 授权管理 如何解决界面多角色/资源问题 访问效果 权限管理在日常开发中很重要,所以 ...

  9. SpringBoot整合Swagger测试api构建

    @Author:SimpleWu 什么是Swagger? Swagger是什么:THE WORLD'S MOST POPULAR API TOOLING 根据官网的介绍: Swagger Inspec ...

最新文章

  1. EasyStack华丽亮相OpenStack Days China
  2. java生成横向的pdf,java – 如何生成横向跨越多个页面的PDF报告
  3. 【PC工具】在线MP3转WAV工具,MP3文件转换成arduino可以直接播放的wav格式
  4. USACO 6.1.3 Cow XOR
  5. 牛客网刷题(纯java题型 31~60题)
  6. plsql 连接oralce数据库,报ora 12557 tns 协议适配器不可加载错误
  7. mybatis 存储过程 tmp_count_mysql存储过程(一)-navicat与mybatis
  8. Linux-IPC进程间通信(day11)
  9. MSAgent 详细解说(上)
  10. Veritas Backup Exec 21配置存储
  11. SNMP-简单网络管理协议
  12. arcgis使用教程和视频教程
  13. tcp服务器修改ip,修改本地连接的TCP/IP属性设置
  14. 百度信息流投放效果不稳定,意图词要怎么筛选,先测试词包还是先测试创意好?
  15. 旋转矩阵(维基百科)
  16. mysql查询表升序降序_创建一个按钮,对MYSQL查询进行升序和降序排序
  17. 两种三角形的打印方法
  18. 全志H3停产,A40I/T3更胜一筹--CoM-X40I核心模块来了
  19. vue组件内容不显示
  20. java heap 参数_java heap space解决方法和JVM参数设置--- JVM参数设置和程序优化篇

热门文章

  1. 下载链接后的MD5是什么意思?
  2. 昆仑通态(MCGS)样板程序,包含历史实时报表查询导出、画面动画风扇转动
  3. 2022.04.08_数据结构—左程云02
  4. 求一段看不懂的乱码_求一对乱七八糟表示废话的符号!急急急急急急!
  5. 51单片机驱动HMI串口屏,串口屏的下载方式
  6. 分享网页文章到微信时如何自定义缩略图、链接、标题和摘要
  7. 1080P、720P、4CIF、CIF所需要的理论带宽和工程中实际带宽及存储容量
  8. TB6600步进电机驱动(包含原理图以及PCB,打样测试可用,性能良好)
  9. ROS2GO之慕课《机器人操作系统入门》配置与使用(2018-2019-2)
  10. 五个不错的样机素材网站推荐