单点登录(第一版)
概念:单点登录就是多系统在单一位置进行登录
还有一个是三方登录,某系统使用其他系统用户实现本系统登录方式,oauth2
本章采用Spring cloudAlibaba的Gateway网关以及认证授权服务实现。
实现方案
实现方案种类
一、Session机制(了解,不算是主流解决方案)
1、Session跨域 2、Spring Session共享(了解) 3、Nginx Session共享(了解)
二、Token机制以及JSON Web Token JWT机制(重点)
使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程
是这样的:
客户端使用用户名、密码请求登录
服务端收到请求,去验证用户名、密码
验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
客户媏每次向服务端请求资源的时候需要带着服务端签发的 Token
服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户
端返回请求的数据
使用 Token 验证的优势:
无状态、可扩展
在客户端存储的Tokens 是无状态的,并且能够被扩展。基于这种无状态和不存储 Session
信息,负载负载均衡器能够将用户信息从一个服务传到其他服务器上、
安全性
请求中发送token 而不再是发送 cookie 能够防止 CSRF(跨站请求伪造)。即使在客户账使
用cookie 存储 token,cookie 也仅仅是一个存储机制而不是用于认证。不将信息存储在 Session
中,让我们少了对session 操作。
传统token与jwt token的区别
token
token的认证流程
1、用户输入用户名和密码,发送给服务器,服务器验证账号密码成功
2、服务器生成令牌,本质是一个32位的uuid(token: 55833b9d752bc1914b3087c7503ae5f9)
3、将该令牌存到数据库或redis中,key是uuid,value是userId
4、把令牌返给客户端,客户端把令牌存在cookie中
5、下次请求的时候就把令牌放在请求头里带上
6、从redis中验证该令牌是否过期
7、获取value内容userId
8、根据userId查询用户信息,再返回客户端
优点
1、可以隐藏真实数据
2、适用于分布式/微服务
3、安全系数高
缺点
1、存放在redis必须依赖服务器
2、暂用服务器资源效率非常低
JWT
概念
JWT是json web token缩写。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证组成:JWT包含三个部分: Header头部,Payload负载和Signature签名。由三部分生成token,三部分之间用“.”号做分割
‘token’:‘eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjb2QiOiI2NDA1NzEtODQ4NzY4LTUwMDE3NS00MjkxNDYtMzg5MDY4IiwidXNyIjoiMTM2NzYyOTExODgiLCJleHAiOjE2NDk2NjkzNTAsImlhdCI6MTY0ODQ1OTc1MCwianRpIjoiZmtnamR2ODMyajBzMTBvMTUxNW9nOG1vaGMifQ.mTjcpqGTBIsEF3yEZmy9sr_0gpUsPAhWcAFVEeGI-F4’
JWT做登录认证
首先思路是
1、要在GateWay的网关中实现全局过滤器,如下图。
并将各个服务的网关转发路径配置完善,如下图。
在配置路径的白名单用于放行,如下图。
2、在全局过滤器中要获取客户端的url查看url是否包含在白名单当中如果包含则直接放行,如下图。
3、获取token信息,先判断token是否为空,然后在判断token是否正确,在校验token是否存在黑名单当中,这个黑名单在登录注销的时候会讲解,最后验证token是否过期,如下图。
以上就是网关过滤器中的jwt认证流程。
下面说认证服务中的东西
首先在认证服务里要与jwt集成
1、添加依赖:
com.auth0
java-jwt
3.10.3
2、然后编写tokeUtil工具类import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.Claims;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.tjxs.commons.constant.TokenConstant;
import com.tjxs.commons.domain.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;/**
• token工具类
/
@Slf4j
@Component
public class TokenUtil {
/*• 创建令牌
•
• @param userId
• @param passWord
• @return
*/
public static String createToken(String userId, String passWord) {
//过期时间
Date date = new Date(System.currentTimeMillis() + TokenConstant.EXPIRE_TIME);
//将userId设置到token里
String token = JWT.create().withAudience(userId)
//设置过期时间,时间是一小时
.withExpiresAt(date)
//设置token的秘钥passWord
.sign(Algorithm.HMAC256(passWord));
return TokenConstant.PREFIX+token;
}//获取当前登录用户id
public static String getCurrentUser(String token) {
try {
if (StrUtil.isNotEmpty(token)) {
return JWT.decode(token).getAudience().get(0);
}
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
return null;
}//校验token是否过期
public static boolean checkToken(String token){
if (StrUtil.isNotEmpty(token)) {
//获取token设置的过期时间
Date expiresAt = JWT.decode(token).getExpiresAt();
//过期时间减去当前时间看是否大于3分钟如果大于则不使用该token
long time = expiresAt.getTime() - System.currentTimeMillis();
if(time<0){
//表示过期
return true;
}
}
//没有过期
return false;
}//校验token的正确性
public static boolean legitimateToken(String token){
try {
JWT.decode(token);
}catch (Exception e){
log.error(“token令牌解析错误,非法令牌闯入:”+e.getMessage());
//token不合法
return true;
}
//合法
return false;
}}
3、编写token常量类/**
• token 常量类
/
public class TokenConstant {
/*• 令牌自定义标识
*/
public static final String AUTHENTICATION = “Authorization”;/**
• 令牌前缀
*/
public static final String PREFIX = "Bearer ";/**
• 过期时间3分钟
*/
public static final long EXPIRE_TIME = 60 * 60 * 30;}
4、在登录控制器层编写登录方法生成token以及注销退出的方法如下图
JWT退出登录(注销)
在使用jwt做注销的时候的两个个思路
1、注销时从客户端删除存储的Token(前端处理,后端不理会)
2、黑名单校验,通过使用redis对token进行黑名单校验凡是退出登录的token都放入黑名单中,定期清理。每次用户请求服务器都校验token是否在黑名单
目前为止就可以实现登录认证了
总结
客户端发送登录请求,网关全局过滤器对请求进行拦截判断请求是否存在于白名单当中如果存在直接放行,网关将请求转发到认证的服务器上,然后通过登录控制器生成token信息进行返回给客户端,然后客户端在携带登录请求所返回的token去发送其他请求。
至于为要在token过期以及退出登录时增加黑名单是有一种情况,比如我生成token过期时间是一个小时,然而此时只过了10分钟我就退出系统了那么这个token其实jwt无法销毁,也就是说只要能拿到这个token就可以登录系统或者做一些其他操作,所以在注销登录后要将token信息存入redis充当黑名单定期清理,在全局过滤其中对token进行黑名单校验即可解决这个问题。