Token, 令牌,代表执行某些操作的权利,也就是我们进行某些操作的通行证。
我们都知道 HTTP 是无状态(stateless)的协议:HTTP 对于事务处理没有记忆能力,不对请求和响应之间的通信状态进行保存。
使用 HTTP 协议,每当有新的请求发送时,就会有对应的新响应产生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把 HTTP 协议设计成如此简单的。
可是,随着 Web 的发展,早期这种无状态的特性却带来了很多不方便性,比如说用户登录新浪微博,在登录页输入用户名、密码之后进入首页,但是由于 HTTP 是无状态的,HTTP 并不知道上一次的 HTTP 请求是否通过了验证,更无法得知当前用户的具体信息。
最简单的解决方案就是在所有的请求里面都带上用户名和密码,这样虽然可行,但是大大加重了服务器的负担(对于每个 request 都需要到数据库验证),而且用户也要每进入一个页面输入一次密码,毫无用户体验可言。
为此,引入了各种身份认证机制
Cookie 是由 HTTP 服务器设置的,保存在浏览器中的小型文本文件,其内容为一系列的键值对
相对于保存在浏览器中的 Cookie,Session 是存储在服务器端的
Cookie 传递过程:
基于Cookie-session的认证过程
token在服务器时可以不用存储用户信息的,token传递的方式也不限于cookie传递。
当用户第一次访问服务器时,服务端通过算法,加密钥,生成一个token。通过BASE64编码后将token发送给客户端。
客户端将token保存起来,下次请求带着token,服务器收到请求会用相同的算法取验证toekn,如果通过就继续执行。
此时服务器变成无状态了,从而比较容易实现扩展
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名依顺序用点号(".")链接而成:1.header,2.payload,3.signature。
如下为一个 JWT字符串:
eyJhbGciOiJIUzI1NiJ9.eyJleHBpcmVUaW1lIjoxNTg5MDE0MzA2OTU0LCJkZXB0Ijoi5Yas6KW_55Oc5ZywIiwidXNlcm5hbWUiOiLlpKfopb_nk5wifQ.QSv0FcvNheiA3FW6OEah7jJKG4SG0ver3q67F0980rY
eyJhbGciOiJIUzI1NiJ9
使用base64解密后得到:
{"alg":"HS256"}
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用:、
也可以自定义 :
Signature 部分是对前两部分的签名,防止数据篡改。
使用指定加密算法以及仅服务器可知的 密钥(secret)对前两部分进行加密。
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以生成完整的JWT token了
本文使用 JJWT 来实现java环境 JWT 生成,解密操作
目前最新版本 0.9.1
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
private static final String SALT = "0142add7c2664198863943f24bf4b8b9"; private static Key getKeyInstance() { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; String apiKey = DatatypeConverter.printBase64Binary(SALT.getBytes()); byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); return signingKey; }
public static String createJavaWebToken4JwtAuth(Map<String, Object> claims) { logger.info("生成的token为开始"); String toekn = Jwts.builder().setClaims(claims).setExpiration(DateUtil.addSeconds(new Date(), 50)) .signWith(SignatureAlgorithm.HS256, getKeyInstance()).compact(); logger.info("生成的token为:" + toekn); return toekn; }
其中 官方Payload 中的属性都有对应api来配置,:chestnut:中配置了 .setExpiration(DateUtil.addSeconds(new Date(), 50))
public static Map<String, Object> verifyJavaWebToken(String jwt) { try { Map<String, Object> jwtClaims = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwt).getBody(); return jwtClaims; } catch (Exception e) { logger.info(e.getMessage()); return null; } }
public static boolean isTokenEffect(String jwt) { if (StringUtils.isEmpty(jwt)) { return false; } Map<String, Object> claims = verifyJavaWebToken(jwt); if (null == claims) { logger.info("转换jwt失败!"); return false; } return true; }
public static void main(String[] args) { Map<String, Object> paramMap = new HashMap<>(); paramMap.put("username", "大西瓜"); paramMap.put("dept", "冬西瓜地"); String tokens = JwtUtil.createJavaWebToken4JwtAuth(paramMap); System.out.println(tokens); System.out.println(isTokenEffect(tokens)); }
本文JwtUtil 已提交github到我的工具集 ytooo-util
<dependency> <groupId>ml.ytooo</groupId> <artifactId>ytooo-util</artifactId> <version>3.6.5</version> </dependency>
<font color="#00dd00" face="微软雅黑">更多好玩好看的内容,欢迎到我的博客交流,共同进步</font> WaterMin