1.登录业务介绍:
单一服务器模式
单一服务器模式用于登录的方式通常是使用session。
1.微服务
在分布式中通常使用单点登入(SSO(single sign on)模式)的方式实现用户登入,三种实现方式
1.1 session广播机制实现
在分布式中各个服务器都是独立运行,互不干涉,如果只是使用单个session的话那么每次打开一个服务器都需要重新输入信息登入,但是可以通过复制session的方式实现输入一次信息就可以登入多个服务器,但是缺点也很明显,服务器少还可以,如果服务器多,复制的session也就多,占用进程和浪费空间。以前使用,现在不推荐。
1.2 使用session+redis实现
实现过程:
当你在项目的任意模块登入时,数据将存储在两个地方
- redis:key: 生成唯一随机值 value:用户数据
- cookie:把redis生成的key放到cookie中
访问其他模块时,发送请求带着cookie进行发送,通过request获取cookie,再把获取的cookie到redis中进行查询即可。
1.3 使用令牌(token)
解释:通过一定的规则生成包含用户信息的字符串
实现过程:
- 当你在项目的任意模块登入后,生成相应的token,并返回
- 通过cookie返回
- 通过地址栏返回
- 每次访问其他模块时都会携带token,根据这个token获取用户信息,有则登入。
规则可以自定义也可以使用已封装好的,eg:JWT,HMAC(AK/SK)
2. JWT
2.1 什么是JWT?
JSON Web Tokens, 这是一个开放的标准,规定了一种Token实现方式,以JSON为格式.
2.2 JWT的组成
2.2.1 JWT头
描述JWT元数据的JSON对象,使用Base64 URL转换为字符串保存。
alg------》alg属性表示签名使用的算法
typ------》typ属性表示令牌的类型
{
"alg": "HS256",
"typ": "JWT"
}
2.2.2 有效载荷
WT的主体内容部分,JSON对象,包含需要传递的数据,使用Base64 URL转换为字符串保存。
默认字段:
sub: 该JWT所面向的用户
iss: 该JWT的签发者
iat(issued at): 在什么时候签发的token
exp(expires): token什么时候过期
nbf(not before):token在此时间之前不能被接收处理
jti:JWT ID为web token提供唯一标识
自定义字段:不建议存放密码
{
"name":"xioaxian"
}
注:Base64是一种编码不是一种加密过程
2.2.3 数字签名
再给上述拼接成的字符串进行加密之前,要在末尾加上数字签名,而签名的作用是防止抵赖以及防止数据被修改。数字签名则是使用密钥(secret)也可以叫私钥,这个密钥每个公司都有自己的生成方法,最后用HS256进行加密。
2.2.4 JWT的优点
通用:因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
紧凑:JWT的构成非常简单,字节占用很小,可以通过 GET、POST 等放在 HTTP 的 header 中,非常便于传输。
扩展:JWT是自我包涵的,包含了必要的所有信息,不需要在服务端保存会话信息, 非常易于应用的扩展。
2.3 怎么使用JWT
2.2.1导入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
2.2.2 创建工具类
public class JwtUtils {
//------------------------------------常量------------------------------------------------
/*
EXPIRE :令牌失效时间 (1000 * 60 * 60 * 24)ms 24h
APP_SECRET :密钥,【自定义】
*/
public static final long EXPIRE = 1000 * 60 * 60 * 24;
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
//------------------------------------常量------------------------------------------------
//-----------------------------------获取token--------------------------------------------
public static String getJwtToken(String id, String name){
String JwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT") //令牌的类型
.setHeaderParam("alg", "HS256") //加密算法
.setSubject("user") //分类,【根据实际修改】
.setIssuedAt(new Date()) //生成token日期
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id) //【自定义字段】
.claim("name", name)
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
//-------------------------------判断token是否存在与有效,传jwtToken-------------------------
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
//-------------------------------判断token是否存在与有效,传request--------------------------
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
//---------------------------------获取id-------------------------------------------------
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}