jwt 介绍就不多说了,下面通过代码演示开发过程中jwt 的使用。

(1)在pom.xml中引入对应的jar

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

(2)引入jwt 工具类:token的生成以及获取对应的token信息

/**
* @author : wl
* @Description :
* @date : 2020/7/3 13:25
*/
public class JwtUtil {

public static final String AUTHORIZATION_SECRET = "wlcoder";
private static final String UID = "uid";
private static final String USERNAME = "username";
private static final String PASSWORD = "password";
private static final String STATUS = "status";

//创建秘钥
public static Key getKeyInstance() {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] bytes = DatatypeConverter.parseBase64Binary(AUTHORIZATION_SECRET);
return new SecretKeySpec(bytes, signatureAlgorithm.getJcaName());
}

/**
* 生成token的方法
*
* @param user
* @param expire
* @return
*/
public static String generatorToken(SysUser user, int expire) {
return Jwts.builder().claim(UID, user.getId())
.claim(USERNAME, user.getUsername())
.claim(PASSWORD, user.getPassword())
.claim(STATUS, user.getStatus())
.setExpiration(DateTime.now().plusSeconds(expire).toDate())
.signWith(SignatureAlgorithm.HS256, getKeyInstance())
.compact();
}

/**
* 根据token获取token中的信息
*
* @param token
* @return
*/
public static SysUser getTokenInfo(String token) {
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return SysUser.builder().id((Integer) claims.get(UID))
.username((String) claims.get(USERNAME))
.password((String) claims.get(PASSWORD))
.status((Integer) claims.get(STATUS))
.build();

}
}

(3)添加注解 @NeedToken,@SkipToken ,加在方法上灵活处理对应的请求

/**
* @author : wl
* @Description : 需要token 验证
* @date : 2020/7/3 11:40
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedToken {
boolean required() default true;
}
/**
* @author : wl
* @Description :跳过token 验证
* @date : 2020/7/3 11:39
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SkipToken {
boolean required() default true;
}

(4)添加拦截器,验证前端请求是否需要token

/**
* @author : wl
* @Description :方法请求拦截
* @date : 2020/7/3 11:47
*/
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
private SysUserService userService;
@Autowired
private RedisUtil redisUtil; @Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws ServletException, IOException {
String token = httpServletRequest.getHeader("token");
if (!(object instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//检查有没有跳过token的注解
if (method.isAnnotationPresent(SkipToken.class)) {
SkipToken skipToken = method.getAnnotation(SkipToken.class);
if (skipToken.required()) {
log.info("该请求无须token验证。。。");
return true;
}
}
//检查有没有需要token的注解
if (method.isAnnotationPresent(NeedToken.class)) {
NeedToken needToken = method.getAnnotation(NeedToken.class);
if (needToken.required()) {
log.info("该请求需要token验证。。。");
if (Objects.isNull(token)) {
throw new BaseException("无token,请重新登录");
}
try {
JwtUtil.getTokenInfo(token);
} catch (ExpiredJwtException e) {
throw new BaseException("token超时");
}
// SysUser user = userService.findUser(sysUser.getUsername(), sysUser.getPassword());
// if (Objects.isNull(user)) {
// throw new BaseException("用户不存在,请重新登录");
// }
if (!Objects.equals(token, redisUtil.get("ms_notify_token"))) {
throw new BaseException("token异常,请重新登录");
}
}
}
return true;
} @Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
} @Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}

(5)添加拦截器异常处理 :出现异常直接跳转到登录页面 ,这里有个坑,前端为ajax请求时候 使用转发或者重定向会失效。因此处理为 判断请求为ajax请求则设置返回一个状态码如:httpServletResponse.setStatus(666); 前端jquery.js中统一判断处理。

/**
* @author : wl
* @Description : 拦截异常处理
* @date : 2020/7/5 15:30
*/
@Slf4j
public class MyWebHandlerException implements HandlerExceptionResolver {
@SneakyThrows
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
log.info("请求出现异常:" + e.getMessage());
e.printStackTrace();
// return new ModelAndView("redirect:/login");
ModelAndView modelAndView = new ModelAndView();
String type = httpServletRequest.getHeader("X-Requested-With");
if (Objects.equals(type, "XMLHttpRequest")) {
//是ajax请求
httpServletResponse.setStatus(666);
httpServletResponse.setContentType("text/javascript; charset=utf-8");
httpServletResponse.getWriter().write(e.getMessage());
return modelAndView;
} else {
modelAndView.setViewName("/login");
return modelAndView;
}
}
}

jquery.js中添加对应状态码处理:

//自定义异常信息: 跳转到登录页面
jQuery.ajaxSetup({
statusCode:{
666:function(data){
alert(data.responseText);
window.location.href="/login";
}
}
})

(6) 添加webConfig配置 实现WebMvcConfigurer,引入自定义的token拦截以及异常处理

/**
* @author : wl
* @Description :web拦截器
* @date : 2020/7/3 11:46
*/
@Configuration
public class webConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");
} @Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
} @Bean
public WebMvcConfigurer webMvcConfigurer() {
WebMvcConfigurer adapter = new WebMvcConfigurer() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
}
};
return adapter;
}
/*
* 异常拦截处理
* */
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(new MyWebHandlerException());
}
}

到这里基本配置基本差不多了,web应用中怎么处理呢?例如登录功能:

登录后端代码:

/**
* @author : wl
* @Description :
* @date : 2020/7/2 16:46
*/
@Controller
public class Login {
@Autowired
private SysUserService sysUserService;
@Autowired
private RedisUtil redisUtil; @SkipToken
@RequestMapping("/login")
public String toLogin() {
System.out.println("跳转到登录页面");
return "login";
} @SkipToken
@RequestMapping("/index")
public String toIndex() {
System.out.println("跳转到主页面");
return "index";
} @SkipToken
@ResponseBody
@RequestMapping("/loginIn")
public ResultUtil loginIn(String username, String password) {
try {
SysUser user = sysUserService.findUser(username, password);
if (null != user) {
user.setPassword(password);
String token = JwtUtil.generatorToken(user, 60*60);
//token 保存在redis中
redisUtil.set("ms_notify_token", token);
return ResultUtil.ok().data("msg", token).message("登录成功");
} else {
return ResultUtil.error().data("msg", "error").message("用户不存在");
}
} catch (BaseException e) {
return ResultUtil.error().data("msg", e.getMessage()).message("登陆失败");
}
} }

登录前端代码:这里需要注意的是 配置window.location.href 不生效 需要检查是否设置为form 表单。

前端接受到token存储在localStorage: localStorage.setItem("token",data.data.msg);

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>登录</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/signin.css" rel="stylesheet">
</head>
<body class="text-center">
<div class="form-signin">
<img class="mb-4" th:src="@{img/my.svg}" alt="" width="80" height="80">
<h1 class="h3 mb-3 font-weight-normal">请登录</h1>
<label class="sr-only">用户名</label>
<input type="text" name="username" id="username" class="form-control" th:placeholder="用户名" required="required" autofocus="">
<p class=""></p>
<label class="sr-only">密码</label>
<input type="password" name="password" id="password" class="form-control" th:placeholder="密码" required="required" >
<button class="btn btn-lg btn-primary btn-block" th:onclick="loginIn()">登录</button>
</div>
</body> <script type="text/javascript" src="/js/jquery-3.5.1.js"></script>
<script type="text/javascript" src="/js/bootstrap.min.js"></script>
<script>
function loginIn() {
debugger;
var username = $('#username').val();
var password = $('#password').val();
if(""==username || ""==password){
alert("用户信息不完整,请检查!");
return;
}
$.ajax({
url: '/loginIn',
type: "post",
data: {'username': username, 'password': password},
success: function (data) {
if (data.success) {
//alert(data.message);
localStorage.setItem("token",data.data.msg);
location.href = "/index"; // window.location.href 不生效 检查是否为form 表单
} else {
alert(data.message)
}
},
error: function () {
alert("登录失败")
}
});
}
</script>
</html>

若是需要token验证,前端对应的ajax 请求需要加上headers 如:

  //禁用,启用
function disable_config(nid, status) {
$.ajax({
url: '/notify/updateStatus',
data: {'nid': nid, 'status': status},
type: "post",
headers: {"token": localStorage.getItem("token")},
success: function (data) {
if (data.success) {
location.reload();
alert(data.message);
} else {
alert(data.message + ":" + data.data.msg)
}
}
});
}

后端对应方法上需要添加注解@NeedToken  如:

   /**
* 禁用 、启用
*/
@NeedToken
@ResponseBody
@SysLogAnnotation("禁用 、启用 配置")
@RequestMapping(value = "/updateStatus")
public ResultUtil updateStatus(HttpServletRequest request, String nid, int status) {
String config_status = (status == 1 ? "启用" : "禁用");
try {
notifyConfigService.updateStatus(nid, status);
} catch (BaseException e) {
return ResultUtil.error().data("msg", e.getMessage()).message(config_status + "配置失败");
}
return ResultUtil.ok().data("msg", "success").message(config_status + "配置成功");
}

设计流程基本如上述代码所示,详细代码可以参考 github地址 : https://github.com/wlcoder/ms-notify

该项目主要是实现自动配置定时发送邮件信息,如果觉得不错,可以star一下。有什么意见或者建议,可以指出来,大家相互学习,谢谢!

SpringBoot整合JWT实战详解的更多相关文章

  1. Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例

    摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!   “看看星空,会觉得自己很渺小,可能我们在宇宙中从来就是一个偶然.所以,无论什么事情,仔细想一 ...

  2. SpringBoot配置文件 application.properties详解

    SpringBoot配置文件 application.properties详解   本文转载:https://www.cnblogs.com/louby/p/8565027.html 阅读过程中若发现 ...

  3. Springboot整合log4j2日志全解

    目录 常用日志框架 日志门面slf4j 为什么选用log4j2 整合步骤 引入Jar包 配置文件 配置文件模版 配置参数简介 Log4j2配置详解 简单使用 使用lombok工具简化创建Logger类 ...

  4. SPRINGBOOT注解最全详解(

    #     SPRINGBOOT注解最全详解(整合超详细版本)          使用注解的优势:               1.采用纯java代码,不在需要配置繁杂的xml文件           ...

  5. 前后端分离,简单JWT登录详解

    前后端分离,简单JWT登录详解 目录 前后端分离,简单JWT登录详解 JWT登录流程 1. 用户认证处理 2. 前端登录 3. 前端请求处理 4. 后端请求处理 5. 前端页面跳转处理 6. 退出登录 ...

  6. 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING

    <Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th  2014 Email:skyseraph00@163.com 更多精彩请直接 ...

  7. 011-Scala中的apply实战详解

    011-Scala中的apply实战详解 object中的apply方法 class中的apply方法 使用方法 apply方法可以应用在类或者Object对象中 class类 必须要创建实例化的类对 ...

  8. 010-Scala单例对象、伴生对象实战详解

    010-Scala单例对象.伴生对象实战详解 Scala单例对象详解 函数的最后一行是返回值 子项目 Scala伴生对象代码实战 object对象的私有成员可以直接被class伴生类访问,但是不可以被 ...

  9. 008-Scala主构造器、私有构造器、构造器重载实战详解

    008-Scala主构造器.私有构造器.构造器重载实战详解 Scala主构造器实战 无参数的主构造器 分析 1.name 需要赋初值,一般通过占位符来代表空值 2.private 声明私有的age 生 ...

随机推荐

  1. vscode 插件保存记录

  2. K8S节点异常怎么办?TKE"节点健康检查和自愈"来帮忙

    节点健康检测 意义 在K8S集群运行的过程中,节点常常会因为运行时组件的问题.内核死锁.资源不足等各种各样的原因不可用.Kubelet默认对节点的PIDPressure.MemoryPressure. ...

  3. selenium--数据填充

    from time import sleep from selenium import webdriver br = webdriver.Chrome() url = "https://ww ...

  4. 【事件中心 Azure Event Hub】使用Logstash消费EventHub中的event时遇见的几种异常(TimeoutException, ReceiverDisconnectedException)

    问题描述 使用EFK(Elasticsearch, Fluentd and Kibana)在收集日志的解决方案中, 可以先把日志发送到EventHub中,然后通过Logstash消费EventHub中 ...

  5. DRF (学习第一部)

    目录 Web应用模式 API接口 RESTful API 规范 序列化 Django Rest_Framework 环境安装预与配置 序列化器 -Serializer Web应用模式 在开发web应用 ...

  6. Django (学习第二部 ORM 模型层)

    Django对数据库的操作 Django的 ORM 简介 ORM操作 (增删改查) ORM操作数据库的增删改查 ORM创建表关系 ORM中常用字段及参数 数据库的查询优化 ORM中如何开启事务 ORM ...

  7. 跨站资源共享CORS原理深度解析

    我相信如果你写过前后端分离的web应用程序,或者写过一些ajax请求调用,你可能会遇到过CORS错误. CORS是什么? 它与安全性有关吗? 为什么要有CORS?它解决了什么目的? CORS是怎样运行 ...

  8. Distcp 跨集群同步

    date: 2020-10-09 17:45:00 updated: 2020-10-10 17:45:00 Distcp 跨集群同步 1. 使用方法及原理 hadoop distcp srcPath ...

  9. E. Enemy is weak 解析(思維、離散化、BIT、線段樹)

    Codeforce 61 E. Enemy is weak 解析(思維.離散化.BIT.線段樹) 今天我們來看看CF61E 題目連結 題目 給一個數列\(a\),求有多少\((i,j,k)\),\(i ...

  10. 想买保时捷的运维李先生学Java性能之 垃圾收集器

    前言 垃圾收集算法是内存回收的方法论:垃圾收集器是内存回收的具体实现.Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商.不同版本的虚拟机所提供的垃圾收集器都有很大的差别,并且 ...