springboot怎么集成JWT实现身份认证
一、什么是JWT
JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案。现在的项目开发一般都是前端端分离,这就涉及到跨域和权鉴问题。
二、JWT组成
由三部分组成:头部(Header)、载荷(Payload)与签名(signature)
头部(Header):
头部信息由两部分组成1、令牌的类型,即JWT;2、使用的签名算法,例如HMASSHA256或RSA;
{ "alg": "HS256", "typ": "JWT" }
这个json中的typ属性,用来标识整个token字符串是一个JWT字符串;它的alg属性,用来说明这个JWT签发的时候所使用的签名和摘要算法,typ跟alg属性的全称其实是type algorithm,分别是类型跟算法的意思。之所以都用三个字母来表示,也是基于JWT最终字串大小的考虑,同时也是跟JWT这个名称保持一致,这样就都是三个字符了…typ跟alg是JWT中标准中规定的属性名称。
载荷(Payload):
payload用来承载要传递的数据,它的json结构实际上是对JWT要传递的数据的一组声明,这些声明被JWT标准称为claims,它的一个“属性值对”其实就是一个claim(要求), 每一个claim的都代表特定的含义和作用。
我们可以在claim里放一些业务信息。
签名(signature):
签名是把header和payload对应的json结构进行base64url编码之后得到的两个串用 '英文句点号' 拼接起来,然后根据header里面alg指定的签名算法生成出来的。
算法不同,签名结果不同。以alg: HS256为例来说明前面的签名如何来得到。
按照前面alg可用值的说明,HS256其实包含的是两种算法:HMAC算法和SHA256算法,前者用于生成摘要,后者用于对摘要进行数字签名。这两个算法也可以用HMACSHA256来统称
jwt数据结构图:
三、JWT运行原理
1.第一次发送登录请求,必然会携带用户信息uname和pwd
2.通过用户信息uname和pwd登录成功,会将用户信息通过jwt工具类生成一个加密的字符串
3.加密字符串 会以response header 响应头的形式 相应到前端
4.前端服务器会有响应拦截器拦截,截取到响应头承载的jwt串,又会放到Vuex中
5.当第二次请求,前端服务器中有一个请求拦截器,会将Vuex中的jwt串放入request header 请求当中
6.当请求通过跨域的方式到达后台服务器,后台服务器中又有一个过滤器,会截取到 request header 请求当中的jwt串
7.jwt工具类会对jwt串进行解析,解析成用户信息,最终进行校验
四、springboot集成JWT
整体思路:
当前端访问后台登录接口login时,先根据用户名和密码判断用户表是否存在该用户,如果存在该用户,则生成jwt串,可以在jwt串里添加些业务信息(比如用登录账号,用户真实姓名等),并把jwt串返给前端
当前端拿到jwt串后放到所有请求的header中,比如token=jwt串
后端开发一个filter,拦截所有请求(除login请求外,因为login请求还没有生成jwt),并从request的header中获取jwt(即token的值),对jwt校验和获取jwt中的业务信息,在把这些业务信息放到request的header中,这样方便后端接口直接从header中获取
如果filter中jwt过期,或者校验失败,则返回给前端提示,前端返回登录页面,让用户重新登录。
1、在pom.xml引入依赖
<!--jwt--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.8.3</version> </dependency>
2、开发jwt生成工具类,代码如下:
package com.lsl.exam.utils; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.lsl.exam.entity.TabUser; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtUtil { private static final long EXPIRE_TIME = 1000 * 60 * 60 *24; //设置私钥 private static final String TOKEN_SECRET = "aa082c-66rt89-29sr3t-y9t7b8"; /** * 创建携带自定义信息和声明的自定义私钥的jwt * @param user 用户信息表 * @return jwt串 */ public static String creatJwt(TabUser user){ //构建头部信息 Map<String,Object> header = new HashMap<>(); header.put("typ","JWT"); header.put("alg","HS256"); //根据私钥构建密钥信息 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); //根据当前用户密码构建密钥信息 // Algorithm algorithm = Algorithm.HMAC256(user.getUserpwd()); //设置过期时间为当前时间一天后 Date nowDate = new Date(); Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME); String jwt = JWT.create().withHeader(header) .withClaim("account",user.getAccount())//业务信息:员工号 .withClaim("username",user.getUsername())//业务信息:员工姓名 .withClaim("rolename",user.getRoleName())//业务信息:角色 .withIssuer("SERVICE")//声明,签名是有谁生成 例如 服务器 .withNotBefore(new Date())//声明,定义在什么时间之前,该jwt都是不可用的 .withExpiresAt(expireDate)//声明, 签名过期的时间 .sign(algorithm);//根据algorithm生成签名 return jwt; } }
3、后端login接口逻辑如下:
package com.lsl.exam.controller; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.lsl.exam.entity.TabUser; import com.lsl.exam.entity.backresult.ResultVO; import com.lsl.exam.service.ITabRoleService; import com.lsl.exam.service.IUserService; import com.lsl.exam.utils.Base64Util; import com.lsl.exam.utils.JwtUtil; import com.lsl.exam.utils.ResultVoUtil; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/exam") public class UserController { private static final Logger LOG = org.slf4j.LoggerFactory.getLogger("UserController"); @Autowired IUserService userService; @Autowired ITabRoleService roleService; @PostMapping(value = "login",produces = "application/json;charset=UTF-8") @ResponseBody public ResultVO<?> login(@RequestBody Map params){ Map reuslt = new HashMap(); String account = params.get("account") == null ? "" : params.get("account").toString(); String pwd = params.get("pwd") == null ? "" : params.get("pwd").toString(); if ("".equals(account) || "".equals(pwd)){ return ResultVoUtil.error(30000,"用户名或者密码不能为空!"); } //pwd解密 String decodePwd = Base64Util.decode(pwd); if ("".contains(decodePwd)){ return ResultVoUtil.error(30000,"密码错误!"); } TabUser user = userService.getOne(new QueryWrapper<TabUser>() .eq("account",account) .eq("userpwd",decodePwd)); if (null == user){ return ResultVoUtil.error(30000,"用户名或者密码错误"); } //获取当前用户拥有的角色 String userId = user.getId(); Map roleMap = new HashMap(); roleMap.put("userId",userId); List<Map> roleList = roleService.qryRoleInfoByUserId(roleMap); List<String> roleNames = new ArrayList<>(); for(Map role : roleList){ roleNames.add(role.get("role").toString()); } user.setRoleName(JSON.toJSONString(roleNames)); //生成带有业务信息的jwt串 String jwt = JwtUtil.creatJwt(user); //把jwt和当前用户信息返给前端 reuslt.put("jwt",jwt); reuslt.put("roleNames",roleNames); reuslt.put("username",user.getUsername()); reuslt.put("account",user.getAccount()); return ResultVoUtil.success(reuslt); } @PostMapping(value = "qryUser",produces = "application/json;charset=UTF-8") @ResponseBody public Object qryUser(HttpServletRequest request){ //这里header中的信息是filter中放进去的 String account = request.getHeader("account"); String username = request.getHeader("username"); String rolename = request.getHeader("rolename"); List<TabUser> list = userService.list(); return ResultVoUtil.success(list); } }
4、开发filter,进行jwt校验
package com.lsl.exam.filter; import com.alibaba.fastjson.JSON; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import com.lsl.exam.entity.backresult.ResultVO; import com.lsl.exam.utils.ResultVoUtil; import org.apache.tomcat.util.http.MimeHeaders; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * jwt校验过滤器 */ @Component @WebFilter(filterName = "jwtFilter",urlPatterns = {"/*"}) public class AuthJwtFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String url = httpServletRequest.getRequestURL().toString(); //配置不进行jwt校验的请求路径 List<String> urlList = new ArrayList<>(); urlList.add("/exam/login"); boolean flag = false; for (String strUrl : urlList){ if (url.contains(strUrl)){ flag = true; } } try { if (!flag){ String token = httpServletRequest.getHeader("token"); //校验token,jwt过期有jwt自行校验,如果超时了,会执行catch里代码 DecodedJWT decodeJwt = JWT.require(Algorithm.HMAC256("aa082c-66rt89-29sr3t-y9t7b8")).build().verify(token); //获取jwt中的业务信息 String account = decodeJwt.getClaim("account").asString(); String username = decodeJwt.getClaim("username").asString(); String rolename = decodeJwt.getClaim("rolename").asString(); Map<String, String> headerMap = new HashMap<>(); headerMap.put("account",account); headerMap.put("username",username); headerMap.put("rolename",rolename); //把业务信息添加到request的header addHeader(httpServletRequest,headerMap); // Class<?> superclass = servletRequest.getClass().getSuperclass().getSuperclass(); // Field requestField = superclass.getDeclaredField("request"); // requestField.setAccessible(true); // RequestFacade requestFacadeInstance = (RequestFacade) requestField.get(servletRequest); RequestFacade requestFacadeInstance = (RequestFacade)superclass3; // Field requestField1 = requestFacadeInstance.getClass().getDeclaredField("request"); // requestField1.setAccessible(true); // Object requestInstance = requestField1.get(requestFacadeInstance); // Field coyoteRequestField = requestInstance.getClass().getDeclaredField("coyoteRequest"); // coyoteRequestField.setAccessible(true); // // Object coyoRequestInstance = requestField1.get(requestInstance); // Field headersField = coyoRequestInstance.getClass().getDeclaredField("headers"); // headersField.setAccessible(true); // // MimeHeaders headers = (MimeHeaders) headersField.get(coyoRequestInstance); // headers.removeHeader("token"); // headers.addValue("account").setString(account); // headers.addValue("username").setString(username); // headers.addValue("roleid").setString(roleid); // } } catch (Exception e) { //jwt校验失败,返给前端的code=1,前端要重定向到登录页面 PrintWriter writer = null; servletResponse.setCharacterEncoding("UTF-8"); servletResponse.setContentType("text/html; charset=utf-8"); try { writer = servletResponse.getWriter(); ResultVO vo = ResultVoUtil.successLogout(); String msg = JSON.toJSONString(vo); writer.println(msg); } catch (IOException ex) { } finally { if (writer != null){ writer.close(); } return; } } filterChain.doFilter(servletRequest,servletResponse); } /** * 向request的header中放业务信息 * @param request * @param headerMap */ private void addHeader(HttpServletRequest request, Map<String, String> headerMap) { if (headerMap==null||headerMap.isEmpty()){ return; } Class<? extends HttpServletRequest> c=request.getClass(); //System.out.println(c.getName()); System.out.println("request实现类="+c.getName()); try{ Field requestField=c.getDeclaredField("request"); requestField.setAccessible(true); Object o=requestField.get(request); Field coyoteRequest=o.getClass().getDeclaredField("coyoteRequest"); coyoteRequest.setAccessible(true); Object o2=coyoteRequest.get(o); System.out.println("coyoteRequest实现类="+o2.getClass().getName()); Field headers=o2.getClass().getDeclaredField("headers"); headers.setAccessible(true); MimeHeaders mimeHeaders=(MimeHeaders) headers.get(o2); for (Map.Entry<String,String> entry:headerMap.entrySet()){ mimeHeaders.removeHeader(entry.getKey()); mimeHeaders.addValue(entry.getKey()).setString(entry.getValue()); } }catch (Exception e){ e.printStackTrace(); } } @Override public void destroy() { } }
以上是springboot怎么集成JWT实现身份认证的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Jasypt介绍Jasypt是一个java库,它允许开发员以最少的努力为他/她的项目添加基本的加密功能,并且不需要对加密工作原理有深入的了解用于单向和双向加密的高安全性、基于标准的加密技术。加密密码,文本,数字,二进制文件...适合集成到基于Spring的应用程序中,开放API,用于任何JCE提供程序...添加如下依赖:com.github.ulisesbocchiojasypt-spring-boot-starter2.1.1Jasypt好处保护我们的系统安全,即使代码泄露,也可以保证数据源的

一、Redis实现分布式锁原理为什么需要分布式锁在聊分布式锁之前,有必要先解释一下,为什么需要分布式锁。与分布式锁相对就的是单机锁,我们在写多线程程序时,避免同时操作一个共享变量产生数据问题,通常会使用一把锁来互斥以保证共享变量的正确性,其使用范围是在同一个进程中。如果换做是多个进程,需要同时操作一个共享资源,如何互斥呢?现在的业务应用通常是微服务架构,这也意味着一个应用会部署多个进程,多个进程如果需要修改MySQL中的同一行记录,为了避免操作乱序导致脏数据,此时就需要引入分布式锁了。想要实现分

springboot读取文件,打成jar包后访问不到最新开发出现一种情况,springboot打成jar包后读取不到文件,原因是打包之后,文件的虚拟路径是无效的,只能通过流去读取。文件在resources下publicvoidtest(){Listnames=newArrayList();InputStreamReaderread=null;try{ClassPathResourceresource=newClassPathResource("name.txt");Input

随着互联网的发展,越来越多的网站和应用需要提供API接口来进行数据交互。在这种情况下,API身份验证和加密成为了非常重要的问题。而JWT和JWE作为一种流行的身份验证和加密机制,在PHP中的应用也越来越广泛。那么,本文将介绍如何在PHP中使用JWT和JWE进行API身份验证和加密。JWT的基本概念JWT代表JSONWe

JWT(JSONWebToken)是一种轻量级的认证和授权机制,它使用JSON对象作为安全令牌,可以在多个系统之间安全地传输用户身份信息。而ThinkPHP6是一种基于PHP语言的高效、灵活的MVC框架,它提供了许多有用的工具和功能,其中就包括JWT认证机制。在本文中,我们将介绍如何使用ThinkPHP6进行JWT认证,以保障Web应用程序的安全性和可靠

SpringBoot和SpringMVC都是Java开发中常用的框架,但它们之间有一些明显的差异。本文将探究这两个框架的特点和用途,并对它们的差异进行比较。首先,我们来了解一下SpringBoot。SpringBoot是由Pivotal团队开发的,它旨在简化基于Spring框架的应用程序的创建和部署。它提供了一种快速、轻量级的方式来构建独立的、可执行

在Springboot+Mybatis-plus不使用SQL语句进行多表添加操作我所遇到的问题准备工作在测试环境下模拟思维分解一下:创建出一个带有参数的BrandDTO对象模拟对后台传递参数我所遇到的问题我们都知道,在我们使用Mybatis-plus中进行多表操作是极其困难的,如果你不使用Mybatis-plus-join这一类的工具,你只能去配置对应的Mapper.xml文件,配置又臭又长的ResultMap,然后再去写对应的sql语句,这种方法虽然看上去很麻烦,但具有很高的灵活性,可以让我们

1、自定义RedisTemplate1.1、RedisAPI默认序列化机制基于API的Redis缓存实现是使用RedisTemplate模板进行数据缓存操作的,这里打开RedisTemplate类,查看该类的源码信息publicclassRedisTemplateextendsRedisAccessorimplementsRedisOperations,BeanClassLoaderAware{//声明了key、value的各种序列化方式,初始值为空@NullableprivateRedisSe
