您好,欢迎访问一九零五行业门户网

springboot怎么集成JWT实现身份认证

一、什么是jwtjson 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实现身份认证的详细内容。
其它类似信息

推荐信息