1.1 jwt和token
1.1.1 token介绍
令牌(Token):在计算机领域,令牌是一种代表某种访问权限或身份认证信息的令牌。它可以是一串随机生成的字符或数字,用于验证用户的身份或授权用户对特定资源的访问。
简单理解 : 每个用户生成的唯一字符串标识 , 可以进行用户识别和校验
优势: token验证标识无法直接识别用户的信息,盗取token后也无法登录
程序 相对安全!
1.1.2 jwt介绍
- Token是一项规范和标准(接口)
- JWT(JSON Web Token)是具体可以生成,校验,解析等动作Token的技术(实现类)
- JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
- JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
- JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑(JWT的登出问题)。
思考 : 正常情况下 修改了密码后就会跳转到登录页面 :修改成功后清空浏览器保存的token了
后端怎么玩? 因为服务端不保留token 我用之前的token 还是可以继续访问的
-
JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
-
为了减少盗用,JWT 不应该使用 HTTP 80 协议明码传输,要使用 HTTPS 443 协议传输。
1.1.3 jwt工作流程
- 用户提供其凭据(通常是用户名和密码)进行身份验证。
- 服务器对这些凭据进行验证,并在验证成功后创建一个JWT。
- 服务器将JWT发送给客户端,并客户端在后续的请求中将JWT附加在请求头或参数中。
- 服务器接收到请求后,验证JWT的签名和有效性,并根据JWT中的声明进行身份验证和授权操作
-
1.1.4 jwt数据组成和包含信息
JWT由三部分组成: header(头部).payload(载荷).signature(签名)
我们需要理解的是, jwt可以携带很多信息! 一般情况,需要加入:有效时间,签名秘钥,其他用户标识信息!
有效时间的作用 : 保证token的时效性,过期可以重新登录获取!
签名秘钥的作用 : 防止其他人随意解析和校验token数据!
用户信息的作用 : 系统解析的时候,分辨Token对应的具体用户!
1.1.5 使用JWT的核心
客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。
此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。
Authorization: Bearer jwt
另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。
1.1.5 jwt使用和测试
- 导入依赖
<!--jjwt只是实现JSON的一个包,还有其他的例如Java-jwt-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.0</version>
</dependency>
- 编写配置application.yaml
#jwt配置
jwt:token:tokenExpiration: 120 #有效时间,单位分钟tokenSignKey: headline123456 #当前程序签名秘钥 自定义
- 封装jwt技术工具类
分析 : 从逻辑上讲,该工具类必须有如下功能
a. 根据传入的用户id生成token并返回 ,以便用户免密登录
b. 根据传入的token判断用户id , 以便知道登录的是哪个用户
c. 判断传入的token是否还在有效期
package com.sunsplanter.utils;@Data
@Component
//读取配置文件中所有前缀为jwt.token的属性,只要最后后缀一样就自动注入
@ConfigurationProperties(prefix = "jwt.token")
public class JwtHelper {private long tokenExpiration; //有效时间,单位毫秒 1000毫秒 == 1秒private String tokenSignKey; //当前程序签名秘钥//生成token字符串public String createToken(Long userId) {System.out.println("tokenExpiration = " + tokenExpiration);System.out.println("tokenSignKey = " + tokenSignKey);String token = Jwts.builder().setSubject("YYGH-USER").setExpiration(new Date(System.currentTimeMillis() + tokenExpiration*1000*60)) //单位分钟.claim("userId", userId).signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();return token;}//从token字符串获取useridpublic Long getUserId(String token) {if(StringUtils.isEmpty(token)) return null;Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);Claims claims = claimsJws.getBody();Integer userId = (Integer)claims.get("userId");return userId.longValue();}//判断token是否有效public boolean isExpiration(String token){try {boolean isExpire = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getExpiration().before(new Date());//没有过期,有效,返回falsereturn isExpire;}catch(Exception e) {//过期出现异常,返回truereturn true;}}
}
4. 使用和测试
package com.sunsplanter.test;@SpringBootTest
public class SBTest {@Autowiredprivate JwtHelper jwtHelper;@Testpublic void test(){//生成 传入用户标识String token = jwtHelper.createToken(1L);System.out.println("token = " + token);//解析用户标识int userId = jwtHelper.getUserId(token).intValue();System.out.println("userId = " + userId);//校验是否到期! false 未到期 true到期boolean expiration = jwtHelper.isExpiration(token);System.out.println("expiration = " + expiration);}}
2. base64
2.1 什么是Base64
- 所谓Base64,就是说选出64个字符:小写字母a-z、大写字母A-Z、数字0-9、符号"+“、”/“(再加上可能的”=",实际上是使用65个字符),作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。
- base64是一个编码方案. 并非加密方案
- base64的编码方案是:每遇到三个字节就按照特定规则编码为4个字节, 因此最后文件体积一定是变大了
- 既然每三个字节才编码为一次为4个字节, 那么最后可能会剩下1/2个字节没法编码, 此时用1/2个"=表示"
2.2 linux base64命令
2.2.1 Linux下用base64命令编解码字符串
#编码:
echo -n 'Hello World' | base64
SGVsbG8gV29ybGQ=
#解码:
echo -n 'SGVsbG8gV29ybGQ=' | base64 -d
Hello World
备注:
echo 命令是带换行符的
echo -n 不换行输出
echo -n ‘{“alg”:“HS256”,“typ”:“JWT”}’ | base64