首页 » PHP教程 » phpjws签名技巧_分布式系统认证必备根本JWT详解

phpjws签名技巧_分布式系统认证必备根本JWT详解

访客 2024-11-25 0

扫一扫用手机浏览

文章目录 [+]

接口认证是所有系统的根本功能,也是最主要的安全手段。
分布式环境下认证更是重中之重。
大略来讲,常用的认证办法有2种,一种是token办法,即利用类似于UUID(或者雪花算法生产字符串),另一个便是JWT的办法。
由于JWT的办法是专门为分布式场景设计的,以是有人也把JWT看做是分布式系统最得当的认证办法,没有之一。

什么是JWT

Json web token (JWT), 是为了在网络运用环境间通报声明而实行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特殊适用于分布式站点的单点登录(SSO)场景。
JWT的声明一样平常被用来在身份供应者和做事供应者间通报被认证的用户身份信息,以便于从资源做事器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

phpjws签名技巧_分布式系统认证必备根本JWT详解

大略来说便是 JWT(Json Web Token)是实现token技能的一种办理方案

phpjws签名技巧_分布式系统认证必备根本JWT详解
(图片来自网络侵删)
为什么利用JWT

token验证和session认证的差异

1>传统的session认证

http协议本身是一种无状态的协议,而这就意味着如果用户向我们的运用供应了用户名和密码来进行用户认证,那么下一次要求时,用户还要再一次进行用户认证才行,由于根据http协议,我们并不能知道是哪个用户发出的要求,所以为了让我们的运用能识别是哪个用户发出的要求,我们只能在做事器存储一份用户登录的信息,这份登录信息会在相应时通报给浏览器,见告其保存为cookie,以便下次要求时发送给我们的运用,这样我们的运用就能识别要求来自哪个用户了,这便是传统的基于session认证。
2>session缺陷

基于session的认证使运用本身很难得到扩展,随着不同客户端用户的增加,独立的做事器已无法承载更多的用户Session办法存储用户id的最大弊病在于要占用大量做事器内存,对付较大型运用而言可能还要保存许多的状态。

3>基于session认证暴露的问题

Session须要在做事器保存,暂用资源扩展性 session认证保存在内存中 ,无法扩展到其他机器中CSRF 基于cookie来进行用户识别的, cookie如果被截获,用户就会很随意马虎受到跨站要求假造的攻击。
基于token的鉴权机制

基于token的鉴权机制类似于http协议也是无状态的,它不须要在做事端去保留用户的认证信息或者会话信息。
这就意味着基于token认证机制的运用不须要去考虑用户在哪一台做事器登录了,这就为运用的扩展供应了便利。

JWT办法将用户状态分散到了客户端中,可以明显减轻做事真个内存压力。
除了用户id之外,还可以存储其他的和用户干系的信息,例如用户角色,用户性别等。

要求流程

用户利用用户名密码来要求做事器做事器进行验证用户的信息做事器通过验证发送给用户一个token客户端存储token,并在每次要求时附送上这个token值做事端验证token值,并返回数据

这个token必须要在每次要求时通报给做事端,它该当保存在要求头里, 其余,做事端要支持 CORS(跨来源资源共享)策略,一样平常我们在做事端这么做就可以了 Access-Control-Allow-Origin: 。
JWT的构造

一个JWT是下面的构造

加密后jwt信息如下所示,是由.分割的三部分组成,分别为Header、Payload、Signature

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

JWT 的组成

Head -紧张包含两个部分,alg指加密类型,可选值为HS256、RSA等等,typ=JWT为固定值,表示token的类型

Header:{ "alg": "HS256", "typ": "JWT"}Payload - Payload又被称为Claims包含您想要签署的任何信息

Claims:{ "sub": "1234567890", "name": "John Doe", "admin": true}

Payload - Payload又被称为Claims包含您想要签署的任何信息JWT Payload的组成Payload常日由三个部分组成,分别是 Registered Claims ; Public Claims ; Private Claims ;每个声明,都有各自的字段。
Registered Claimsiss 【issuer】发布者的url地址sub 【subject】该JWT所面向的用户,用于处理特定运用,不是常用的字段aud 【audience】接管者的url地址exp 【expiration】 该jwt销毁的韶光;unix韶光戳nbf 【not before】 该jwt的利用韶光不能早于该韶光;unix韶光戳iat 【issued at】 该jwt的发布韶光;unix 韶光戳jti 【JWT ID】 该jwt的唯一ID编号
Signature 对 则为对Header、Payload的署名

Signature:base64UrlEncode(Header) + "." + base64UrlEncode(Claims)

头部、声明、署名用 . 号连在一起就得到了我们要的JWT 也便是夏明这种类型的字符串

eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MTUyOTgxNDEsImtleSI6InZhdWxlIn0.orewTmil7YmIXKILHwFnw3Bq1Ox4maXEzp0NC5LRaFQ实在这些事一行的,我只是让看的更直白点将其割开了。
JAVA 实现

JAVA中利用JWT

Maven中引入办法

<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>

须要特殊把稳:JWT依赖于Jackson,须要在程序中加入Jackson的jar包且版今年夜于2.x

签发JWT

public static String createJWT() { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; SecretKey secretKey = generalKey(); JwtBuilder builder = Jwts.builder() .setId(id) // JWT_ID .setAudience("") // 接管者 .setClaims(null) // 自定义属性 .setSubject("") // 主题 .setIssuer("") // 签发者 .setIssuedAt(new Date()) // 签发韶光 .setNotBefore(new Date()) // 失落效韶光 .setExpiration(long) // 过期韶光 .signWith(signatureAlgorithm, secretKey); // 署名算法以及密匙 return builder.compact();}

验证JWT

public static Claims parseJWT(String jwt) throws Exception { SecretKey secretKey = generalKey(); return Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(jwt) .getBody();}

完全示例

import com.google.gson.Gson;import io.jsonwebtoken.Claims;import io.jsonwebtoken.JwtBuilder;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import org.apache.tomcat.util.codec.binary.Base64;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.util.Date;import java.util.HashMap;import java.util.Map;public class JwtUtil { / 由字符串天生加密key @return / public SecretKey generalKey() { String stringKey = Constant.JWT_SECRET; // 本地的密码解码 byte[] encodedKey = Base64.decodeBase64(stringKey); // 根据给定的字节数组利用AES加密算法布局一个密钥 SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } / 创建jwt @param id @param issuer @param subject @param ttlMillis @return @throws Exception / public String createJWT(String id, String issuer, String subject, long ttlMillis) throws Exception { // 指定署名的时候利用的署名算法,也便是header那部分,jjwt已经将这部分内容封装好了。
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 天生JWT的韶光 long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); // 创建payload的私有声明(根据特定的业务须要添加,如果要拿这个做验证,一样平常是须要和jwt的吸收方提前沟通好验证办法的) Map<String, Object> claims = new HashMap<>(); claims.put("uid", "123456"); claims.put("user_name", "admin"); claims.put("nick_name", "X-rapido"); // 天生署名的时候利用的秘钥secret,牢记这个秘钥不能外露哦。
它便是你做事真个私钥,在任何场景都不应该流露出去。
// 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
SecretKey key = generalKey(); // 下面便是在为payload添加各种标准声明和私有声明了 JwtBuilder builder = Jwts.builder() // 这里实在便是new一个JwtBuilder,设置jwt的body .setClaims(claims) // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,便是覆盖了那些标准的声明的 .setId(id) // 设置jti(JWT ID):是JWT的唯一标识,根据业务须要,这个可以设置为一个不重复的值,紧张用来作为一次性token,从而回避重放攻击。
.setIssuedAt(now) // iat: jwt的签发韶光 .setIssuer(issuer) // issuer:jwt签发人 .setSubject(subject) // sub(Subject):代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
.signWith(signatureAlgorithm, key); // 设置署名利用的署名算法和署名利用的秘钥 // 设置过期韶光 if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp); } return builder.compact(); } / 解密jwt @param jwt @return @throws Exception / public Claims parseJWT(String jwt) throws Exception { SecretKey key = generalKey(); //署名秘钥,和天生的署名的秘钥千篇一律 Claims claims = Jwts.parser() //得到DefaultJwtParser .setSigningKey(key) //设置署名的秘钥 .parseClaimsJws(jwt).getBody(); //设置须要解析的jwt return claims; } public static void main(String[] args) { User user = new User("tingfeng", "bulingbuling", "1056856191"); String subject = new Gson().toJson(user); try { JwtUtil util = new JwtUtil(); String jwt = util.createJWT(Constant.JWT_ID, "Anson", subject, Constant.JWT_TTL); System.out.println("JWT:" + jwt); System.out.println("\n解密\n"); Claims c = util.parseJWT(jwt); System.out.println(c.getId()); System.out.println(c.getIssuedAt()); System.out.println(c.getSubject()); System.out.println(c.getIssuer()); System.out.println(c.get("uid", String.class)); } catch (Exception e) { e.printStackTrace(); } }}

Constant.java

import java.util.UUID;public class Constant { public static final String JWT_ID = UUID.randomUUID().toString(); / 加密密文 / public static final String JWT_SECRET = "woyebuzhidaoxiediansha"; public static final int JWT_TTL = 60601000; //millisecond}

输出示例

JWT:eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiIxMjM0NTYiLCJzdWIiOiJ7XCJuaWNrbmFtZVwiOlwidGluZ2ZlbmdcIixcIndlY2hhdFwiOlwiYnVsaW5nYnVsaW5nXCIsXCJxcVwiOlwiMTA1Njg1NjE5MVwifSIsInVzZXJfbmFtZSI6ImFkbWluIiwibmlja19uYW1lIjoiWC1yYXBpZG8iLCJpc3MiOiJBbnNvbiIsImV4cCI6MTUyMjMxNDEyNCwiaWF0IjoxNTIyMzEwNTI0LCJqdGkiOiJhNGQ5MjA0Zi1kYjM3LTRhZGYtODE0NS1iZGNmMDAzMzFmZjYifQ.B5wdY3_W4MZLj9uBHSYalG6vmYwdpdTXg0otdwTmU4U解密a4d9204f-db37-4adf-8145-bdcf00331ff6Thu Mar 29 16:02:04 CST 2018{"nickname":"tingfeng","wechat":"bulingbuling","qq":"1056856191"}Anson123456总结

优点

由于json的通用性,以是JWT是可以进行跨措辞支持的,像JAVA,JavaScript,NodeJS,PHP等很多措辞都可以利用。
由于有了payload部分,以是JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
便于传输,jwt的构成非常大略,字节占用很小,以是它是非常便于传输的。
它不须要在做事端保存会话信息, 以是它易于运用的扩展

安全干系

不应该在jwt的payload部分存放敏感信息,由于该部分是客户端可解密的部分。
保护好secret私钥,该私钥非常主要。
如果可以,请利用https协议

以上为全部内容。

标签:

相关文章

构建未来IT网络框架,关键要素与实施步骤

随着信息技术的飞速发展,网络已成为企业和社会运行的重要基础设施。构建一个高效、安全、可靠的IT网络框架,已成为企业信息化建设的关键...

PHP教程 2024-12-27 阅读0 评论0

构建高效IT运维体系,规划与方法

随着信息技术的飞速发展,IT运维在企业的信息化建设中扮演着越来越重要的角色。高效、稳定的IT运维体系是企业持续发展的基石。本文将从...

PHP教程 2024-12-27 阅读0 评论0

初探C语言,开启编程之旅的大门

在信息时代的浪潮中,编程已成为一种必备技能。而C语言作为一门历史悠久、应用广泛的编程语言,无疑成为了众多编程爱好者的入门首选。今天...

PHP教程 2024-12-27 阅读0 评论0