2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > Spring Boot整合JWT实现用户认证(附源码)

Spring Boot整合JWT实现用户认证(附源码)

时间:2024-04-15 15:59:05

相关推荐

Spring Boot整合JWT实现用户认证(附源码)

点击上方“程序IT圈”,选择“置顶公众号”

每天早上8点50分进来看看,就是最大的支持

来源:/yv1Do6e3

什么是JWT

JWT(Json Web Token),是一种工具,格式为XXXX.XXXX.XXXX的字符串,JWT以一种安全的方式在用户和服务器之间传递存放在JWT中的不敏感信息。

为什么要用JWT

设想这样一个场景,在我们登录一个网站之后,再把网页或者浏览器关闭,下一次打开网页的时候可能显示的还是登录的状态,不需要再次进行登录操作,通过JWT就可以实现这样一个用户认证的功能。当然使用Session可以实现这个功能,但是使用Session的同时也会增加服务器的存储压力,而JWT是将存储的压力分布到各个客户端机器上,从而减轻服务器的压力。

JWT长什么样

JWT由3个子字符串组成,分别为HeaderPayload以及Signature,结合JWT的格式即:Header.Payload.Signature。(Claim是描述Json的信息的一个Json,将Claim转码之后生成Payload)。

Header

Header是由以下这个格式的Json通过Base64编码(编码不是加密,是可以通过反编码的方式获取到这个原来的Json,所以JWT中存放的一般是不敏感的信息)生成的字符串,Header中存放的内容是说明编码对象是一个JWT以及使用“SHA-256”的算法进行加密(加密用于生成Signature)

{"typ":"JWT","alg":"HS256"}

Claim

Claim是一个Json,Claim中存放的内容是JWT自身的标准属性,所有的标准属性都是可选的,可以自行添加,比如:JWT的签发者、JWT的接收者、JWT的持续时间等;同时Claim中也可以存放一些自定义的属性,这个自定义的属性就是在用户认证中用于标明用户身份的一个属性,比如用户存放在数据库中的id,为了安全起见,一般不会将用户名及密码这类敏感的信息存放在Claim中。将Claim通过Base64转码之后生成的一串字符串称作Payload。

{"iss":"Issuer——用于说明该JWT是由谁签发的","sub":"Subject——用于说明该JWT面向的对象","aud":"Audience——用于说明该JWT发送给的用户","exp":"ExpirationTime——数字类型,说明该JWT过期的时间","nbf":"NotBefore——数字类型,说明在该时间之前JWT不能被接受与处理","iat":"IssuedAt——数字类型,说明该JWT何时被签发","jti":"JWTID——说明标明JWT的唯一ID","user-definde1":"自定义属性举例","user-definde2":"自定义属性举例"}

Signature

Signature是由Header和Payload组合而成,将Header和Claim这两个Json分别使用Base64方式进行编码,生成字符串Header和Payload,然后将Header和Payload以Header.Payload的格式组合在一起形成一个字符串,然后使用上面定义好的加密算法和一个密匙(这个密匙存放在服务器上,用于进行验证)对这个字符串进行加密,形成一个新的字符串,这个字符串就是Signature。

总结

JWT实现认证的原理

服务器在生成一个JWT之后会将这个JWT会以Authorization : Bearer JWT 键值对的形式存放在cookies里面发送到客户端机器,在客户端再次访问收到JWT保护的资源URL链接的时候,服务器会获取到cookies中存放的JWT信息,首先将Header进行反编码获取到加密的算法,在通过存放在服务器上的密匙对Header.Payload 这个字符串进行加密,比对JWT中的Signature和实际加密出来的结果是否一致,如果一致那么说明该JWT是合法有效的,认证成功,否则认证失败。

JWT实现用户认证的流程图

JWT的代码实现

这里的代码实现使用的是Spring Boot(版本号:1.5.10)框架,以及Apache Ignite(版本号:2.3.0)数据库。有关Ignite和Spring Boot的整合可以查看这里。

/ltl112358/article/details/79399026

代码说明:

代码中与JWT有关的内容如下:

config包中JwtCfg类配置生成一个JWT并配置了JWT拦截的URL

controller包中PersonController 用于处理用户的登录注册时生成JWT,SecureController 用于测试JWT

model包中JwtFilter 用于处理与验证JWT的正确性

其余属于Ignite数据库访问的相关内容

JwtCfg 类

这个类中声明了一个@Bean ,用于生成一个过滤器类,对/secure 链接下的所有资源访问进行JWT的验证

/***ThisisJwtconfigurationwhichsettheurl"/secure/*"forfiltering*@program:users*@create:-03-0321:18**/@ConfigurationpublicclassJwtCfg{@BeanpublicFilterRegistrationBeanjwtFilter(){finalFilterRegistrationBeanregistrationBean=newFilterRegistrationBean();registrationBean.setFilter(newJwtFilter());registrationBean.addUrlPatterns("/secure/*");returnregistrationBean;}}

JwtFilter 类

这个类声明了一个JWT过滤器类,从Http请求中提取JWT的信息,并使用了”secretkey”这个密匙对JWT进行验证

/***Checkthejwttokenfromfrontendifisinvalid*@program:users*@create:-03-0111:03**/publicclassJwtFilterextendsGenericFilterBean{publicvoiddoFilter(finalServletRequestreq,finalServletResponseres,finalFilterChainchain)throwsIOException,ServletException{//ChangethereqandrestoHttpServletRequestandHttpServletResponsefinalHttpServletRequestrequest=(HttpServletRequest)req;finalHttpServletResponseresponse=(HttpServletResponse)res;//GetauthorizationfromHttprequestfinalStringauthHeader=request.getHeader("authorization");//IftheHttprequestisOPTIONSthenjustreturnthestatuscode200//whichisHttpServletResponse.SC_OKinthiscodeif("OPTIONS".equals(request.getMethod())){response.setStatus(HttpServletResponse.SC_OK);chain.doFilter(req,res);}//ExceptOPTIONS,otherrequestshouldbecheckedbyJWTelse{//Checktheauthorization,checkifthetokenisstartedby"Bearer"if(authHeader==null||!authHeader.startsWith("Bearer")){thrownewServletException("MissingorinvalidAuthorizationheader");}//ThengettheJWTtokenfromauthorizationfinalStringtoken=authHeader.substring(7);try{//UseJWTparsertocheckifthesignatureisvalidwiththeKey"secretkey"finalClaimsclaims=Jwts.parser().setSigningKey("secretkey").parseClaimsJws(token).getBody();//Addtheclaimtorequestheaderrequest.setAttribute("claims",claims);}catch(finalSignatureExceptione){thrownewServletException("Invalidtoken");}chain.doFilter(req,res);}}}

PersonController 类

这个类中在用户进行登录操作成功之后,将生成一个JWT作为返回

/***@program:users*@create:-02-2719:28**/@RestControllerpublicclassPersonController{@AutowiredprivatePersonServicepersonService;/***Userregisterwithwhoseusernameandpassword*@paramreqPerson*@returnSuccessmessage*@throwsServletException*/@RequestMapping(value="/register",method=RequestMethod.POST)publicStringregister(@RequestBody()ReqPersonreqPerson)throwsServletException{//Checkifusernameandpasswordisnullif(reqPerson.getUsername()==""||reqPerson.getUsername()==null||reqPerson.getPassword()==""||reqPerson.getPassword()==null)thrownewServletException("UsernameorPasswordinvalid!");//Checkiftheusernameisusedif(personService.findPersonByUsername(reqPerson.getUsername())!=null)thrownewServletException("Usernameisused!");//Giveadefaultrole:MEMBERList<Role>roles=newArrayList<Role>();roles.add(Role.MEMBER);//CreateapersoninignitepersonService.save(newPerson(reqPerson.getUsername(),reqPerson.getPassword(),roles));return"RegisterSuccess!";}/***Checkuser`slogininfo,thencreateajwttokenreturnedtofrontend*@paramreqPerson*@returnjwttoken*@throwsServletException*/@PostMappingpublicStringlogin(@RequestBody()ReqPersonreqPerson)throwsServletException{//Checkifusernameandpasswordisnullif(reqPerson.getUsername()==""||reqPerson.getUsername()==null||reqPerson.getPassword()==""||reqPerson.getPassword()==null)thrownewServletException("Pleasefillinusernameandpassword");//Checkiftheusernameisusedif(personService.findPersonByUsername(reqPerson.getUsername())==null||!reqPerson.getPassword().equals(personService.findPersonByUsername(reqPerson.getUsername()).getPassword())){thrownewServletException("Pleasefillinusernameandpassword");}//CreateTwttokenStringjwtToken=Jwts.builder().setSubject(reqPerson.getUsername()).claim("roles","member").setIssuedAt(newDate()).signWith(SignatureAlgorithm.HS256,"secretkey").compact();returnjwtToken;}}

SecureController 类

这个类中只是用于测试JWT功能,当用户认证成功之后,/secure 下的资源才可以被访问

/***Testthejwt,ifthetokenisvalidthenreturn"LoginSuccessful"*Ifisnotvalid,therequestwillbeinterceptedbyJwtFilter*@program:users*@create:-03-0111:05**/@RestController@RequestMapping("/secure")publicclassSecureController{@RequestMapping("/users/user")publicStringloginSuccess(){return"LoginSuccessful!";}}

代码功能测试

本例使用Postman对代码进行测试,这里并没有考虑到安全性传递的明文密码,实际上应该用SSL进行加密

1.首先进行一个新的测试用户的注册,可以看到注册成功的提示返回

2.再让该用户进行登录,可以看到登录成功之后返回的JWT字符串

3.直接申请访问/secure/users/user ,这时候肯定是无法访问的,服务器返回500错误

4.将获取到的JWT作为Authorization属性提交,申请访问/secure/users/user ,可以访问成功

示例代码

源码请在公众号后台回复关键词【622】获取本文代码 。

PS:今天打卡在头条推文末尾

我的知识星球,限时免费加入!

Spring Boot 快速整合MyBatis (附上源码)

10张GIF动图让你弄懂递归、二分检索等概念

什么是红黑树?今天详细学习一下。

HashMap底层的数据结构和算法

详解 Spring AOP 的实现机制

十二张图详解Redis的数据结构和对象系统

五分钟彻底理解一致性哈希算法

我们为什么要使用 AOP ?

TCP/IP的底层队列实现原理

教你巧记OSI七层网络模型

图解 Java 垃圾回收机制

记得将本号置顶/标星,

不忘每天签到哦!

如果有帮助,请点个“在看”支持!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。