文章目录
- 1. 首先、了解什么是会话
- 2. 会话跟踪的主要技术
- 3. Token 令牌学习
- 3.1 流程图
- 3.2 token
- 3.3 JWT(JSON web Tokens)Json web 令牌(规范)
- 3.4 JWT结构
- 3.5 JWT需要的依赖
- 3.6 JWT的获取与验证流程
- 3.7JWT的使用方式
- 3.8 jjwt的使用(创建JWT方式)
- 1. jjwt需要的依赖
- 2.测试类
- 3.RSA算法支持
- 4.使用自己的key
- 3.9 前端如何使用token,并且加入请求头
1. 首先、了解什么是会话
会话是指一个终端用户与交互系统进行通讯的过程
2. 会话跟踪的主要技术
2.1 URL重写技术
就是在URL结尾添加一个附加数据以标识该会话,会把会话ID通过URL的信息传递过去,以便在服务端进行识别不同的用户。
2.2 隐藏表单域
将会话ID添加到HTML表单元素中提交到服务器,此表单不再客户端显示。
2.3 cookie
Cookie是web服务器发送给客户端的一小段信息,客户端请求时可以读取该信息发送到服务器端,进而进行用户的识别。对于客户端的每次请求,服务器都会将KCookie发送到客户端,在客户端可以进行保存,以便下次使用。
2.4 session
在服务端会创建一个session对象,产生一个sessionID来标识这个session对象,然后将这个sessionID放入到Cookie中发送到客户端,下一次访问时,sessionID发送到服务器,在服务端进行识别不同的用户。
session是依赖Cookie的,如果Cookie被禁用,那么session也将失效,session默认的会话时长为30分钟。
2.5 Token
token是令牌,访问系统的凭证。
token是服务端生成的一串字符串,客户端首先会请求一个令牌,当第一次登录后,服务器会生成一个token并将此token返回给客户端后,以后客户端只需带上这个token请求数据即可,无需再次带上用户名和密码。
2.6 三者相同点与区别
相同点:都是用于身份验证或鉴权的,都是服务器产生的
区别:
1.cookie是保存在客户端,session是存储在服务器的
2.session保存在服务器的内存,默认是30分钟,token是保存在服务器的数据库里面,持久。
3. Token 令牌学习
3.1 流程图

① 客户端使用用户名和密码请求登录
② 服务端收到请求,验证用户名和密码
③ 验证成功后,服务端会签发一个token,再把这个token返回给客户端
④ 客户端收到token后可以把它存储起来,比如放到cookie中
⑤ 客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带
⑥ 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据
3.2 token
- token加密方式
对称加密:DES,AES、
双钥加密:RSA,一般用于金融项目里面做签名
只加密不解密:MD5,SHA系列
编码格式:Base64
2.token有两种
access_token:时效15分钟到2小时之间
refresh_token:时效15天
3.3 JWT(JSON web Tokens)Json web 令牌(规范)
这种基于token的认证方式相比传统的session认证方式更节约服务器资源,并且对移动端和分布式更加友好。其优点如下:
① 支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题
② 无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力
③ 更适用CDN:可以通过内容分发网络请求服务端的所有资料
④ 更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
⑤ 无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御
3.4 JWT结构
1.Header
JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存
{
“alg”: “HS256”,
“typ”: “JWT”
}
2.Payload
有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
这些预定义的字段并不要求强制使用。除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如下例:
{
“sub”: “1234567890”,
“name”: “Helen”,
“admin”: true
}
请注意,默认情况下JWT是未加密的,因为只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT中,以防止信息泄露。JWT只是适合在网络中传输一些非敏感的信息
3.Signature
签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名
H M A C S H A 256 ( b a s e 64 U r l E n c o d e ( h e a d e r ) + " . " + b a s e 64 U r l E n c o d e ( p a y l o a d ) , s e c r e t ) HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)
HMACSHA256(base64UrlEncode(header)+“.”+base64UrlEncode(payload),secret)
在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象
3.5 JWT需要的依赖

3.6 JWT的获取与验证流程
package edu.zhku.fire_ant_project;import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;@SpringBootTest
public class FireAntProjectApplicationTest {//创建header//@Testpublic String getHeader() throws UnsupportedEncodingException {String header="{\"arg\":\"HS256\",\"typ\":\"JWT\"}";//把header的Json转成Base64URL编码的字符串String header64=Base64.encodeBase64URLSafeString(header.getBytes("UTF-8"));System.out.println("header64="+header64);return header64;}//创建Payload// @Testpublic String getPayload() throws UnsupportedEncodingException {String payload="{\"sub\":\"username\",\"id\":\"1001\",\"role\":\"admin\"}";String payload64=Base64.encodeBase64URLSafeString(payload.getBytes("UTF-8"));System.out.println("play="+payload64);return payload64;}//创建签名Signature// @Testpublic void getSignature() throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {String secret = "h1d444wt65ywj454et45hdh08797hjh1";System.out.println(secret.length());//创建Mac类Mac mac=Mac.getInstance("HmacSHA256");//创建安全的密钥对象SecretKeySpec spec=new SecretKeySpec(secret.getBytes("UTF-8"),"HmacSHA256");//让Mac对象使用指定的密钥对象mac.init(spec);//准备数据String data=getHeader()+"."+getPayload();//生成签名值byte[] bytes=mac.doFinal(data.getBytes("UTF-8"));//把bytes转为StringString signMsg=Base64.encodeBase64URLSafeString(bytes);System.out.println("sign="+signMsg);//组成完整的jwtString jwt = data+"."+signMsg;System.out.println("jwt="+jwt);}//验证签名值@Testpublic void checkSign() throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {//密钥String secret="h1d444wt65ywj454et45hdh08797hjh1";//jwt数据String jwt="eyJhcmciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +"." +"eyJzdWIiOiJ1c2VybmFtZSIsImlkIjoiMTAwMSIsInJvbGUiOiJhZG1pbiJ9" +"." +"vD3nhQz_gVlqV6FLfxW357Mi313bThc2ajyhVic1XNQ";//验证签名:使用和创建时一样的算法,再计算出一个签名值,和jwt比较String data =jwt.substring(0,jwt.lastIndexOf("."));//获取jwt中的签名String signature=jwt.substring(jwt.lastIndexOf(".")+1);
// System.out.println("data="+data);
// System.out.println("signature="+signature);//重新计算签名值Mac mac =Mac.getInstance("HmacSHA256");SecretKeySpec spec=new SecretKeySpec(secret.getBytes("UTF-8"),"HmacSHA256");mac.init(spec);byte[] bytes=mac.doFinal(data.getBytes("UTF-8"));String sign=Base64.encodeBase64URLSafeString(bytes);System.out.println("signature=sign:"+(signature.equals(sign)));}
}
3.7JWT的使用方式
- 首先用户登录校验后,服务器会返回jwt token
- 客户端获取jwt token,使用cookie或localStorage存储jwt token,但这样不能跨域,采用的方式放在请求header的Authorization中,也可以放在get或者post请求中
- 客户端访问其他api接口,传递token给服务器,服务器认证token后,返回给请求的数据。
Authorization:Bearer
3.8 jjwt的使用(创建JWT方式)
1. jjwt需要的依赖

2.测试类
//jjwt的使用@Testpublic void test01(){//创建一个key使用HS256算法Key key= Keys.secretKeyFor(SignatureAlgorithm.HS256);//使用jjwt创建jwtString jws=Jwts.builder().setSubject("username").signWith(key).compact();System.out.println("jws:\n"+jws);jws="eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmaXJlX2FudF9wcm9qZWN0X2FkbWluIn0.ecJI1-6rOtyuPvK8ZxsxddE0nly1CQXxoNfWi9sIhdc";//验证JWT,key已刷新,则会抛出异常Claims body=Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jws).getBody();String subject=body.getSubject();System.out.println("subject:\n"+subject);}
3.RSA算法支持
//RSA算法支持,私钥创建jwt,公钥验证jwt@Testpublic void test02(){KeyPair keyPair =Keys.keyPairFor(SignatureAlgorithm.RS256);PrivateKey aPrivate=keyPair.getPrivate();String jws=Jwts.builder().setSubject("username").signWith(aPrivate).compact();System.out.println("jws:\n"+jws);jws="eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJmaXJlX2FudF9wcm9qZWN0X2FkbWluIn0.JiCzArgVdQNGkpFLaxa0h11KGJsJrj";//验签,使用公钥try {String subject = Jwts.parserBuilder().setSigningKey(keyPair.getPublic()).build().parseClaimsJws(jws).getBody().getSubject();System.out.println("subject:\n" + subject);}catch (SignatureException se){se.printStackTrace();System.out.println("false");}catch (Exception e){e.printStackTrace();}
4.使用自己的key
//使用自己的key@Testpublic void privateKey(){String secret = UUID.randomUUID().toString().replaceAll("-","");//安全的密钥对象SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));String token = Jwts.builder().setSubject("username").signWith(secretKey,SignatureAlgorithm.HS256).compact();System.out.println(secret.length());System.out.println(token);//验证JWT,key已刷新,则会抛出异常Claims body=Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token).getBody();String subject=body.getSubject();System.out.println("subject:\n"+subject);}
//规定token时间
** public void privateKey(){Date date = new Date(System.currentTimeMillis() + 60000);
// String secret = UUID.randomUUID().toString().replaceAll("-","");
// //安全的密钥对象String secret ="dd9b84d01c0940c6a5723a744d0d66a8";SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
// String token = Jwts.builder().setSubject("fire_ant_project_admin")
// .signWith(secretKey,SignatureAlgorithm.HS256)
// .setExpiration(date)
// .compact();String token ="eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmaXJlX2FudF9wcm9qZWN0X2FkbWluIiwiZXhwIjoxNjQyOTA5NTI0fQ.tHfJm-MsEaaqlakOFoRvWMog-weCeSGlJVlGV3YyVPs";System.out.println(token);//验证JWT,key已刷新,则会抛出异常Claims body=Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token).getBody();String subject=body.getSubject();System.out.println("subject:\n"+subject);}//**
3.9 前端如何使用token,并且加入请求头
1.可以通过cookie存储
document.cookie=token;
2.加入请求头Authoriziation
① headers:{“Authoriziation”:token}
② 存储:
获取:

















