前言

《玩转 SpringBoot 2 之整合 JWT 上篇》 中介绍了关于 JWT 相关概念和JWT 基本使用的操作方式。本文为 SpringBoot 整合 JWT 的下篇,通过解决 App 用户登录 Session 问题的实战操作,带你更深入理解 JWT。通过本文你还可以了解到如下内容:

  1. SpringBoot 使用拦截器的实际应用
  2. SpringBoot 统一异常处理
  3. SpringBoot 快速搭建 RESTful Api

关于生成JWT 操作请参考 《玩转 SpringBoot 2 之整合 JWT 上篇》

实战操作

登录操作

登录操作流程图:

登录操作流程介绍:

  1. App 根据用户名和密码访问登录接口。
  2. 如果用户名和密码错误则提示 App 用户密码输入错误。
  3. 如果用户名和密码正确则获取用户信息(表示登录成功)并根据用户信息生成 Token 并将其存入ServletContext 中。
  4. 将生成的 Token 返回给 App。

登录操作具体代码:

@RestController
public class LoginController {
Logger log = LoggerFactory.getLogger(LoginController.class);
@Autowired
private JWTService jwtService; @RequestMapping("/login")
public ReturnMessage<Object> login(String loginName,String password,HttpServletRequest request) {
if(valid(loginName,password)) {
ReturnMessageUtil.error(CodeEnum.LOGINNAMEANDPWDERROR);
} Map<String,String> userInfo = createUserInfoMap(loginName,password);
String token = jwtService.createToken(userInfo, 1); ServletContext context = request.getServletContext();
context.setAttribute(token, token);
log.info("token:"+token);
return ReturnMessageUtil.sucess(token);
}
} private Map<String,String> createUserInfoMap(String loginName, String password) {
Map<String,String> userInfo = new HashMap<String,String>();
userInfo.put("loginName", loginName);
userInfo.put("password", password);
return userInfo;
} private boolean valid(String loginName, String password) {
if(Objects.equal("ljk", loginName) && Objects.equal("123456", password) ) {
return true;
}
return false;
}

拦截操作

拦截操作流程图:

拦截操作流程介绍:

  1. 服务器获取 (App访问具体的Api 时携带的 Token)Token,如果 Token 为空则提示 App Token不能为空。

  2. 如果 Token 不为空则从 ServletContext 中获取 Token,如果不存在则提示 App 该Token为非法 Token !

  3. 如果 Token 不为空并且 ServletContext 中存在该Token,需要判断 Token 是否过期。如果未过期则放开拦截。

  4. 如果Token 已经过期则提示 App Token已经过期,需要重新登录。

拦截操作具体代码:

public class LoginInterceptor implements HandlerInterceptor{

	Logger log = LoggerFactory.getLogger(LoginInterceptor.class);

	private JWTService jwtService;

	public LoginInterceptor(JWTService jwtService) {
this.jwtService = jwtService;
} public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { log.info("Token Checkout processing");
String token = request.getParameter("token"); if (StringUtils.isEmpty(token)) {
throw new JKException(CodeEnum.TOKENISEMPTY);
} String tokenInServletContext = (String)request.getServletContext().getAttribute(token);
if(StringUtils.isEmpty(tokenInServletContext)) {
throw new JKException(CodeEnum.ILLEGALTOKEN);
} try {
jwtService.verifyToken(token);
} catch (AlgorithmMismatchException e) {
log.error("Token Checkout processing AlgorithmMismatchException 异常!"+e.getLocalizedMessage());
throw new JKException(CodeEnum.ILLEGALTOKEN);
}catch (TokenExpiredException e) {
log.info("token已经过期");
throw new JKException(CodeEnum.EXPIRETOKEN);
}catch (SignatureVerificationException e) {
log.error("Token Checkout processing SignatureVerificationException 异常!"+e.getLocalizedMessage());
throw new JKException(CodeEnum.ILLEGALTOKEN);
}catch (Exception e) {
log.error("Token Checkout processing 未知异常!"+e.getLocalizedMessage());
throw e;
} return true;
}
}

退出操作

退出操作流程介绍:

访问退出接口并传递登录生成的 Token,然后将 ServletContext中的 Token 删除。

退出操作具体代码:

	@GetMapping("/logout")
public ReturnMessage<?> logout(String token,String issuer,HttpServletRequest request) {
ServletContext context = request.getServletContext();
context.removeAttribute(token);
return ReturnMessageUtil.sucess();
}

公共代码

IndexController App 访问测试Api,具体代码如下:

@RestController
public class IndexController { @GetMapping("index")
public ReturnMessage index() {
return ReturnMessageUtil.sucess();
}
}

统一异常次处理的 Handle

@RestControllerAdvice
public class ExceptionHandle {
private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);
@ExceptionHandler(value = Exception.class)
//@ResponseBody
public ReturnMessage<Object> handle(HttpServletResponse response, Exception exception) {
response.setCharacterEncoding("utf-8");
if(exception instanceof JKException) {
JKException sbexception = (JKException)exception;
return ReturnMessageUtil.error(sbexception.getCode(), sbexception.getMessage());
}else {
logger.error("系统异常 {}",exception);
return ReturnMessageUtil.error(-1, "未知异常"+exception.getMessage());
}
}
}

JWTService 工具类 代码可以在我的GitHub上进行查看,具体地址请查看下面代码示例章节。

测试

这里使用PostMan 进行测试,当然你也可以选用你顺手的工具进行测试哈!

访问 http://localhost:8080/sbe/login?loginName=ljk&password=123456 进行登录获取Token,如下图所示date字段的值就是登录成功后生成的 Token。

访问 http://localhost:8080/sbe/index?token=具体token值,如下图所示访问成功!

如果不携带 Token 会提示Token不能为空,如下图所示:

如果输入不存在的 Token 则提示 非法Token!,如下图所示:

http://localhost:8080/sbe/logout?token=具体token值 进行退出,如下图所示:

退出后再次使用已经退出的Token 访问,会提示非法Token 如下图所示:

小结

登录操作通过 JWT 生成Token 返回给App,拦截操作(也可以理解成校验操作)通过拦截器(HandlerInterceptor)来进行实现。最后退出操作是通过将Token 保存ServletContent 中,退出其实就是将 Token 从 ServletContent 中删除。

本文主旨是通过简单实现,带你了解 App 认证过程处理方式,对于拦截部分你也可以通过 Filter 或 Aop 来进行实现。Token 存储也可以考虑使用Redis来实现,还有一个问题就是:JWT 续期问题本文并没有实现(JWT 过期时间延期问题)。这个部分就当成一个作业,欢迎大家在评论区说说你的解决方案?

代码示例

本文并没有对 JWTService 工具类、统一异常处理、拦截器使用搭建进行详细介绍,如果你想直接查看本文全部源码,请在我的GitHub 仓库 SpringbootExamples 中的 spring-boot-2.x-jwt 模块进行查看。

GitHub:https://github.com/zhuoqianmingyue/springbootexamples

同时你也可以通过查看我关于拦截器、统一异常处理、搭建 RESTful Api 详细教程总结自己完成相关的实现:

玩转 SpringBoot 2 之整合 JWT 下篇的更多相关文章

  1. 玩转 SpringBoot 2 之整合 JWT 上篇

    前言 该文主要带你了解什么是 JWT,以及JWT 定义和先关概念的介绍,并通过简单Demo 带你了解如何使用 SpringBoot 2 整合 JWT. 介绍前在这里我们来探讨一下如何学习一门新的技术, ...

  2. 玩转 SpringBoot 2 快速整合 | 丝袜哥(Swagger)

    概述 首先让我引用 Swagger 官方的介绍: Design is the foundation of your API development. Swagger makes API design ...

  3. 玩转 SpringBoot 2 快速整合拦截器

    概述 首先声明一下,这里所说的拦截器是 SpringMVC 的拦截器 HandlerInterceptor.使用SpringMVC 拦截器需要做如下操作: 创建拦截器类需要实现 HandlerInte ...

  4. 玩转 SpringBoot 2 快速整合 | FreeMarker篇

    FreeMarker 介绍 Apache FreeMarker™是一个模板引擎:一个Java库,用于根据模板和更改数据生成文本输出(HTML网页,电子邮件,配置文件,源代码等).模板是用FreeMar ...

  5. 玩转 SpringBoot 2 快速整合 | JSP 篇

    前言 JavaServer Pages(JSP)技术使Web开发人员和设计人员能够快速开发和轻松维护利用现有业务系统的信息丰富的动态Web页面. 作为Java技术系列的一部分,JSP技术可以快速开发独 ...

  6. 玩转 SpringBoot 2 快速整合 | Thymeleaf 篇

    前言 Thymeleaf是一个适用于Web和独立环境的现代服务器端Java模板引擎. Thymeleaf的主要目标是为您的开发工作流程带来优雅的自然模板 - 可以在浏览器中正确显示的HTML,也可以用 ...

  7. 玩转 SpringBoot 2 快速整合 Filter

    概述 SpringBoot 中没有 web.xml, 我们无法按照原来的方式在 web.xml 中配置 Filter .但是我们可以通过 JavaConfig(@Configuration +@Bea ...

  8. 教你 Shiro + SpringBoot 整合 JWT

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

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

    什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点 ...

随机推荐

  1. Mac迅雷瘦身精简教程

    迅雷是个大家很熟悉的工具了,尽管吐槽的人不少,但相信大家也都是口嫌体直,边骂边用. 其实 macOS 版迅雷在界面上,相比于 Windows 的客户端来说,已经很克制了,但有些功能仍然对用户造成了干扰 ...

  2. openSession 与 getCurrentSession的区别

    1.openSession 每一次获得的是一个全新的session对象,而getCurrentSession获得的是与当前线程绑定的session对象 package cn.kiwifly.view; ...

  3. [转]Linux系统结构

    Linux系统一般有4个主要部分:内核.shell.文件系统.应用程序. 内核.shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序.管理文件并使用系统. 1.linux内核 内 ...

  4. 听说你不会调参?TextCNN的优化经验Tricks汇总

    前言:本篇是TextCNN系列的第三篇,分享TextCNN的优化经验 前两篇可见: 文本分类算法TextCNN原理详解(一) TextCNN代码详解(附测试数据集以及GitHub 地址)(二) 调优模 ...

  5. HDU-4857-逃生-反向拓扑排序+优先队列

    HDU-4857 题意就是做一个符合条件的排序,用到拓扑序列. 我一开始wa了多发,才发现有几个样例过不了,发现1->2->3...的顺序无法保证. 后来就想用并查集强连,还是wa: 后来 ...

  6. luogu- P1373 小a和uim之大逃离 DP 四维,其中一维记录差值

    P1373 小a和uim之大逃离: https://www.luogu.org/problemnew/show/P1373 题意: 在一个矩阵中,小A和小B轮流取数,小A可以从任意点先取,小B后取,最 ...

  7. CodeForces 507E Breaking Good 2维权重dij

    Breaking Good 题解:         2维权重dij, 先距离最短, 后改变最小.   在这个题中, 如果要改变最小, 则让更多的可用边放进来. 然后可以用pre存下关键边.   代码: ...

  8. yzoj P1948 取数字问题

    题意 sb题目,不多说,爆搜就能过. 代码 #include<bits/stdc++.h> using namespace std; int n,m,ans=1<<30,a[1 ...

  9. 《Hive编程指南》读书笔记 | 一文看懂Hive的数据类型和文件格式

    Hive支持关系型数据库中的大多数基本数据类型,同时也支持关系型数据库中很少出现的3种集合数据类型. 和大多数数据库相比,Hive具有一个独特的功能,那就是其对于数据在文件中的编码方式具有非常大的灵活 ...

  10. CSS3 03. 3D变换、坐标系、透视perspective、transformZ、transform-style添加3D效果、backface-visibility元素背面可见、动画animation、@keyfarmes、多列布局

    1.左手坐标系 伸出左手,让拇指和食指成“L”形,大拇指向右,食指向上,中指指向前方.这样我们就建立了一个左手坐标系,拇指.食指和中指分别代表X.Y.Z轴的正方向.如下图 CSS中的3D坐标系 CSS ...