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

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

  1. <dependency>
  2. <groupId>io.jsonwebtoken</groupId>
  3. <artifactId>jjwt</artifactId>
  4. <version>0.7.0</version>
  5. </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 ,加在方法上灵活处理对应的请求

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

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

  1. /**
  2. * @author : wl
  3. * @Description :方法请求拦截
  4. * @date : 2020/7/3 11:47
  5. */
  6. @Slf4j
  7. public class AuthenticationInterceptor implements HandlerInterceptor {
  8. @Autowired
  9. private SysUserService userService;
  10. @Autowired
  11. private RedisUtil redisUtil;
  12.  
  13. @Override
  14. public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws ServletException, IOException {
  15. String token = httpServletRequest.getHeader("token");
  16. if (!(object instanceof HandlerMethod)) {
  17. return true;
  18. }
  19. HandlerMethod handlerMethod = (HandlerMethod) object;
  20. Method method = handlerMethod.getMethod();
  21. //检查有没有跳过token的注解
  22. if (method.isAnnotationPresent(SkipToken.class)) {
  23. SkipToken skipToken = method.getAnnotation(SkipToken.class);
  24. if (skipToken.required()) {
  25. log.info("该请求无须token验证。。。");
  26. return true;
  27. }
  28. }
  29. //检查有没有需要token的注解
  30. if (method.isAnnotationPresent(NeedToken.class)) {
  31. NeedToken needToken = method.getAnnotation(NeedToken.class);
  32. if (needToken.required()) {
  33. log.info("该请求需要token验证。。。");
  34. if (Objects.isNull(token)) {
  35. throw new BaseException("无token,请重新登录");
  36. }
  37. try {
  38. JwtUtil.getTokenInfo(token);
  39. } catch (ExpiredJwtException e) {
  40. throw new BaseException("token超时");
  41. }
  42. // SysUser user = userService.findUser(sysUser.getUsername(), sysUser.getPassword());
  43. // if (Objects.isNull(user)) {
  44. // throw new BaseException("用户不存在,请重新登录");
  45. // }
  46. if (!Objects.equals(token, redisUtil.get("ms_notify_token"))) {
  47. throw new BaseException("token异常,请重新登录");
  48. }
  49. }
  50. }
  51. return true;
  52. }
  53.  
  54. @Override
  55. public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
  56. }
  57.  
  58. @Override
  59. public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
  60. }
  61. }

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

  1. /**
  2. * @author : wl
  3. * @Description : 拦截异常处理
  4. * @date : 2020/7/5 15:30
  5. */
  6. @Slf4j
  7. public class MyWebHandlerException implements HandlerExceptionResolver {
  8. @SneakyThrows
  9. @Override
  10. public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
  11. log.info("请求出现异常:" + e.getMessage());
  12. e.printStackTrace();
  13. // return new ModelAndView("redirect:/login");
  14. ModelAndView modelAndView = new ModelAndView();
  15. String type = httpServletRequest.getHeader("X-Requested-With");
  16. if (Objects.equals(type, "XMLHttpRequest")) {
  17. //是ajax请求
  18. httpServletResponse.setStatus(666);
  19. httpServletResponse.setContentType("text/javascript; charset=utf-8");
  20. httpServletResponse.getWriter().write(e.getMessage());
  21. return modelAndView;
  22. } else {
  23. modelAndView.setViewName("/login");
  24. return modelAndView;
  25. }
  26. }
  27. }

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

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

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

  1. /**
  2. * @author : wl
  3. * @Description :web拦截器
  4. * @date : 2020/7/3 11:46
  5. */
  6. @Configuration
  7. public class webConfig implements WebMvcConfigurer {
  8. @Override
  9. public void addInterceptors(InterceptorRegistry registry) {
  10. registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");
  11. }
  12.  
  13. @Bean
  14. public AuthenticationInterceptor authenticationInterceptor() {
  15. return new AuthenticationInterceptor();
  16. }
  17.  
  18. @Bean
  19. public WebMvcConfigurer webMvcConfigurer() {
  20. WebMvcConfigurer adapter = new WebMvcConfigurer() {
  21. @Override
  22. public void addViewControllers(ViewControllerRegistry registry) {
  23. registry.addViewController("/").setViewName("login");
  24. registry.addViewController("/index.html").setViewName("login");
  25. }
  26. };
  27. return adapter;
  28. }
  29. /*
  30. * 异常拦截处理
  31. * */
  32. @Override
  33. public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
  34. resolvers.add(new MyWebHandlerException());
  35. }
  36. }

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

登录后端代码:

  1. /**
  2. * @author : wl
  3. * @Description :
  4. * @date : 2020/7/2 16:46
  5. */
  6. @Controller
  7. public class Login {
  8. @Autowired
  9. private SysUserService sysUserService;
  10. @Autowired
  11. private RedisUtil redisUtil;
  12.  
  13. @SkipToken
  14. @RequestMapping("/login")
  15. public String toLogin() {
  16. System.out.println("跳转到登录页面");
  17. return "login";
  18. }
  19.  
  20. @SkipToken
  21. @RequestMapping("/index")
  22. public String toIndex() {
  23. System.out.println("跳转到主页面");
  24. return "index";
  25. }
  26.  
  27. @SkipToken
  28. @ResponseBody
  29. @RequestMapping("/loginIn")
  30. public ResultUtil loginIn(String username, String password) {
  31. try {
  32. SysUser user = sysUserService.findUser(username, password);
  33. if (null != user) {
  34. user.setPassword(password);
  35. String token = JwtUtil.generatorToken(user, 60*60);
  36. //token 保存在redis中
  37. redisUtil.set("ms_notify_token", token);
  38. return ResultUtil.ok().data("msg", token).message("登录成功");
  39. } else {
  40. return ResultUtil.error().data("msg", "error").message("用户不存在");
  41. }
  42. } catch (BaseException e) {
  43. return ResultUtil.error().data("msg", e.getMessage()).message("登陆失败");
  44. }
  45. }
  46.  
  47. }

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

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

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  6. <meta name="description" content="">
  7. <meta name="author" content="">
  8. <title>登录</title>
  9. <link href="css/bootstrap.min.css" rel="stylesheet">
  10. <link href="css/signin.css" rel="stylesheet">
  11. </head>
  12. <body class="text-center">
  13. <div class="form-signin">
  14. <img class="mb-4" th:src="@{img/my.svg}" alt="" width="80" height="80">
  15. <h1 class="h3 mb-3 font-weight-normal">请登录</h1>
  16. <label class="sr-only">用户名</label>
  17. <input type="text" name="username" id="username" class="form-control" th:placeholder="用户名" required="required" autofocus="">
  18. <p class=""></p>
  19. <label class="sr-only">密码</label>
  20. <input type="password" name="password" id="password" class="form-control" th:placeholder="密码" required="required" >
  21. <button class="btn btn-lg btn-primary btn-block" th:onclick="loginIn()">登录</button>
  22. </div>
  23. </body>
  24.  
  25. <script type="text/javascript" src="/js/jquery-3.5.1.js"></script>
  26. <script type="text/javascript" src="/js/bootstrap.min.js"></script>
  27. <script>
  28. function loginIn() {
  29. debugger;
  30. var username = $('#username').val();
  31. var password = $('#password').val();
  32. if(""==username || ""==password){
  33. alert("用户信息不完整,请检查!");
  34. return;
  35. }
  36. $.ajax({
  37. url: '/loginIn',
  38. type: "post",
  39. data: {'username': username, 'password': password},
  40. success: function (data) {
  41. if (data.success) {
  42. //alert(data.message);
  43. localStorage.setItem("token",data.data.msg);
  44. location.href = "/index"; // window.location.href 不生效 检查是否为form 表单
  45. } else {
  46. alert(data.message)
  47. }
  48. },
  49. error: function () {
  50. alert("登录失败")
  51. }
  52. });
  53. }
  54. </script>
  55. </html>

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

  1. //禁用,启用
  2. function disable_config(nid, status) {
  3. $.ajax({
  4. url: '/notify/updateStatus',
  5. data: {'nid': nid, 'status': status},
  6. type: "post",
  7. headers: {"token": localStorage.getItem("token")},
  8. success: function (data) {
  9. if (data.success) {
  10. location.reload();
  11. alert(data.message);
  12. } else {
  13. alert(data.message + ":" + data.data.msg)
  14. }
  15. }
  16. });
  17. }

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

  1. /**
  2. * 禁用 、启用
  3. */
  4. @NeedToken
  5. @ResponseBody
  6. @SysLogAnnotation("禁用 、启用 配置")
  7. @RequestMapping(value = "/updateStatus")
  8. public ResultUtil updateStatus(HttpServletRequest request, String nid, int status) {
  9. String config_status = (status == 1 ? "启用" : "禁用");
  10. try {
  11. notifyConfigService.updateStatus(nid, status);
  12. } catch (BaseException e) {
  13. return ResultUtil.error().data("msg", e.getMessage()).message(config_status + "配置失败");
  14. }
  15. return ResultUtil.ok().data("msg", "success").message(config_status + "配置成功");
  16. }

设计流程基本如上述代码所示,详细代码可以参考 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. SQl编程存储过程

    过程化存储 存储过程,一组为完成特定功能.经过编译后存储在数据库中的SQL语序集 灵活性:存储过程中可以进行流程控制和循环操作来完成复杂的判断和运算 一致性:通过存储过程可以使一些关联的操作一起发生, ...

  2. python 递归求和

    例子: 1 mylist = [1,2,3,4,5] 2 def func(var): 3 if var == []: 4 print('列表为空') 5 return 0 6 return var[ ...

  3. go 加锁

    package mainimport ( "fmt" "math/rand" "sync" "time")var tic ...

  4. linux 压缩 tar命令

    linux中tar命令用法    总结 *.tar 用 tar –xvf 解压 *.gz 用 gzip -d或者gunzip 解压 *.tar.gz和*.tgz 用 tar –xzf 解压 *.bz2 ...

  5. linux磁盘空间满了

    df-h 看到根目录已经用满了! find / -size +100M |xargs ls -lh列出系统内大于100M的文件 du --max-depth=1 -h 查看当前目录内文件夹的大小 看一 ...

  6. 小tip:CSS计数器+伪类实现数值动态计算与呈现【转】

    [原文]http://www.zhangxinxu.com/wordpress/2014/12/css-counters-pseudo-class-checked-numbers/ 一.CSS计数器为 ...

  7. Pytest配置文件声明自定义用例标识

    使用pytest.ini添加自定义用例标识: [pytest] # 1.使用没有注册过的标记抛出错误 addopts = --strict-markers # 2.自定义标记 markers = sm ...

  8. MVC实现修改数据

    前言 前言 最近没什么好写的,所以写个修改来巩固一下知识 控制器 public ActionResult UpdeteCartsNum(SYS_Carts sYS_Carts) { var list ...

  9. 556. 下一个更大元素 III

    556. 下一个更大元素 III 给定一个32位正整数 n,你需要找到最小的32位整数,其与 n 中存在的位数完全相同,并且其值大于n.如果不存在这样的32位整数,则返回-1. 示例 1: 输入: 1 ...

  10. 八皇后C++版本

    emmmm~刚刚学C++,写一个八皇后,凑合看吧嘤嘤嘤 1 #include <iostream> 2 #include<cstdlib> 3 #include<cmat ...