大家好,我是北军。是的,我知道你们都是我的。今天给大家介绍一下JWT。JWT简介JWT(JsonWebToken)是一个基于Json的开放标准,实现了在网络应用环境之间传递声明。JWT声明一般用于在身份提供者和服务提供者之间传递经过认证的用户身份信息,从而从资源服务器获取资源。基于令牌的认证机制基于令牌的认证机制类似于HTTP协议,也是无状态的。它不需要在服务器端保留用户认证信息或会话信息。这意味着基于token认证机制的应用不需要考虑用户登录的是哪台服务器。服务器使用秘钥(serectkey),然后将用户id等信息打包,生成一系列加密算法。成功,顺便把token返回给客户端,用户以后拿token访问相关资源。服务端收到token,使用serectkey再用特定的算法解析出token中的用户id,解析成功!那么这个人就是用户,然后通过解析出来的id对用户进行相关操作。这为应用扩展提供了方便。JWT认证流程如下:首先,前端通过Web表单将自己的用户名和密码发送给后端接口。这个过程一般是POST请求。后端校验用户名和密码成功后,将包含用户信息的数据作为JWTPayload,进行base64编码后与JWTHeader拼接,签名形成JWTToken。形成的JWTToken是一个类似{header}.{payload}.{signature}的字符串。后端将JWTToken字符串作为登录成功的结果返回给前端。前端可以将返回结果保存在浏览器中,退出时删除保存的JWTToken。前端在每次请求的HTTP请求头中放入JWTToken,后端检查前端传过来的JWTToken,验证其有效性,比如检查签名是否正确,是否过期,token的接收者是否是自己等。验证通过后,后端解析出JWTToken中包含的用户信息,进行其他逻辑操作(通常是根据用户信息获取权限等),返回结果注意:这个令牌必须在每次请求时传递给服务器,并且应该存储在请求标头中。另外,服务器必须支持CORS(Cross-OriginResourceSharing)策略。Generally,wedothisontheserver做就可以了Access-Control-Allow-Origin:JWT的构成eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJ0ZXN0OTUyNyIsImlhdCI6MTY1NjU1OTY2OCwic3ViIjoie1widXNlcklkXCI6XCI5NTI3XCIsXCJ1c2VyTmFtZVwiOlwidGVzdDk1MjdcIn0iLCJleHAiOjE2NTY1NjE0Njh9.7oot0glDxaL-g7pOJ2mZld2VLRhpo0h5y_BbVI4ZolA第一部分我们称它为头部(header),第二部分我们称其为载荷(payload,类似于飞机上承运的物品),第三部分是签证(签名)。头部(header)jwt的头部携带两部分信息:语句类型,这里是jwt语句加密的算法,通常直接使用HMACSHA256。这种加密算法也是签名算法。{"alg":"HS256"}经过Base64编码后,ewogICAgImFsZyI6IkhTMjU2Igp9载荷(payload)就是存放有效信息的地方。有效信息包含标准公开声明私有声明三部分注册的声明{"sub":{"userId":"9527","userName":"test9527"},"exp":1656561468,"iat":1656559668,"jti":"test9527"}sub:主题,一般是用户信息,最好不要放敏感信息exp:jwt过期时间,这个过期时间必须大于发布时间iat:jwt发布时间jti:jwt(签名)的唯一标识需要base64加密的header和base64加密的payload才能使用。将字符串连接起来,然后使用header中声明的加密方式对salt和secret的组合进行加密,然后构成jwt的第三部分。顾名思义,签名就是签名。签名通常是一个字符串,可用于使用某种算法来验证身份。secret是在server端配置的,不能外泄。您可以为自己发行令牌。JWT的使用(JAVA)新建一个项目,导入jwt包;io.jsonwebtokenjjwt0.9.0JWT工具类:publicclassJwtUtil{//用于加密的密钥解密用于生成密钥publicstaticfinalStringJWT_KEY="secret";//过期时间为30分钟privatestaticfinallongEXPIRE_TIME=30*60*1000;//生成加密密钥secretKeypublicstaticSecretKeygeneralKey(){byte[]encodedKey=Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKeykey=newSecretKeySpec(encodedKey,0,encodedKey.length,"AES");返回键;}publicstaticStringcreateJWT(Stringid,Stringsubject){//指定签名时使用的签名算法,即header部分,jjwt已经封装了这部分内容。签名算法signatureAlgorithm=SignatureAlgorithm.HS256;//生成JWT的时间longnowMillis=System.currentTimeMillis();现在日期=新日期(nowMillis);//生成签名时使用的秘钥,本地封装了该方法,一般可以从本地配置文件中读取。请记住,这个秘密密钥不能暴露。它是您服务器的私钥,在任何情况下都不应泄露。一旦client知道了secret,就意味着client可以自己下发jwt了。SecretKeykey=generalKey();//这里其实是新建了一个JwtBuilder,setjwtbodyJwtBuilderbuilder=Jwts.builder()//如果有private语句,必须先设置你创建的private语句,这个是给builder的claim赋值的,一旦写完标准索赔转让,是为了涵盖这些标准索赔。setClaims(claims)//设置jti(JWTID):是JWT的唯一标识。根据业务需要,这个可以设置为唯一值,主要作为一次性令牌使用,避免重放攻击。.setId(id)//iat:JWT发行时间。setIssuedAt(now)//sub(Subject):代表这个JWT的主题,也就是它的所有者。这是一个json格式的字符串,其中可以存储userid、roldid等,作为任何用户的唯一标识。.setSubject(subject)//设置签名使用的签名算法和签名使用的秘钥.signWith(signatureAlgorithm,key);longexpMillis=nowMillis+EXPIRE_TIME;日期exp=newDate(expMillis);//设置过期时间builder.设置过期时间(exp);//开始压缩成jwtlikexxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxreturnbuilder.compact();}/***在不解密secret的情况下获取token中的信息并获取token中包含的用户名*/publicstaticStringgetUsername(Stringtoken){try{DecodedJWTjwt=JWT._decode_(token);返回jwt.getClaim("userName").asString();}catch(JWTDecodeExceptione){返回null;}}}创建控制器类;访问测试;获取到的token可以进行Base64解码,可以看到用户信息,所以token中不能包含敏感信息,以防泄露。使用此token调用getUser接口,可以正确解析;接下来,我们不使用getToken获取token,而是伪造token;{"sub":{"userId":"001","userName":"test001"},"exp":1656561468,"iat":1656559668,"jti":"test001"}再组合header,payload和signature,得到新的token访问getUser;eyJhbGciOiJIUzI1NiJ9.ewogICAgInN1YiI6ewogICAgICAgICJ1c2VySWQiOiIwMDEiLAogICAgICAgICJ1c2VyTmFtZSI6InRlc3QwMDEiCiAgICB9LAogICAgImV4cCI6MTY1NjU2MTQ2OCwKICAgICJpYXQiOjE2NTY1NTk2NjgsCiAgICAianRpIjoidGVzdDAwMSIKfQ==.7oot0glDxaL-g7pOJ2mZld2VLRhpo0h5y_BbVI4ZolA得到结果如下:无法正确解析token,indicatingthatthetokenisinvalid.最后,JWT过期时间应该适当设置。如果过长,可能会被拦截,造成用户信息泄露、安全等问题;如果太短,用户体验不好。